#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";
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;
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 =
* ===========================================================================
+/* ===========================================================================
+ * = 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;
}
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;
}
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:
--- /dev/null
+#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;
+}