Add per-process pthread init probes installation 61/131761/6
authorAlexander Aksenov <a.aksenov@samsung.com>
Thu, 25 May 2017 15:17:24 +0000 (18:17 +0300)
committerAlexander Aksenov <a.aksenov@samsung.com>
Fri, 2 Jun 2017 12:50:36 +0000 (15:50 +0300)
Library can be loaded very early, when pthread was not inited.
Due to pthread library usage in our service probe lib, this causes
assert in library constructor. To prevent this pthread init probe
is installed, and so we can identify when it is inited.
This mechanism was previously made for got patcher, now this is
preload implementation.

This commit is related to swap-manager's commit
Pass pthread's init data to preload module

Change-Id: I83954951e6797eaf583c44f3a348d8d37a277cbf
Signed-off-by: Alexander Aksenov <a.aksenov@samsung.com>
loader/loader_pd.c
preload/Kbuild
preload/preload_debugfs.c
preload/preload_module.c
preload/preload_module.h
preload/preload_process.c [new file with mode: 0644]
preload/preload_process.h [new file with mode: 0644]

index 4333eed..a7597e3 100644 (file)
@@ -20,8 +20,7 @@ struct pd_t {
        unsigned long loader_base;
        unsigned long data_page;
        struct list_head handlers;
-       bool is_pthread_init;           /* TODO Move to GOT patcher, because
-                                        * only it uses this field */
+       bool is_pthread_init;
 };
 
 struct hd_t {
index 425a43d..466b89b 100644 (file)
@@ -5,4 +5,5 @@ swap_preload-y := preload_module.o \
                   preload_control.o \
                   preload_debugfs.o \
                   preload_probe.o \
-                  preload_threads.o
+                  preload_threads.o \
+                  preload_process.o
index ef2c968..9b0b4bb 100644 (file)
@@ -8,10 +8,12 @@
 #include <linux/limits.h>
 #include <asm/uaccess.h>
 #include <master/swap_debugfs.h>
+#include <master/swap_initializer.h>
 #include "preload.h"
 #include "preload_module.h"
 #include "preload_debugfs.h"
 #include "preload_control.h"
+#include "preload_process.h"
 
 static const char PRELOAD_FOLDER[] = "preload";
 static const char PRELOAD_TARGET[] = "target_binaries";
@@ -19,6 +21,16 @@ static const char PRELOAD_IGNORED[] = "ignored_binaries";
 static const char PRELOAD_BINARIES_LIST[] = "bins_list";
 static const char PRELOAD_BINARIES_ADD[] = "bins_add";
 static const char PRELOAD_BINARIES_REMOVE[] = "bins_remove";
+static const char PRELOAD_ENABLE[] = "enable";
+static const char PRELOAD_BY_PATH[] = "by_path";
+static const char PRELOAD_BY_PID[] = "by_pid";
+static const char PRELOAD_BY_ID[] = "by_id";
+static const char PRELOAD_ADD[] = "add";
+static const char PRELOAD_DEL[] = "del";
+static const char PRELOAD_DEL_ALL[] = "del_all";
+static const char PRELOAD_PTHREAD[] = "pthread";
+static const char PRELOAD_PATH[] = "path";
+static const char PRELOAD_MINIMAL_INIT[] = "minimal_init_off";
 
 static struct dentry *preload_root;
 static struct dentry *target_list = NULL;
@@ -29,6 +41,280 @@ static struct dentry *ignored_add = NULL;
 static struct dentry *ignored_remove = NULL;
 
 
+/* Type for functions that add process by path and by id */
+typedef int (*sh_t)(const char *);
+
+/* Type for functions that add process by pid */
+typedef int (*ph_t)(pid_t);
+
+/* Type for function that handles unsigned long grabbed from userspace */
+typedef int (*ulh_t)(unsigned long);
+
+
+/* remove end-line symbols */
+static void rm_endline_symbols(char *buf, size_t len)
+{
+       char *p, *buf_end;
+
+       buf_end = buf + len;
+       for (p = buf; p != buf_end; ++p)
+               if (*p == '\n' || *p == '\r')
+                       *p = '\0';
+}
+
+static ssize_t get_string(const char __user *buf, size_t len, char **kbuf)
+{
+       char *string;
+       ssize_t ret;
+
+       string = kmalloc(len + 1, GFP_KERNEL);
+       if (!string) {
+               pr_warn(PRELOAD_PREFIX "No mem for user string!\n");
+               return -ENOMEM;
+       }
+
+       if (copy_from_user(string, buf, len)) {
+               pr_warn(PRELOAD_PREFIX "Failed to copy data from user!\n");
+               ret = -EINVAL;
+               goto get_string_fail;
+       }
+
+       string[len] = '\0';
+       rm_endline_symbols(string, len);
+       *kbuf = string;
+
+       return len;
+
+get_string_fail:
+       kfree(string);
+
+       return ret;
+}
+
+
+static ssize_t get_ul_and_call(const char __user *buf, size_t len, ulh_t cb)
+{
+       ssize_t ret;
+       char *ulstring;
+       unsigned long ul;
+
+       ret = get_string(buf, len, &ulstring);
+       if (ret != len)
+               return ret;
+
+       ret = kstrtoul(ulstring, 16, &ul);
+       if (ret)
+               goto get_ul_write_out;
+
+       ret = cb(ul);
+
+get_ul_write_out:
+       kfree(ulstring);
+
+       return ret == 0 ? len : ret;
+}
+
+static ssize_t get_string_and_call(const char __user *buf, size_t len, sh_t cb)
+{
+       char *string;
+       ssize_t ret;
+
+       ret = get_string(buf, len, &string);
+       if (ret != len)
+               return ret;
+
+       ret = cb(string);
+       if (ret)
+               pr_warn(PRELOAD_PREFIX "Error adding process by <%s>\n",
+                       string);
+
+       kfree(string);
+
+       return ret == 0 ? len : ret;
+}
+
+static ssize_t get_pid_and_call(const char __user *buf, size_t len, ph_t cb)
+{
+       char *string;
+       pid_t pid;
+       ssize_t ret;
+
+       ret = get_string(buf, len, &string);
+       if (ret != len)
+               return ret;
+
+       ret = kstrtoul(string, 10, (unsigned long *)&pid);
+       if (ret) {
+               pr_warn(PRELOAD_PREFIX "Invalid PID!\n");
+               goto get_pid_out;
+       }
+
+       ret = cb(pid);
+       if (ret)
+               pr_warn(PRELOAD_PREFIX "Error adding process by <%s>\n",
+                       string);
+
+get_pid_out:
+       kfree(string);
+
+       return ret == 0 ? len : ret;
+}
+
+/* ===========================================================================
+ * =                           TARGET PROCESSES                              =
+ * ===========================================================================
+ */
+
+static ssize_t by_path_add_write(struct file *file, const char __user *buf,
+                                size_t len, loff_t *ppos)
+{
+       return get_string_and_call(buf, len, pp_add_by_path);
+}
+
+static ssize_t by_path_del_write(struct file *file, const char __user *buf,
+                                size_t len, loff_t *ppos)
+{
+       return get_string_and_call(buf, len, pp_del_by_path);
+}
+
+static ssize_t by_pid_add_write(struct file *file, const char __user *buf,
+                               size_t len, loff_t *ppos)
+{
+       return get_pid_and_call(buf, len, pp_add_by_pid);
+}
+
+static ssize_t by_pid_del_write(struct file *file, const char __user *buf,
+                               size_t len, loff_t *ppos)
+{
+       return get_pid_and_call(buf, len, pp_del_by_pid);
+}
+
+static ssize_t by_id_add_write(struct file *file, const char __user *buf,
+                              size_t len, loff_t *ppos)
+{
+       return get_string_and_call(buf, len, pp_add_by_id);
+}
+
+static ssize_t by_id_del_write(struct file *file, const char __user *buf,
+                              size_t len, loff_t *ppos)
+{
+       return get_string_and_call(buf, len, pp_del_by_id);
+}
+
+static ssize_t del_all_write(struct file *file, const char __user *buf,
+                               size_t len, loff_t *ppos)
+{
+       pp_del_all();
+
+       WARN(pc_clean_instrumented_bins(), PRELOAD_PREFIX
+            "Error while cleaning target bins\n");
+       WARN(pc_clean_ignored_bins(), PRELOAD_PREFIX
+            "Error while cleaning ignored bins\n");
+
+       return len;
+}
+
+static const struct file_operations by_path_add_fops = {
+       .owner = THIS_MODULE,
+       .open = swap_init_simple_open,
+       .release = swap_init_simple_release,
+       .write = by_path_add_write,
+};
+
+static const struct file_operations by_path_del_fops = {
+       .owner = THIS_MODULE,
+       .open = swap_init_simple_open,
+       .release = swap_init_simple_release,
+       .write = by_path_del_write,
+};
+
+static const struct file_operations by_pid_add_fops = {
+       .owner = THIS_MODULE,
+       .open = swap_init_simple_open,
+       .release = swap_init_simple_release,
+       .write = by_pid_add_write,
+};
+
+static const struct file_operations by_pid_del_fops = {
+       .owner = THIS_MODULE,
+       .open = swap_init_simple_open,
+       .release = swap_init_simple_release,
+       .write = by_pid_del_write,
+};
+
+static const struct file_operations by_id_add_fops = {
+       .owner = THIS_MODULE,
+       .open = swap_init_simple_open,
+       .release = swap_init_simple_release,
+       .write = by_id_add_write,
+};
+
+static const struct file_operations by_id_del_fops = {
+       .owner = THIS_MODULE,
+       .open = swap_init_simple_open,
+       .release = swap_init_simple_release,
+       .write = by_id_del_write,
+};
+
+static const struct file_operations del_all_fops = {
+       .owner = THIS_MODULE,
+       .open = swap_init_simple_open,
+       .release = swap_init_simple_release,
+       .write = del_all_write,
+};
+
+/* ===========================================================================
+ * =                              ENABLE                                     =
+ * ===========================================================================
+ */
+
+static ssize_t enable_read(struct file *file, char __user *buf,
+                          size_t len, loff_t *ppos)
+{
+       char val[2];
+
+       val[0] = (pm_status() == PRELOAD_ON ? '1' : '0');
+       val[1] = '\0';
+
+       return simple_read_from_buffer(buf, len, ppos, val, 2);
+}
+
+static ssize_t enable_write(struct file *file, const char __user *buf,
+                           size_t len, loff_t *ppos)
+{
+       ssize_t ret = 0;
+       char val[2];
+       size_t val_size;
+
+       val_size = min(len, (sizeof(val) - 1));
+       if (copy_from_user(val, buf, val_size))
+               return -EFAULT;
+
+       val[1] = '\0';
+       switch (val[0]) {
+       case '0':
+               ret = pm_switch(PRELOAD_OFF);
+               break;
+       case '1':
+               ret = pm_switch(PRELOAD_ON);
+               break;
+       default:
+               printk(PRELOAD_PREFIX "Invalid state!\n");
+               return -EINVAL;
+       }
+
+       return ret == 0 ? len : ret;
+}
+
+static const struct file_operations enable_fops = {
+       .owner = THIS_MODULE,
+       .open = swap_init_simple_open,
+       .release = swap_init_simple_release,
+       .write = enable_write,
+       .read = enable_read,
+};
+
+
 /* ===========================================================================
  * =                                BIN PATH                                 =
  * ===========================================================================
@@ -193,12 +479,46 @@ static const struct file_operations bin_remove_file_ops = {
 
 
 
+/* ===========================================================================
+ * =                             PTHREAD                                     =
+ * ===========================================================================
+ */
+
+static ssize_t pthread_path_write(struct file *file, const char __user *buf,
+                                 size_t len, loff_t *ppos)
+{
+       return get_string_and_call(buf, len, pp_set_pthread_path);
+}
+
+static ssize_t init_off_write(struct file *file, const char __user *buf,
+                             size_t len, loff_t *ppos)
+{
+       return get_ul_and_call(buf, len, pp_set_init_offset);
+}
+
+static const struct file_operations pthread_path_fops = {
+       .owner = THIS_MODULE,
+       .open = swap_init_simple_open,
+       .release = swap_init_simple_release,
+       .write = pthread_path_write,
+};
+
+static const struct file_operations pthread_init_off_fops = {
+       .owner = THIS_MODULE,
+       .open = swap_init_simple_open,
+       .release = swap_init_simple_release,
+       .write = init_off_write,
+};
+
+
+
 
 
 
 int pd_init(void)
 {
-       struct dentry *swap_dentry, *root, *target_path, *ignored_path;
+       struct dentry *swap_dentry, *root, *target_path, *ignored_path,
+                     *by_path, *by_pid, *by_id, *pthread, *dentry;
        int ret;
 
        ret = -ENODEV;
@@ -224,16 +544,16 @@ int pd_init(void)
        }
 
        target_list = debugfs_create_file(PRELOAD_BINARIES_LIST,
-                                         PRELOAD_DEFAULT_PERMS, target_path, NULL,
-                                         &bin_list_file_ops);
+                                         PRELOAD_DEFAULT_PERMS, target_path,
+                                         NULL, &bin_list_file_ops);
        if (IS_ERR_OR_NULL(target_list)) {
                ret = -ENOMEM;
                goto remove;
        }
 
        target_add = debugfs_create_file(PRELOAD_BINARIES_ADD,
-                                        PRELOAD_DEFAULT_PERMS, target_path, NULL,
-                                        &bin_add_file_ops);
+                                        PRELOAD_DEFAULT_PERMS, target_path,
+                                        NULL, &bin_add_file_ops);
        if (IS_ERR_OR_NULL(target_add)) {
                ret = -ENOMEM;
                goto remove;
@@ -262,21 +582,117 @@ int pd_init(void)
        }
 
        ignored_add = debugfs_create_file(PRELOAD_BINARIES_ADD,
-                                         PRELOAD_DEFAULT_PERMS, ignored_path, NULL,
-                                         &bin_add_file_ops);
+                                         PRELOAD_DEFAULT_PERMS, ignored_path,
+                                         NULL, &bin_add_file_ops);
        if (IS_ERR_OR_NULL(ignored_add)) {
                ret = -ENOMEM;
                goto remove;
        }
 
        ignored_remove = debugfs_create_file(PRELOAD_BINARIES_REMOVE,
-                                            PRELOAD_DEFAULT_PERMS, ignored_path, NULL,
+                                            PRELOAD_DEFAULT_PERMS,
+                                            ignored_path, NULL,
                                             &bin_remove_file_ops);
        if (IS_ERR_OR_NULL(ignored_remove)) {
                ret = -ENOMEM;
                goto remove;
        }
 
+       by_path = debugfs_create_dir(PRELOAD_BY_PATH, root);
+       if (IS_ERR_OR_NULL(by_path)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       dentry = debugfs_create_file(PRELOAD_ADD, PRELOAD_DEFAULT_PERMS,
+                                    by_path, NULL, &by_path_add_fops);
+       if (IS_ERR_OR_NULL(dentry)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       dentry = debugfs_create_file(PRELOAD_DEL, PRELOAD_DEFAULT_PERMS,
+                                    by_path, NULL, &by_path_del_fops);
+       if (IS_ERR_OR_NULL(dentry)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       by_pid = debugfs_create_dir(PRELOAD_BY_PID, root);
+       if (IS_ERR_OR_NULL(by_pid)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       dentry = debugfs_create_file(PRELOAD_ADD, PRELOAD_DEFAULT_PERMS, by_pid,
+                                    NULL, &by_pid_add_fops);
+       if (IS_ERR_OR_NULL(dentry)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       dentry = debugfs_create_file(PRELOAD_DEL, PRELOAD_DEFAULT_PERMS, by_pid,
+                                    NULL, &by_pid_del_fops);
+       if (IS_ERR_OR_NULL(dentry)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       by_id = debugfs_create_dir(PRELOAD_BY_ID, root);
+       if (IS_ERR_OR_NULL(by_id)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       dentry = debugfs_create_file(PRELOAD_ADD, PRELOAD_DEFAULT_PERMS, by_id,
+                                    NULL, &by_id_add_fops);
+       if (IS_ERR_OR_NULL(dentry)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       dentry = debugfs_create_file(PRELOAD_DEL, PRELOAD_DEFAULT_PERMS, by_id,
+                                    NULL, &by_id_del_fops);
+       if (IS_ERR_OR_NULL(dentry)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       dentry = debugfs_create_file(PRELOAD_DEL_ALL, PRELOAD_DEFAULT_PERMS,
+                                    root, NULL, &del_all_fops);
+       if (IS_ERR_OR_NULL(dentry)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       dentry = debugfs_create_file(PRELOAD_ENABLE, PRELOAD_DEFAULT_PERMS,
+                                    root, NULL, &enable_fops);
+       if (IS_ERR_OR_NULL(dentry)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       pthread = debugfs_create_dir(PRELOAD_PTHREAD, root);
+       if (IS_ERR_OR_NULL(pthread)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       dentry = debugfs_create_file(PRELOAD_PATH, PRELOAD_DEFAULT_PERMS,
+                                    pthread, NULL, &pthread_path_fops);
+       if (IS_ERR_OR_NULL(dentry)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
+       dentry = debugfs_create_file(PRELOAD_MINIMAL_INIT,
+                                    PRELOAD_DEFAULT_PERMS, pthread, NULL,
+                                    &pthread_init_off_fops);
+       if (IS_ERR_OR_NULL(dentry)) {
+               ret = -ENOMEM;
+               goto remove;
+       }
+
        return 0;
 
 remove:
index 331a470..a16094c 100644 (file)
@@ -1,6 +1,7 @@
 #include <linux/bug.h>
 #include <linux/slab.h>
 #include <linux/namei.h>
+#include <linux/mutex.h>
 #include <kprobe/swap_kprobes_deps.h>
 #include <writer/kernel_operations.h>
 #include <writer/swap_msg.h>
@@ -15,6 +16,7 @@
 #include "preload_debugfs.h"
 #include "preload_control.h"
 #include "preload_threads.h"
+#include "preload_process.h"
 
 enum {
        /* task preload flags */
@@ -27,6 +29,14 @@ struct user_ptrs {
        char *call_type;
 };
 
+static enum preload_status _status = PRELOAD_OFF;
+static DEFINE_MUTEX(status_change);
+
+
+static inline bool _is_enable_no_lock(void)
+{
+       return _status;
+}
 
 static inline struct vm_area_struct *__get_vma_by_addr(struct task_struct *task,
                                                       unsigned long caddr)
@@ -182,8 +192,8 @@ static int preload_us_entry(struct uretprobe_instance *ri, struct pt_regs *regs)
        if (hd == NULL)
                goto out_set_orig;
 
-       if (lpd_get_state(hd) == NOT_LOADED ||
-           lpd_get_state(hd) == FAILED)
+       if ((lpd_get_state(hd) == NOT_LOADED ||
+           lpd_get_state(hd) == FAILED) && lpd_get_init_state(pd))
                vaddr = loader_not_loaded_entry(ri, regs, pd, hd);
        else if (lpd_get_state(hd) == LOADED)
                vaddr =__do_preload_entry(ri, regs, hd);
@@ -501,6 +511,48 @@ void pm_uprobe_exit(struct sspt_ip *ip)
                swap_put_dentry(dentry);
 }
 
+int pm_switch(enum preload_status stat)
+{
+       int ret;
+
+       mutex_lock(&status_change);
+       switch (stat) {
+       case PRELOAD_ON:
+               if (_is_enable_no_lock())
+                       goto pm_switch_unlock;
+
+               ret = pp_enable();
+               if (!ret)
+                       _status = PRELOAD_ON;
+               break;
+       case PRELOAD_OFF:
+               if (!_is_enable_no_lock())
+                       goto pm_switch_unlock;
+
+               pp_disable();
+               _status = PRELOAD_OFF;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+pm_switch_unlock:
+       mutex_unlock(&status_change);
+
+       return ret;
+}
+
+enum preload_status pm_status(void)
+{
+       enum preload_status s;
+
+       mutex_lock(&status_change);
+       s = _status;
+       mutex_unlock(&status_change);
+
+       return s;
+}
+
 static int pm_init(void)
 {
        int ret;
index 6a3369a..fd19bee 100644 (file)
@@ -4,6 +4,15 @@
 struct sspt_ip;
 struct dentry;
 
+
+enum preload_status {
+       PRELOAD_ON,
+       PRELOAD_OFF
+};
+
+enum preload_status pm_status(void);
+int pm_switch(enum preload_status stat);
+
 int pm_uprobe_init(struct sspt_ip *ip);
 void pm_uprobe_exit(struct sspt_ip *ip);
 
diff --git a/preload/preload_process.c b/preload/preload_process.c
new file mode 100644 (file)
index 0000000..54f0827
--- /dev/null
@@ -0,0 +1,462 @@
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/namei.h>
+#include <linux/slab.h>
+#include <us_manager/us_common_file.h>
+#include <us_manager/pf/pf_group.h>
+#include <us_manager/sspt/sspt_page.h>
+#include <us_manager/sspt/sspt_file.h>
+#include <us_manager/sspt/sspt_ip.h>
+#include <us_manager/probes/probe_info_new.h>
+#include <loader/loader.h>
+#include "preload.h"
+#include "preload_process.h"
+
+
+enum task_id_t {
+       SET_BY_PATH,
+       SET_BY_PID,
+       SET_BY_ID
+};
+
+struct process_t {
+       struct list_head list;
+       struct pf_group *pfg;
+       enum task_id_t idt;
+       union {
+               struct dentry *dentry;
+               pid_t pid;
+               char *id;
+       };
+       struct probe_new p_init;
+};
+
+struct bin_data_t {
+       struct dentry *dentry;
+       unsigned long off;
+};
+
+/* For process list elements checker */
+typedef struct process_t *(*checker_t)(struct process_t *, void *);
+
+static LIST_HEAD(_reg_proc_list);
+static LIST_HEAD(_unreg_proc_list);
+static DEFINE_MUTEX(_proc_list_lock);
+
+static struct bin_data_t _pthread_init;
+
+static inline void _lock_proc_list(void)
+{
+       mutex_lock(&_proc_list_lock);
+}
+
+static inline void _unlock_proc_list(void)
+{
+       mutex_unlock(&_proc_list_lock);
+}
+
+static inline struct process_t *_check_by_dentry(struct process_t *proc,
+                                                void *data)
+{
+       struct dentry *dentry = data;
+
+       if (proc->idt == SET_BY_PATH && proc->dentry == dentry)
+               return proc;
+
+       return NULL;
+}
+
+static inline struct process_t *_check_by_pid(struct process_t *proc,
+                                             void *data)
+{
+       pid_t pid = *(pid_t *)data;
+
+       if (proc->idt == SET_BY_PID && proc->pid == pid)
+               return proc;
+
+       return NULL;
+}
+
+static inline struct process_t *_check_by_id(struct process_t *proc, void *data)
+{
+       char *id = data;
+
+       if (proc->idt == SET_BY_ID && proc->id == id)
+               return proc;
+
+       return NULL;
+}
+
+static inline bool _is_pthread_data_available(void)
+{
+       return (_pthread_init.dentry && _pthread_init.off);
+}
+
+
+
+
+static int pthread_init_eh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+       struct pd_t *pd = lpd_get_by_task(current);
+
+       lpd_set_init_state(pd, false);
+
+       return 0;
+}
+
+static int pthread_init_rh(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+       struct pd_t *pd = lpd_get_by_task(current);
+
+       lpd_set_init_state(pd, true);
+
+       return 0;
+}
+
+static struct probe_desc pin_pinit = MAKE_URPROBE(pthread_init_eh,
+                                                 pthread_init_rh, 0);
+
+static int _proc_reg_no_lock(struct process_t *proc)
+{
+       int ret;
+
+       proc->p_init.desc = &pin_pinit;
+       proc->p_init.offset = _pthread_init.off;
+       ret = pin_register(&proc->p_init, proc->pfg, _pthread_init.dentry);
+       if (ret)
+               pr_warn(PRELOAD_PREFIX "Can't register pthread init probe\n");
+
+       return ret;
+}
+
+static void _proc_unreg_no_lock(struct process_t *proc)
+{
+       pin_unregister(&proc->p_init, proc->pfg);
+}
+
+static struct process_t *_proc_create(struct pf_group *pfg)
+{
+       struct process_t *proc;
+
+       proc = kzalloc(sizeof(*proc), GFP_KERNEL);
+       if (!proc) {
+               pr_warn(PRELOAD_PREFIX "No mem to alloc proccess_t struct!\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       INIT_LIST_HEAD(&proc->list);
+       proc->pfg = pfg;
+
+       return proc;
+}
+
+static void _proc_destroy(struct process_t *proc)
+{
+       if (proc->pfg)
+               put_pf_group(proc->pfg);
+
+       if (proc->idt == SET_BY_PATH)
+               swap_put_dentry(proc->dentry);
+
+       if (proc->idt == SET_BY_ID)
+               kfree(proc->id);
+
+       kfree(proc);
+}
+
+static int _add_to_list(struct process_t *proc)
+{
+       _lock_proc_list();
+       list_add_tail(&proc->list, &_unreg_proc_list);
+       _unlock_proc_list();
+
+       return 0;
+}
+
+/* checker - used to check process list candidates, chosen by type of
+ * registration (path, pid, id)
+ * data - pointer to target process data, checker compares candidate with
+ */
+
+static struct process_t *_get_from_list_no_lock(checker_t checker, void *data,
+                                               struct list_head *list)
+{
+       struct process_t *proc;
+
+       list_for_each_entry(proc, list, list) {
+               if (checker(proc, data))
+                       return proc;
+       }
+
+       return NULL;
+}
+
+static void _delete_from_list(checker_t checker, void *data)
+{
+       struct process_t *proc;
+
+       _lock_proc_list();
+       proc = _get_from_list_no_lock(checker, data, &_unreg_proc_list);
+       if (proc) {
+               list_del(&proc->list);
+               _proc_destroy(proc);
+               goto delete_from_list_unlock;
+       }
+
+       proc = _get_from_list_no_lock(checker, data, &_reg_proc_list);
+       if (proc) {
+               list_del(&proc->list);
+               _proc_unreg_no_lock(proc);
+               _proc_destroy(proc);
+       }
+
+delete_from_list_unlock:
+       _unlock_proc_list();
+}
+
+
+
+int pp_add_by_path(const char *path)
+{
+       struct dentry *dentry;
+       struct pf_group *pfg;
+       struct process_t *proc;
+       int ret;
+
+       dentry = swap_get_dentry(path);
+       if (!dentry) {
+               pr_warn(PRELOAD_PREFIX "Can't get dentry for <%s>\n", path);
+               return -EINVAL;
+       }
+
+       pfg = get_pf_group_by_dentry(dentry, dentry);
+       if (pfg == NULL) {
+               ret = -ENOMEM;
+               goto by_path_put_dentry;
+       }
+
+       proc = _proc_create(pfg);
+       if (!proc) {
+               ret = -EINVAL;
+               goto by_path_put_pfg;
+       }
+
+       proc->idt = SET_BY_PATH;
+       proc->dentry = dentry;
+
+       ret = _add_to_list(proc);
+       if (ret)
+               goto by_path_free;
+
+       return 0;
+
+by_path_free:
+       kfree(proc);
+
+by_path_put_pfg:
+       put_pf_group(pfg);
+
+by_path_put_dentry:
+       swap_put_dentry(dentry);
+
+       return ret;
+}
+
+int pp_add_by_pid(pid_t pid)
+{
+       struct pf_group *pfg;
+       struct process_t *proc;
+       int ret;
+
+       pfg = get_pf_group_by_tgid(pid, NULL);
+       if (pfg == NULL)
+               return -ENOMEM;
+
+       proc = _proc_create(pfg);
+       if (!proc) {
+               ret = -EINVAL;
+               goto by_pid_put_pfg;
+       }
+
+       proc->idt = SET_BY_PID;
+       proc->pid = pid;
+
+       ret = _add_to_list(proc);
+       if (ret)
+               goto by_pid_free;
+
+       return 0;
+
+by_pid_free:
+       kfree(proc);
+
+by_pid_put_pfg:
+       put_pf_group(pfg);
+
+       return ret;
+}
+
+int pp_add_by_id(const char *id)
+{
+       char *new_id;
+       struct pf_group *pfg;
+       struct process_t *proc;
+       int ret;
+
+       new_id = kstrdup(id, GFP_KERNEL);
+       if (!new_id)
+               return -ENOMEM;
+
+       pfg = get_pf_group_by_comm((char *)id, NULL);
+       if (pfg == NULL) {
+               ret = -ENOMEM;
+               goto by_id_free_new_id;
+       }
+
+       proc = _proc_create(pfg);
+       if (!proc) {
+               ret = -EINVAL;
+               goto by_id_put_pfg;
+       }
+
+       proc->idt = SET_BY_ID;
+       proc->id = new_id;
+
+       ret = _add_to_list(proc);
+       if (ret)
+               goto by_id_free;
+
+       kfree(new_id);
+
+       return 0;
+
+by_id_free:
+       kfree(proc);
+
+by_id_put_pfg:
+       put_pf_group(pfg);
+
+by_id_free_new_id:
+       kfree(new_id);
+
+       return ret;
+}
+
+int pp_del_by_path(const char *path)
+{
+       struct dentry *dentry;
+
+       dentry = swap_get_dentry(path);
+       if (!dentry) {
+               pr_warn(PRELOAD_PREFIX "No dentry for <%s>\n", path);
+               return -EINVAL;
+       }
+
+       _delete_from_list(_check_by_dentry, dentry);
+
+       swap_put_dentry(dentry);
+
+       return 0;
+}
+
+int pp_del_by_pid(pid_t pid)
+{
+       _delete_from_list(_check_by_pid, &pid);
+
+       return 0;
+}
+
+int pp_del_by_id(const char *id)
+{
+       _delete_from_list(_check_by_id, (void *)id);
+
+       return 0;
+}
+
+void pp_del_all(void)
+{
+       struct process_t *tmp, *proc;
+
+       _lock_proc_list();
+
+       list_for_each_entry_safe(proc, tmp, &_unreg_proc_list, list) {
+               list_del(&proc->list);
+               _proc_destroy(proc);
+       }
+
+       list_for_each_entry_safe(proc, tmp, &_reg_proc_list, list) {
+               list_del(&proc->list);
+               _proc_unreg_no_lock(proc);
+               _proc_destroy(proc);
+       }
+
+       _unlock_proc_list();
+}
+
+void pp_disable(void)
+{
+       struct process_t *proc;
+
+       _lock_proc_list();
+
+       list_for_each_entry(proc, &_reg_proc_list, list) {
+               _proc_unreg_no_lock(proc);
+               list_move_tail(&proc->list, &_unreg_proc_list);
+       }
+
+       _unlock_proc_list();
+}
+
+int pp_enable(void)
+{
+       struct process_t *proc;
+       int ret;
+
+       if (!_is_pthread_data_available())
+               return -EINVAL;
+
+       _lock_proc_list();
+
+       list_for_each_entry(proc, &_unreg_proc_list, list) {
+               ret = _proc_reg_no_lock(proc);
+               if (ret)
+                       goto enable_pp_fail;
+               else
+                       list_move_tail(&proc->list, &_reg_proc_list);
+       }
+
+       _unlock_proc_list();
+
+       return 0;
+
+enable_pp_fail:
+       _unlock_proc_list();
+
+       pr_warn(PRELOAD_PREFIX "Error register probes, disabling...\n");
+       pp_disable();
+
+       return ret;
+}
+
+int pp_set_pthread_path(const char *path)
+{
+       struct dentry *dentry;
+
+       dentry = swap_get_dentry(path);
+       if (dentry == NULL)
+               return -EINVAL;
+
+       if (_pthread_init.dentry != NULL)
+               swap_put_dentry(_pthread_init.dentry);
+
+       _pthread_init.dentry = dentry;
+
+       return 0;
+}
+
+int pp_set_init_offset(unsigned long off)
+{
+       _pthread_init.off = off;
+
+       return 0;
+}
diff --git a/preload/preload_process.h b/preload/preload_process.h
new file mode 100644 (file)
index 0000000..5799f9c
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __PRELOAD_PROCESS_H__
+#define __PRELOAD_PROCESS_H__
+
+int pp_add_by_path(const char *path);
+int pp_add_by_pid(pid_t pid);
+int pp_add_by_id(const char *id);
+
+int pp_del_by_path(const char *path);
+int pp_del_by_pid(pid_t pid);
+int pp_del_by_id(const char *id);
+void pp_del_all(void);
+
+int pp_enable(void);
+void pp_disable(void);
+
+int pp_set_pthread_path(const char *path);
+int pp_set_init_offset(unsigned long off);
+
+#endif /* __PRELOAD_PROCESS_H__ */