diff options
author | Christian Neukirchen <chneukirchen@gmail.com> | 2014-03-11 18:02:35 +0100 |
---|---|---|
committer | Christian Neukirchen <chneukirchen@gmail.com> | 2014-03-11 18:02:35 +0100 |
commit | 2284ea8ec70d8529a00928b00b94e8fdd5e93ecb (patch) | |
tree | b1e79b6b333d4b4e417adc516d03303d7d892bc8 | |
download | extrace-2284ea8ec70d8529a00928b00b94e8fdd5e93ecb.tar.gz extrace-2284ea8ec70d8529a00928b00b94e8fdd5e93ecb.tar.xz extrace-2284ea8ec70d8529a00928b00b94e8fdd5e93ecb.zip |
initial commit of extrace
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | extrace.c | 254 |
2 files changed, 260 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2c355c4 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +CFLAGS=-Wall -pedantic + +all: extrace + +clean: + rm -f extrace diff --git a/extrace.c b/extrace.c new file mode 100644 index 0000000..f2372ce --- /dev/null +++ b/extrace.c @@ -0,0 +1,254 @@ +/* extrace - trace exec() calls system-wide + * + * Requires CONFIG_CONNECTOR=y and CONFIG_PROC_EVENTS=y. + * Requires root or "setcap cap_net_admin+ep extrace". + * + * -p PID only show processes descendant of PID + * -w show full command line + * -f flat display without indentation + * + * Copyright (C) 2014 Christian Neukirchen <chneukirchen@gmail.com> + * + * hacked from sources of: + */ +/* exec-notify, so you can watch your acrobat reader or vim executing "bash -c" + * commands ;-) + * Requires some 2.6.x Linux kernel with proc connector enabled. + * + * $ cc -Wall -ansi -pedantic -std=c99 exec-notify.c + * + * (C) 2007-2010 Sebastian Krahmer <krahmer@suse.de> original netlink handling + * stolen from an proc-connector example, copyright folows: + */ +/* Copyright (C) Matt Helsley, IBM Corp. 2005 + * Derived from fcctl.c by Guillaume Thouvenin + * Original copyright notice follows: + * + * Copyright (C) 2005 BULL SA. + * Written by 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 + */ + +#define _XOPEN_SOURCE 700 + +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/socket.h> +#include <sys/types.h> + +#include <linux/connector.h> +#include <linux/netlink.h> +#include <linux/cn_proc.h> + +#define max(x,y) ((y)<(x)?(x):(y)) +#define min(x,y) ((y)>(x)?(x):(y)) + +#define SEND_MESSAGE_LEN (NLMSG_LENGTH(sizeof (struct cn_msg) + \ + sizeof (enum proc_cn_mcast_op))) +#define RECV_MESSAGE_LEN (NLMSG_LENGTH(sizeof (struct cn_msg) + \ + sizeof (struct proc_event))) + +#define SEND_MESSAGE_SIZE (NLMSG_SPACE(SEND_MESSAGE_LEN)) +#define RECV_MESSAGE_SIZE (NLMSG_SPACE(RECV_MESSAGE_LEN)) + +#define BUFF_SIZE (max(max(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE), 1024)) +#define MIN_RECV_SIZE (min(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE)) + +#define CMDLINE_MAX 1024 +pid_t parent = 1; +int width = 80; +int flat = 0; + +int +pid_depth(pid_t pid) +{ + pid_t ppid = 0; + FILE *f; + char name[PATH_MAX]; + int d; + + snprintf(name, sizeof name, "/proc/%d/stat", pid); + + if ((f = fopen(name, "r"))) { + fscanf(f, "%*d (%*[^)]) %*c %d", &ppid); + fclose(f); + } + + if (ppid == parent) + return 0; + + if (ppid == 0) + return -1; /* a parent we are not interested in */ + + d = pid_depth(ppid); + if (d == -1) + return -1; + + return d+1; +} + +void +handle_msg(struct cn_msg *cn_hdr) +{ + char cmdline[CMDLINE_MAX], name[PATH_MAX]; + char buf[CMDLINE_MAX]; + + int r = 0, fd, i, d; + struct proc_event *ev = (struct proc_event *)cn_hdr->data; + + if (ev->what == PROC_EVENT_EXEC) { + d = pid_depth(ev->event_data.exec.process_pid); + if (d < 0) + return; + + snprintf(name, sizeof name, "/proc/%d/cmdline", + ev->event_data.exec.process_pid); + + memset(&cmdline, 0, sizeof cmdline); + fd = open(name, O_RDONLY); + if (fd > 0) { + r = read(fd, cmdline, sizeof cmdline); + close(fd); + + /* convert nuls (argument seperators) to spaces */ + for (i = 0; r > 0 && i < r; i++) + if (cmdline[i] == 0) + cmdline[i] = ' '; + } + + snprintf(buf, min(sizeof buf, width+1), + "%*s%d %s", flat ? 0 : 2*d, "", + ev->event_data.exec.process_pid, + cmdline); + printf("%s\n", buf); + } +} + +int +main(int argc, char *argv[]) +{ + int sk_nl; + struct sockaddr_nl my_nla, kern_nla, from_nla; + socklen_t from_nla_len; + char buff[BUFF_SIZE]; + struct nlmsghdr *nl_hdr, *nlh; + struct cn_msg *cn_hdr; + enum proc_cn_mcast_op *mcop_msg; + size_t recv_len = 0; + int rc = -1, opt; + + if (getenv("COLUMNS")) + width = atoi(getenv("COLUMNS")); + if (width == 0) + width = 80; + + while ((opt = getopt(argc, argv, "fp:w")) != -1) + switch (opt) { + case 'f': flat = 1; break; + case 'p': parent = atoi(optarg); break; + case 'w': width = CMDLINE_MAX; break; + default: goto usage; + } + + if (optind != argc) { +usage: + fprintf(stderr, "Usage: extrace [-f] [-w] [-p PID]\n"); + exit(1); + } + + setvbuf(stdout, NULL, _IONBF, 0); + + sk_nl = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (sk_nl == -1) { + printf("socket sk_nl error"); + exit(1); + } + + my_nla.nl_family = AF_NETLINK; + my_nla.nl_groups = CN_IDX_PROC; + my_nla.nl_pid = getpid(); + + kern_nla.nl_family = AF_NETLINK; + kern_nla.nl_groups = CN_IDX_PROC; + kern_nla.nl_pid = 1; + + if (bind(sk_nl, (struct sockaddr *)&my_nla, sizeof my_nla) == -1) { + perror("binding sk_nl error"); + goto close_and_exit; + } + nl_hdr = (struct nlmsghdr *)buff; + cn_hdr = (struct cn_msg *)NLMSG_DATA(nl_hdr); + mcop_msg = (enum proc_cn_mcast_op*)&cn_hdr->data[0]; + + memset(buff, 0, sizeof buff); + *mcop_msg = PROC_CN_MCAST_LISTEN; + + nl_hdr->nlmsg_len = SEND_MESSAGE_LEN; + nl_hdr->nlmsg_type = NLMSG_DONE; + nl_hdr->nlmsg_flags = 0; + nl_hdr->nlmsg_seq = 0; + nl_hdr->nlmsg_pid = getpid(); + + cn_hdr->id.idx = CN_IDX_PROC; + cn_hdr->id.val = CN_VAL_PROC; + cn_hdr->seq = 0; + cn_hdr->ack = 0; + cn_hdr->len = sizeof (enum proc_cn_mcast_op); + + if (send(sk_nl, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) { + printf("failed to send proc connector mcast ctl op!\n"); + goto close_and_exit; + } + + if (*mcop_msg == PROC_CN_MCAST_IGNORE) { + rc = 0; + goto close_and_exit; + } + + while (1) { + memset(buff, 0, sizeof buff); + from_nla_len = sizeof from_nla; + nlh = (struct nlmsghdr *)buff; + memcpy(&from_nla, &kern_nla, sizeof from_nla); + recv_len = recvfrom(sk_nl, buff, BUFF_SIZE, 0, + (struct sockaddr *)&from_nla, &from_nla_len); + if (from_nla.nl_pid != 0 || recv_len < 1) + continue; + + while (NLMSG_OK(nlh, recv_len)) { + if (nlh->nlmsg_type == NLMSG_NOOP) + continue; + if (nlh->nlmsg_type == NLMSG_ERROR || nlh->nlmsg_type == NLMSG_OVERRUN) + break; + + handle_msg(NLMSG_DATA(nlh)); + + if (nlh->nlmsg_type == NLMSG_DONE) + break; + nlh = NLMSG_NEXT(nlh, recv_len); + } + } + +close_and_exit: + close(sk_nl); + return rc; +} |