[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>
This commit is contained in:
parent
49364ce253
commit
9f46080c41
9 changed files with 379 additions and 0 deletions
|
@ -10,4 +10,12 @@ config CONNECTOR
|
|||
Connector support can also be built as a module. If so, the module
|
||||
will be called cn.ko.
|
||||
|
||||
config PROC_EVENTS
|
||||
boolean "Report process events to userspace"
|
||||
depends on CONNECTOR=y
|
||||
default y
|
||||
---help---
|
||||
Provide a connector that reports process events to userspace. Send
|
||||
events such as fork, exec, id change (uid, gid, suid, etc), and exit.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
obj-$(CONFIG_CONNECTOR) += cn.o
|
||||
obj-$(CONFIG_PROC_EVENTS) += cn_proc.o
|
||||
|
||||
cn-y += cn_queue.o connector.o
|
||||
|
|
222
drivers/connector/cn_proc.c
Normal file
222
drivers/connector/cn_proc.c
Normal file
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* cn_proc.c - process events connector
|
||||
*
|
||||
* Copyright (C) Matt Helsley, IBM Corp. 2005
|
||||
* Based on cn_fork.c by Guillaume Thouvenin <guillaume.thouvenin@bull.net>
|
||||
* Original copyright notice follows:
|
||||
* Copyright (C) 2005 BULL SA.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#include <linux/cn_proc.h>
|
||||
|
||||
#define CN_PROC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event))
|
||||
|
||||
static atomic_t proc_event_num_listeners = ATOMIC_INIT(0);
|
||||
static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC };
|
||||
|
||||
/* proc_counts is used as the sequence number of the netlink message */
|
||||
static DEFINE_PER_CPU(__u32, proc_event_counts) = { 0 };
|
||||
|
||||
static inline void get_seq(__u32 *ts, int *cpu)
|
||||
{
|
||||
*ts = get_cpu_var(proc_event_counts)++;
|
||||
*cpu = smp_processor_id();
|
||||
put_cpu_var(proc_counts);
|
||||
}
|
||||
|
||||
void proc_fork_connector(struct task_struct *task)
|
||||
{
|
||||
struct cn_msg *msg;
|
||||
struct proc_event *ev;
|
||||
__u8 buffer[CN_PROC_MSG_SIZE];
|
||||
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
msg = (struct cn_msg*)buffer;
|
||||
ev = (struct proc_event*)msg->data;
|
||||
get_seq(&msg->seq, &ev->cpu);
|
||||
ev->what = PROC_EVENT_FORK;
|
||||
ev->event_data.fork.parent_pid = task->real_parent->pid;
|
||||
ev->event_data.fork.parent_tgid = task->real_parent->tgid;
|
||||
ev->event_data.fork.child_pid = task->pid;
|
||||
ev->event_data.fork.child_tgid = task->tgid;
|
||||
|
||||
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
|
||||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
/* If cn_netlink_send() failed, the data is not sent */
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_exec_connector(struct task_struct *task)
|
||||
{
|
||||
struct cn_msg *msg;
|
||||
struct proc_event *ev;
|
||||
__u8 buffer[CN_PROC_MSG_SIZE];
|
||||
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
msg = (struct cn_msg*)buffer;
|
||||
ev = (struct proc_event*)msg->data;
|
||||
get_seq(&msg->seq, &ev->cpu);
|
||||
ev->what = PROC_EVENT_EXEC;
|
||||
ev->event_data.exec.process_pid = task->pid;
|
||||
ev->event_data.exec.process_tgid = task->tgid;
|
||||
|
||||
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
|
||||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_id_connector(struct task_struct *task, int which_id)
|
||||
{
|
||||
struct cn_msg *msg;
|
||||
struct proc_event *ev;
|
||||
__u8 buffer[CN_PROC_MSG_SIZE];
|
||||
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
msg = (struct cn_msg*)buffer;
|
||||
ev = (struct proc_event*)msg->data;
|
||||
ev->what = which_id;
|
||||
ev->event_data.id.process_pid = task->pid;
|
||||
ev->event_data.id.process_tgid = task->tgid;
|
||||
if (which_id == PROC_EVENT_UID) {
|
||||
ev->event_data.id.r.ruid = task->uid;
|
||||
ev->event_data.id.e.euid = task->euid;
|
||||
} else if (which_id == PROC_EVENT_GID) {
|
||||
ev->event_data.id.r.rgid = task->gid;
|
||||
ev->event_data.id.e.egid = task->egid;
|
||||
} else
|
||||
return;
|
||||
get_seq(&msg->seq, &ev->cpu);
|
||||
|
||||
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
|
||||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void proc_exit_connector(struct task_struct *task)
|
||||
{
|
||||
struct cn_msg *msg;
|
||||
struct proc_event *ev;
|
||||
__u8 buffer[CN_PROC_MSG_SIZE];
|
||||
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
msg = (struct cn_msg*)buffer;
|
||||
ev = (struct proc_event*)msg->data;
|
||||
get_seq(&msg->seq, &ev->cpu);
|
||||
ev->what = PROC_EVENT_EXIT;
|
||||
ev->event_data.exit.process_pid = task->pid;
|
||||
ev->event_data.exit.process_tgid = task->tgid;
|
||||
ev->event_data.exit.exit_code = task->exit_code;
|
||||
ev->event_data.exit.exit_signal = task->exit_signal;
|
||||
|
||||
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
|
||||
msg->ack = 0; /* not used */
|
||||
msg->len = sizeof(*ev);
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an acknowledgement message to userspace
|
||||
*
|
||||
* Use 0 for success, EFOO otherwise.
|
||||
* Note: this is the negative of conventional kernel error
|
||||
* values because it's not being returned via syscall return
|
||||
* mechanisms.
|
||||
*/
|
||||
static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
|
||||
{
|
||||
struct cn_msg *msg;
|
||||
struct proc_event *ev;
|
||||
__u8 buffer[CN_PROC_MSG_SIZE];
|
||||
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
||||
msg = (struct cn_msg*)buffer;
|
||||
ev = (struct proc_event*)msg->data;
|
||||
msg->seq = rcvd_seq;
|
||||
ev->cpu = -1;
|
||||
ev->what = PROC_EVENT_NONE;
|
||||
ev->event_data.ack.err = err;
|
||||
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
|
||||
msg->ack = rcvd_ack + 1;
|
||||
msg->len = sizeof(*ev);
|
||||
cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* cn_proc_mcast_ctl
|
||||
* @data: message sent from userspace via the connector
|
||||
*/
|
||||
static void cn_proc_mcast_ctl(void *data)
|
||||
{
|
||||
struct cn_msg *msg = data;
|
||||
enum proc_cn_mcast_op *mc_op = NULL;
|
||||
int err = 0;
|
||||
|
||||
if (msg->len != sizeof(*mc_op))
|
||||
return;
|
||||
|
||||
mc_op = (enum proc_cn_mcast_op*)msg->data;
|
||||
switch (*mc_op) {
|
||||
case PROC_CN_MCAST_LISTEN:
|
||||
atomic_inc(&proc_event_num_listeners);
|
||||
break;
|
||||
case PROC_CN_MCAST_IGNORE:
|
||||
atomic_dec(&proc_event_num_listeners);
|
||||
break;
|
||||
default:
|
||||
err = EINVAL;
|
||||
break;
|
||||
}
|
||||
cn_proc_ack(err, msg->seq, msg->ack);
|
||||
}
|
||||
|
||||
/*
|
||||
* cn_proc_init - initialization entry point
|
||||
*
|
||||
* Adds the connector callback to the connector driver.
|
||||
*/
|
||||
static int __init cn_proc_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = cn_add_callback(&cn_proc_event_id, "cn_proc",
|
||||
&cn_proc_mcast_ctl))) {
|
||||
printk(KERN_WARNING "cn_proc failed to register\n");
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(cn_proc_init);
|
|
@ -48,6 +48,7 @@
|
|||
#include <linux/syscalls.h>
|
||||
#include <linux/rmap.h>
|
||||
#include <linux/acct.h>
|
||||
#include <linux/cn_proc.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/mmu_context.h>
|
||||
|
@ -1096,6 +1097,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
|
|||
fput(bprm->file);
|
||||
bprm->file = NULL;
|
||||
current->did_exec = 1;
|
||||
proc_exec_connector(current);
|
||||
return retval;
|
||||
}
|
||||
read_lock(&binfmt_lock);
|
||||
|
|
127
include/linux/cn_proc.h
Normal file
127
include/linux/cn_proc.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* cn_proc.h - process events connector
|
||||
*
|
||||
* Copyright (C) Matt Helsley, IBM Corp. 2005
|
||||
* Based on cn_fork.h by Nguyen Anh Quynh and Guillaume Thouvenin
|
||||
* Original copyright notice follows:
|
||||
* Copyright (C) 2005 Nguyen Anh Quynh <aquynh@gmail.com>
|
||||
* Copyright (C) 2005 Guillaume Thouvenin <guillaume.thouvenin@bull.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef CN_PROC_H
|
||||
#define CN_PROC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/connector.h>
|
||||
|
||||
/*
|
||||
* Userspace sends this enum to register with the kernel that it is listening
|
||||
* for events on the connector.
|
||||
*/
|
||||
enum proc_cn_mcast_op {
|
||||
PROC_CN_MCAST_LISTEN = 1,
|
||||
PROC_CN_MCAST_IGNORE = 2
|
||||
};
|
||||
|
||||
/*
|
||||
* From the user's point of view, the process
|
||||
* ID is the thread group ID and thread ID is the internal
|
||||
* kernel "pid". So, fields are assigned as follow:
|
||||
*
|
||||
* In user space - In kernel space
|
||||
*
|
||||
* parent process ID = parent->tgid
|
||||
* parent thread ID = parent->pid
|
||||
* child process ID = child->tgid
|
||||
* child thread ID = child->pid
|
||||
*/
|
||||
|
||||
struct proc_event {
|
||||
enum what {
|
||||
/* Use successive bits so the enums can be used to record
|
||||
* sets of events as well
|
||||
*/
|
||||
PROC_EVENT_NONE = 0x00000000,
|
||||
PROC_EVENT_FORK = 0x00000001,
|
||||
PROC_EVENT_EXEC = 0x00000002,
|
||||
PROC_EVENT_UID = 0x00000004,
|
||||
PROC_EVENT_GID = 0x00000040,
|
||||
/* "next" should be 0x00000400 */
|
||||
/* "last" is the last process event: exit */
|
||||
PROC_EVENT_EXIT = 0x80000000
|
||||
} what;
|
||||
__u32 cpu;
|
||||
union { /* must be last field of proc_event struct */
|
||||
struct {
|
||||
__u32 err;
|
||||
} ack;
|
||||
|
||||
struct fork_proc_event {
|
||||
pid_t parent_pid;
|
||||
pid_t parent_tgid;
|
||||
pid_t child_pid;
|
||||
pid_t child_tgid;
|
||||
} fork;
|
||||
|
||||
struct exec_proc_event {
|
||||
pid_t process_pid;
|
||||
pid_t process_tgid;
|
||||
} exec;
|
||||
|
||||
struct id_proc_event {
|
||||
pid_t process_pid;
|
||||
pid_t process_tgid;
|
||||
union {
|
||||
uid_t ruid; /* current->uid */
|
||||
gid_t rgid; /* current->gid */
|
||||
} r;
|
||||
union {
|
||||
uid_t euid;
|
||||
gid_t egid;
|
||||
} e;
|
||||
} id;
|
||||
|
||||
struct exit_proc_event {
|
||||
pid_t process_pid;
|
||||
pid_t process_tgid;
|
||||
__u32 exit_code, exit_signal;
|
||||
} exit;
|
||||
} event_data;
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#ifdef CONFIG_PROC_EVENTS
|
||||
void proc_fork_connector(struct task_struct *task);
|
||||
void proc_exec_connector(struct task_struct *task);
|
||||
void proc_id_connector(struct task_struct *task, int which_id);
|
||||
void proc_exit_connector(struct task_struct *task);
|
||||
#else
|
||||
static inline void proc_fork_connector(struct task_struct *task)
|
||||
{}
|
||||
|
||||
static inline void proc_exec_connector(struct task_struct *task)
|
||||
{}
|
||||
|
||||
static inline void proc_id_connector(struct task_struct *task,
|
||||
int which_id)
|
||||
{}
|
||||
|
||||
static inline void proc_exit_connector(struct task_struct *task)
|
||||
{}
|
||||
#endif /* CONFIG_PROC_EVENTS */
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* CN_PROC_H */
|
|
@ -27,6 +27,12 @@
|
|||
#define CN_IDX_CONNECTOR 0xffffffff
|
||||
#define CN_VAL_CONNECTOR 0xffffffff
|
||||
|
||||
/*
|
||||
* Process Events connector unique ids -- used for message routing
|
||||
*/
|
||||
#define CN_IDX_PROC 0x1
|
||||
#define CN_VAL_PROC 0x1
|
||||
|
||||
#define CN_NETLINK_USERS 1
|
||||
|
||||
/*
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/cpuset.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/cn_proc.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
@ -863,6 +864,7 @@ fastcall NORET_TYPE void do_exit(long code)
|
|||
module_put(tsk->binfmt->module);
|
||||
|
||||
tsk->exit_code = code;
|
||||
proc_exit_connector(tsk);
|
||||
exit_notify(tsk);
|
||||
#ifdef CONFIG_NUMA
|
||||
mpol_free(tsk->mempolicy);
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include <linux/profile.h>
|
||||
#include <linux/rmap.h>
|
||||
#include <linux/acct.h>
|
||||
#include <linux/cn_proc.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/pgalloc.h>
|
||||
|
@ -1143,6 +1144,7 @@ static task_t *copy_process(unsigned long clone_flags,
|
|||
__get_cpu_var(process_counts)++;
|
||||
}
|
||||
|
||||
proc_fork_connector(p);
|
||||
if (!current->signal->tty && p->signal->tty)
|
||||
p->signal->tty = NULL;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/suspend.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/cn_proc.h>
|
||||
|
||||
#include <linux/compat.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
@ -623,6 +624,7 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
|
|||
current->egid = new_egid;
|
||||
current->gid = new_rgid;
|
||||
key_fsgid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_GID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -662,6 +664,7 @@ asmlinkage long sys_setgid(gid_t gid)
|
|||
return -EPERM;
|
||||
|
||||
key_fsgid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_GID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -751,6 +754,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
|
|||
current->fsuid = current->euid;
|
||||
|
||||
key_fsuid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_UID);
|
||||
|
||||
return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE);
|
||||
}
|
||||
|
@ -798,6 +802,7 @@ asmlinkage long sys_setuid(uid_t uid)
|
|||
current->suid = new_suid;
|
||||
|
||||
key_fsuid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_UID);
|
||||
|
||||
return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID);
|
||||
}
|
||||
|
@ -846,6 +851,7 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
|||
current->suid = suid;
|
||||
|
||||
key_fsuid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_UID);
|
||||
|
||||
return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES);
|
||||
}
|
||||
|
@ -898,6 +904,7 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
|
|||
current->sgid = sgid;
|
||||
|
||||
key_fsgid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_GID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -940,6 +947,7 @@ asmlinkage long sys_setfsuid(uid_t uid)
|
|||
}
|
||||
|
||||
key_fsuid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_UID);
|
||||
|
||||
security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
|
||||
|
||||
|
@ -968,6 +976,7 @@ asmlinkage long sys_setfsgid(gid_t gid)
|
|||
}
|
||||
current->fsgid = gid;
|
||||
key_fsgid_changed(current);
|
||||
proc_id_connector(current, PROC_EVENT_GID);
|
||||
}
|
||||
return old_fsgid;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue