about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2014-03-11 18:02:35 +0100
committerChristian Neukirchen <chneukirchen@gmail.com>2014-03-11 18:02:35 +0100
commit2284ea8ec70d8529a00928b00b94e8fdd5e93ecb (patch)
treeb1e79b6b333d4b4e417adc516d03303d7d892bc8
downloadextrace-2284ea8ec70d8529a00928b00b94e8fdd5e93ecb.tar.gz
extrace-2284ea8ec70d8529a00928b00b94e8fdd5e93ecb.tar.xz
extrace-2284ea8ec70d8529a00928b00b94e8fdd5e93ecb.zip
initial commit of extrace
-rw-r--r--Makefile6
-rw-r--r--extrace.c254
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;
+}