[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:
Matt Helsley 2005-11-07 00:59:16 -08:00 committed by Linus Torvalds
parent 49364ce253
commit 9f46080c41
9 changed files with 379 additions and 0 deletions

View File

@ -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

View File

@ -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
View 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);

View File

@ -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
View 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 */

View File

@ -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
/*

View File

@ -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);

View File

@ -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;

View File

@ -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;
}