Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] Process Events Connector

This patch adds a connector that reports fork, exec, id change, and exit
events for all processes to userspace. It replaces the fork_advisor patch
that ELSA is currently using. Applications that may find these events
useful include accounting/auditing (e.g. ELSA), system activity monitoring
(e.g. top), security, and resource management (e.g. CKRM).

Signed-off-by: Matt Helsley <matthltc@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Matt Helsley and committed by
Linus Torvalds
9f46080c 49364ce2

+379
+8
drivers/connector/Kconfig
··· 10 10 Connector support can also be built as a module. If so, the module 11 11 will be called cn.ko. 12 12 13 + config PROC_EVENTS 14 + boolean "Report process events to userspace" 15 + depends on CONNECTOR=y 16 + default y 17 + ---help--- 18 + Provide a connector that reports process events to userspace. Send 19 + events such as fork, exec, id change (uid, gid, suid, etc), and exit. 20 + 13 21 endmenu
+1
drivers/connector/Makefile
··· 1 1 obj-$(CONFIG_CONNECTOR) += cn.o 2 + obj-$(CONFIG_PROC_EVENTS) += cn_proc.o 2 3 3 4 cn-y += cn_queue.o connector.o
+222
drivers/connector/cn_proc.c
··· 1 + /* 2 + * cn_proc.c - process events connector 3 + * 4 + * Copyright (C) Matt Helsley, IBM Corp. 2005 5 + * Based on cn_fork.c by Guillaume Thouvenin <guillaume.thouvenin@bull.net> 6 + * Original copyright notice follows: 7 + * Copyright (C) 2005 BULL SA. 8 + * 9 + * 10 + * This program is free software; you can redistribute it and/or modify 11 + * it under the terms of the GNU General Public License as published by 12 + * the Free Software Foundation; either version 2 of the License, or 13 + * (at your option) any later version. 14 + * 15 + * This program is distributed in the hope that it will be useful, 16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 + * GNU General Public License for more details. 19 + * 20 + * You should have received a copy of the GNU General Public License 21 + * along with this program; if not, write to the Free Software 22 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 + */ 24 + 25 + #include <linux/module.h> 26 + #include <linux/kernel.h> 27 + #include <linux/init.h> 28 + #include <asm/atomic.h> 29 + 30 + #include <linux/cn_proc.h> 31 + 32 + #define CN_PROC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event)) 33 + 34 + static atomic_t proc_event_num_listeners = ATOMIC_INIT(0); 35 + static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC }; 36 + 37 + /* proc_counts is used as the sequence number of the netlink message */ 38 + static DEFINE_PER_CPU(__u32, proc_event_counts) = { 0 }; 39 + 40 + static inline void get_seq(__u32 *ts, int *cpu) 41 + { 42 + *ts = get_cpu_var(proc_event_counts)++; 43 + *cpu = smp_processor_id(); 44 + put_cpu_var(proc_counts); 45 + } 46 + 47 + void proc_fork_connector(struct task_struct *task) 48 + { 49 + struct cn_msg *msg; 50 + struct proc_event *ev; 51 + __u8 buffer[CN_PROC_MSG_SIZE]; 52 + 53 + if (atomic_read(&proc_event_num_listeners) < 1) 54 + return; 55 + 56 + msg = (struct cn_msg*)buffer; 57 + ev = (struct proc_event*)msg->data; 58 + get_seq(&msg->seq, &ev->cpu); 59 + ev->what = PROC_EVENT_FORK; 60 + ev->event_data.fork.parent_pid = task->real_parent->pid; 61 + ev->event_data.fork.parent_tgid = task->real_parent->tgid; 62 + ev->event_data.fork.child_pid = task->pid; 63 + ev->event_data.fork.child_tgid = task->tgid; 64 + 65 + memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); 66 + msg->ack = 0; /* not used */ 67 + msg->len = sizeof(*ev); 68 + /* If cn_netlink_send() failed, the data is not sent */ 69 + cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); 70 + } 71 + 72 + void proc_exec_connector(struct task_struct *task) 73 + { 74 + struct cn_msg *msg; 75 + struct proc_event *ev; 76 + __u8 buffer[CN_PROC_MSG_SIZE]; 77 + 78 + if (atomic_read(&proc_event_num_listeners) < 1) 79 + return; 80 + 81 + msg = (struct cn_msg*)buffer; 82 + ev = (struct proc_event*)msg->data; 83 + get_seq(&msg->seq, &ev->cpu); 84 + ev->what = PROC_EVENT_EXEC; 85 + ev->event_data.exec.process_pid = task->pid; 86 + ev->event_data.exec.process_tgid = task->tgid; 87 + 88 + memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); 89 + msg->ack = 0; /* not used */ 90 + msg->len = sizeof(*ev); 91 + cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); 92 + } 93 + 94 + void proc_id_connector(struct task_struct *task, int which_id) 95 + { 96 + struct cn_msg *msg; 97 + struct proc_event *ev; 98 + __u8 buffer[CN_PROC_MSG_SIZE]; 99 + 100 + if (atomic_read(&proc_event_num_listeners) < 1) 101 + return; 102 + 103 + msg = (struct cn_msg*)buffer; 104 + ev = (struct proc_event*)msg->data; 105 + ev->what = which_id; 106 + ev->event_data.id.process_pid = task->pid; 107 + ev->event_data.id.process_tgid = task->tgid; 108 + if (which_id == PROC_EVENT_UID) { 109 + ev->event_data.id.r.ruid = task->uid; 110 + ev->event_data.id.e.euid = task->euid; 111 + } else if (which_id == PROC_EVENT_GID) { 112 + ev->event_data.id.r.rgid = task->gid; 113 + ev->event_data.id.e.egid = task->egid; 114 + } else 115 + return; 116 + get_seq(&msg->seq, &ev->cpu); 117 + 118 + memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); 119 + msg->ack = 0; /* not used */ 120 + msg->len = sizeof(*ev); 121 + cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); 122 + } 123 + 124 + void proc_exit_connector(struct task_struct *task) 125 + { 126 + struct cn_msg *msg; 127 + struct proc_event *ev; 128 + __u8 buffer[CN_PROC_MSG_SIZE]; 129 + 130 + if (atomic_read(&proc_event_num_listeners) < 1) 131 + return; 132 + 133 + msg = (struct cn_msg*)buffer; 134 + ev = (struct proc_event*)msg->data; 135 + get_seq(&msg->seq, &ev->cpu); 136 + ev->what = PROC_EVENT_EXIT; 137 + ev->event_data.exit.process_pid = task->pid; 138 + ev->event_data.exit.process_tgid = task->tgid; 139 + ev->event_data.exit.exit_code = task->exit_code; 140 + ev->event_data.exit.exit_signal = task->exit_signal; 141 + 142 + memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); 143 + msg->ack = 0; /* not used */ 144 + msg->len = sizeof(*ev); 145 + cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); 146 + } 147 + 148 + /* 149 + * Send an acknowledgement message to userspace 150 + * 151 + * Use 0 for success, EFOO otherwise. 152 + * Note: this is the negative of conventional kernel error 153 + * values because it's not being returned via syscall return 154 + * mechanisms. 155 + */ 156 + static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack) 157 + { 158 + struct cn_msg *msg; 159 + struct proc_event *ev; 160 + __u8 buffer[CN_PROC_MSG_SIZE]; 161 + 162 + if (atomic_read(&proc_event_num_listeners) < 1) 163 + return; 164 + 165 + msg = (struct cn_msg*)buffer; 166 + ev = (struct proc_event*)msg->data; 167 + msg->seq = rcvd_seq; 168 + ev->cpu = -1; 169 + ev->what = PROC_EVENT_NONE; 170 + ev->event_data.ack.err = err; 171 + memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); 172 + msg->ack = rcvd_ack + 1; 173 + msg->len = sizeof(*ev); 174 + cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); 175 + } 176 + 177 + /** 178 + * cn_proc_mcast_ctl 179 + * @data: message sent from userspace via the connector 180 + */ 181 + static void cn_proc_mcast_ctl(void *data) 182 + { 183 + struct cn_msg *msg = data; 184 + enum proc_cn_mcast_op *mc_op = NULL; 185 + int err = 0; 186 + 187 + if (msg->len != sizeof(*mc_op)) 188 + return; 189 + 190 + mc_op = (enum proc_cn_mcast_op*)msg->data; 191 + switch (*mc_op) { 192 + case PROC_CN_MCAST_LISTEN: 193 + atomic_inc(&proc_event_num_listeners); 194 + break; 195 + case PROC_CN_MCAST_IGNORE: 196 + atomic_dec(&proc_event_num_listeners); 197 + break; 198 + default: 199 + err = EINVAL; 200 + break; 201 + } 202 + cn_proc_ack(err, msg->seq, msg->ack); 203 + } 204 + 205 + /* 206 + * cn_proc_init - initialization entry point 207 + * 208 + * Adds the connector callback to the connector driver. 209 + */ 210 + static int __init cn_proc_init(void) 211 + { 212 + int err; 213 + 214 + if ((err = cn_add_callback(&cn_proc_event_id, "cn_proc", 215 + &cn_proc_mcast_ctl))) { 216 + printk(KERN_WARNING "cn_proc failed to register\n"); 217 + return err; 218 + } 219 + return 0; 220 + } 221 + 222 + module_init(cn_proc_init);
+2
fs/exec.c
··· 48 48 #include <linux/syscalls.h> 49 49 #include <linux/rmap.h> 50 50 #include <linux/acct.h> 51 + #include <linux/cn_proc.h> 51 52 52 53 #include <asm/uaccess.h> 53 54 #include <asm/mmu_context.h> ··· 1097 1096 fput(bprm->file); 1098 1097 bprm->file = NULL; 1099 1098 current->did_exec = 1; 1099 + proc_exec_connector(current); 1100 1100 return retval; 1101 1101 } 1102 1102 read_lock(&binfmt_lock);
+127
include/linux/cn_proc.h
··· 1 + /* 2 + * cn_proc.h - process events connector 3 + * 4 + * Copyright (C) Matt Helsley, IBM Corp. 2005 5 + * Based on cn_fork.h by Nguyen Anh Quynh and Guillaume Thouvenin 6 + * Original copyright notice follows: 7 + * Copyright (C) 2005 Nguyen Anh Quynh <aquynh@gmail.com> 8 + * Copyright (C) 2005 Guillaume Thouvenin <guillaume.thouvenin@bull.net> 9 + * 10 + * This program is free software; you can redistribute it and/or modify 11 + * it under the terms of the GNU General Public License as published by 12 + * the Free Software Foundation; either version 2 of the License, or 13 + * (at your option) any later version. 14 + * 15 + * This program is distributed in the hope that it will be useful, 16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 + * GNU General Public License for more details. 19 + * 20 + * You should have received a copy of the GNU General Public License 21 + * along with this program; if not, write to the Free Software 22 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 + */ 24 + 25 + #ifndef CN_PROC_H 26 + #define CN_PROC_H 27 + 28 + #include <linux/types.h> 29 + #include <linux/connector.h> 30 + 31 + /* 32 + * Userspace sends this enum to register with the kernel that it is listening 33 + * for events on the connector. 34 + */ 35 + enum proc_cn_mcast_op { 36 + PROC_CN_MCAST_LISTEN = 1, 37 + PROC_CN_MCAST_IGNORE = 2 38 + }; 39 + 40 + /* 41 + * From the user's point of view, the process 42 + * ID is the thread group ID and thread ID is the internal 43 + * kernel "pid". So, fields are assigned as follow: 44 + * 45 + * In user space - In kernel space 46 + * 47 + * parent process ID = parent->tgid 48 + * parent thread ID = parent->pid 49 + * child process ID = child->tgid 50 + * child thread ID = child->pid 51 + */ 52 + 53 + struct proc_event { 54 + enum what { 55 + /* Use successive bits so the enums can be used to record 56 + * sets of events as well 57 + */ 58 + PROC_EVENT_NONE = 0x00000000, 59 + PROC_EVENT_FORK = 0x00000001, 60 + PROC_EVENT_EXEC = 0x00000002, 61 + PROC_EVENT_UID = 0x00000004, 62 + PROC_EVENT_GID = 0x00000040, 63 + /* "next" should be 0x00000400 */ 64 + /* "last" is the last process event: exit */ 65 + PROC_EVENT_EXIT = 0x80000000 66 + } what; 67 + __u32 cpu; 68 + union { /* must be last field of proc_event struct */ 69 + struct { 70 + __u32 err; 71 + } ack; 72 + 73 + struct fork_proc_event { 74 + pid_t parent_pid; 75 + pid_t parent_tgid; 76 + pid_t child_pid; 77 + pid_t child_tgid; 78 + } fork; 79 + 80 + struct exec_proc_event { 81 + pid_t process_pid; 82 + pid_t process_tgid; 83 + } exec; 84 + 85 + struct id_proc_event { 86 + pid_t process_pid; 87 + pid_t process_tgid; 88 + union { 89 + uid_t ruid; /* current->uid */ 90 + gid_t rgid; /* current->gid */ 91 + } r; 92 + union { 93 + uid_t euid; 94 + gid_t egid; 95 + } e; 96 + } id; 97 + 98 + struct exit_proc_event { 99 + pid_t process_pid; 100 + pid_t process_tgid; 101 + __u32 exit_code, exit_signal; 102 + } exit; 103 + } event_data; 104 + }; 105 + 106 + #ifdef __KERNEL__ 107 + #ifdef CONFIG_PROC_EVENTS 108 + void proc_fork_connector(struct task_struct *task); 109 + void proc_exec_connector(struct task_struct *task); 110 + void proc_id_connector(struct task_struct *task, int which_id); 111 + void proc_exit_connector(struct task_struct *task); 112 + #else 113 + static inline void proc_fork_connector(struct task_struct *task) 114 + {} 115 + 116 + static inline void proc_exec_connector(struct task_struct *task) 117 + {} 118 + 119 + static inline void proc_id_connector(struct task_struct *task, 120 + int which_id) 121 + {} 122 + 123 + static inline void proc_exit_connector(struct task_struct *task) 124 + {} 125 + #endif /* CONFIG_PROC_EVENTS */ 126 + #endif /* __KERNEL__ */ 127 + #endif /* CN_PROC_H */
+6
include/linux/connector.h
··· 27 27 #define CN_IDX_CONNECTOR 0xffffffff 28 28 #define CN_VAL_CONNECTOR 0xffffffff 29 29 30 + /* 31 + * Process Events connector unique ids -- used for message routing 32 + */ 33 + #define CN_IDX_PROC 0x1 34 + #define CN_VAL_PROC 0x1 35 + 30 36 #define CN_NETLINK_USERS 1 31 37 32 38 /*
+2
kernel/exit.c
··· 28 28 #include <linux/cpuset.h> 29 29 #include <linux/syscalls.h> 30 30 #include <linux/signal.h> 31 + #include <linux/cn_proc.h> 31 32 32 33 #include <asm/uaccess.h> 33 34 #include <asm/unistd.h> ··· 864 863 module_put(tsk->binfmt->module); 865 864 866 865 tsk->exit_code = code; 866 + proc_exit_connector(tsk); 867 867 exit_notify(tsk); 868 868 #ifdef CONFIG_NUMA 869 869 mpol_free(tsk->mempolicy);
+2
kernel/fork.c
··· 42 42 #include <linux/profile.h> 43 43 #include <linux/rmap.h> 44 44 #include <linux/acct.h> 45 + #include <linux/cn_proc.h> 45 46 46 47 #include <asm/pgtable.h> 47 48 #include <asm/pgalloc.h> ··· 1144 1143 __get_cpu_var(process_counts)++; 1145 1144 } 1146 1145 1146 + proc_fork_connector(p); 1147 1147 if (!current->signal->tty && p->signal->tty) 1148 1148 p->signal->tty = NULL; 1149 1149
+9
kernel/sys.c
··· 28 28 #include <linux/suspend.h> 29 29 #include <linux/tty.h> 30 30 #include <linux/signal.h> 31 + #include <linux/cn_proc.h> 31 32 32 33 #include <linux/compat.h> 33 34 #include <linux/syscalls.h> ··· 624 623 current->egid = new_egid; 625 624 current->gid = new_rgid; 626 625 key_fsgid_changed(current); 626 + proc_id_connector(current, PROC_EVENT_GID); 627 627 return 0; 628 628 } 629 629 ··· 664 662 return -EPERM; 665 663 666 664 key_fsgid_changed(current); 665 + proc_id_connector(current, PROC_EVENT_GID); 667 666 return 0; 668 667 } 669 668 ··· 754 751 current->fsuid = current->euid; 755 752 756 753 key_fsuid_changed(current); 754 + proc_id_connector(current, PROC_EVENT_UID); 757 755 758 756 return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE); 759 757 } ··· 802 798 current->suid = new_suid; 803 799 804 800 key_fsuid_changed(current); 801 + proc_id_connector(current, PROC_EVENT_UID); 805 802 806 803 return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID); 807 804 } ··· 851 846 current->suid = suid; 852 847 853 848 key_fsuid_changed(current); 849 + proc_id_connector(current, PROC_EVENT_UID); 854 850 855 851 return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES); 856 852 } ··· 904 898 current->sgid = sgid; 905 899 906 900 key_fsgid_changed(current); 901 + proc_id_connector(current, PROC_EVENT_GID); 907 902 return 0; 908 903 } 909 904 ··· 947 940 } 948 941 949 942 key_fsuid_changed(current); 943 + proc_id_connector(current, PROC_EVENT_UID); 950 944 951 945 security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS); 952 946 ··· 976 968 } 977 969 current->fsgid = gid; 978 970 key_fsgid_changed(current); 971 + proc_id_connector(current, PROC_EVENT_GID); 979 972 } 980 973 return old_fsgid; 981 974 }