Hello community, here is the log from the commit of package forkstat for openSUSE:Factory checked in at 2017-06-27 10:21:47 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/forkstat (Old) and /work/SRC/openSUSE:Factory/.forkstat.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "forkstat" Tue Jun 27 10:21:47 2017 rev:4 rq:506335 version:0.02.00 Changes: -------- --- /work/SRC/openSUSE:Factory/forkstat/forkstat.changes 2017-06-26 15:53:32.992775741 +0200 +++ /work/SRC/openSUSE:Factory/.forkstat.new/forkstat.changes 2017-06-27 10:21:54.329825789 +0200 @@ -1,0 +2,24 @@ +Sun Jun 25 19:12:00 UTC 2017 - [email protected] + +- update to version 0.02.00 + * Makefile: update version and change codename + * Minor tweaks to the manual, cosmetic changes + * Move arrays to end of stacks to help avoid any stack smashing + * Minor source clean-up + * Avoid TOCTOU race on stat + open + * Use alternative ptrace pid info + * Add -x extra UID and TTY information option + * Add uid/sid event tracing + * Display name of events on -e option when invalid event used + * Add ptrace event spying + * Fix realtime flag opts mask + * Fix -r help option + * Add extra spacing in heading as it is offset by one after PID + * Remove old set_priority code now that -r replaces it + * Manpage: fix pdf warning: + * Makefile: add pdf man page rule and add .PHONEYs + * Add Philipp Gesang to the man page for kudos for contributions. + * Add -r real time scheduling option + * Put help options into one large printf statement + +------------------------------------------------------------------- Old: ---- forkstat-0.01.19.tar.gz New: ---- forkstat-0.02.00.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ forkstat.spec ++++++ --- /var/tmp/diff_new_pack.Sc9sQb/_old 2017-06-27 10:21:54.889746653 +0200 +++ /var/tmp/diff_new_pack.Sc9sQb/_new 2017-06-27 10:21:54.889746653 +0200 @@ -18,7 +18,7 @@ Name: forkstat -Version: 0.01.19 +Version: 0.02.00 Release: 0 Summary: Process fork/exec/exit monitoring tool License: GPL-2.0+ ++++++ forkstat-0.01.19.tar.gz -> forkstat-0.02.00.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forkstat-0.01.19/Makefile new/forkstat-0.02.00/Makefile --- old/forkstat-0.01.19/Makefile 2017-06-21 18:59:39.000000000 +0200 +++ new/forkstat-0.02.00/Makefile 2017-06-24 21:43:28.000000000 +0200 @@ -16,9 +16,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # -VERSION=0.01.19 +VERSION=0.02.00 # -# Version "Frantic Forking Finder" +# Version "Perspicacious Process Peeker" # CFLAGS += -Wall -Wextra -DVERSION='"$(VERSION)"' -O2 @@ -43,6 +43,7 @@ forkstat.8.gz: forkstat.8 gzip -c $< > $@ +.PHONEY: dist dist: rm -rf forkstat-$(VERSION) mkdir forkstat-$(VERSION) @@ -51,10 +52,17 @@ tar -zcf forkstat-$(VERSION).tar.gz forkstat-$(VERSION) rm -rf forkstat-$(VERSION) +.PHONEY: pdf +pdf: + man -t ./forkstat.8 | ps2pdf - > forkstat.pdf + +.PHONEY: clean clean: rm -f forkstat forkstat.o forkstat.8.gz rm -f forkstat-$(VERSION).tar.gz + rm -f forkstat.pdf +.PHONEY: install install: forkstat forkstat.8.gz mkdir -p ${DESTDIR}${BINDIR} cp forkstat ${DESTDIR}${BINDIR} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forkstat-0.01.19/forkstat.8 new/forkstat-0.02.00/forkstat.8 --- old/forkstat-0.01.19/forkstat.8 2017-06-21 18:59:39.000000000 +0200 +++ new/forkstat-0.02.00/forkstat.8 2017-06-24 21:43:28.000000000 +0200 @@ -2,7 +2,7 @@ .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) -.TH FORKSTAT 8 "21 June, 2017" +.TH FORKSTAT 8 "24 June, 2017" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: @@ -26,9 +26,11 @@ .RI [ \-e ] .RI [ \-h ] .RI [ \-l ] +.RI [ \-r ] .RI [ \-s ] .RI [ \-S ] .RI [ \-q ] +.RI [ \-x ] .br .SH DESCRIPTION @@ -43,12 +45,12 @@ Forkstat will display several columns of process related information: .TS -l lw(5i). +l l. \fBTitle Description\fR Time When the fork/exec/exit event occurred. Event Type of event. PID Process or thread ID. -Info Parent or child if a fork, or exit value. +Info Parent or child if a fork, or process exit(2) value. Duration T{ On exit, the duration the command ran for in seconds. Process The process name. The name will be in [ ] brackets if it is a kernel thread. @@ -67,7 +69,7 @@ specify events to trace as a comma seperated list. By default the fork, exec and exit events are traced. Available events are: .TS -l lw(5i). +l lw(4i). \fBEvent Description\fR fork forks exec execs @@ -75,12 +77,22 @@ core core dumps comm process name changes in comm field clone clone (normally on thread creation) +ptrce ptrace attach or detach +uid uid/gid events +sid sid events all all the events above .TE .TP .B \-h show brief help summary. .TP +.B \-l +set stdout to line-buffered mode. +.TP +.B \-r +run with real time FIFO scheduling with maximum priority to keep up with high volumes +of process events. +.TP .B \-s show short process name information. .TP @@ -90,8 +102,8 @@ .B \-q run quietly and enable the \-S option. .TP -.B \-l -set stdout to line-buffered mode. +.B \-x +show extra process related information: user ID and TTY of the process. .SH EXAMPLES .LP Show process activity with short process names and directory base path stripped off: @@ -121,7 +133,8 @@ .SH SEE ALSO .BR vmstat (8) .SH AUTHOR -forkstat was written by Colin King <[email protected]> +forkstat was written by Colin King <[email protected]>. Thanks also +for contributions from Philipp Gesang. .PP This manual page was written by Colin King <[email protected]>, for the Ubuntu project (but may be used by others). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forkstat-0.01.19/forkstat.c new/forkstat-0.02.00/forkstat.c --- old/forkstat-0.01.19/forkstat.c 2017-06-21 18:59:39.000000000 +0200 +++ new/forkstat-0.02.00/forkstat.c 2017-06-24 21:43:28.000000000 +0200 @@ -39,6 +39,7 @@ #include <time.h> #include <getopt.h> #include <sched.h> +#include <pwd.h> #include <sys/ioctl.h> #include <sys/time.h> @@ -52,24 +53,32 @@ #include <linux/cn_proc.h> #define APP_NAME "forkstat" -#define MAX_PIDS (32769) /* Hash Max PIDs */ +#define MAX_PIDS (32769) /* Hash Max PIDs */ -#define NULL_PID (-1) - -#define OPT_CMD_LONG (0x00000001) -#define OPT_CMD_SHORT (0x00000002) -#define OPT_CMD_DIRNAME_STRIP (0x00000004) -#define OPT_STATS (0x00000008) -#define OPT_QUIET (0x00000010) - -#define OPT_EV_FORK (0x00000100) -#define OPT_EV_EXEC (0x00000200) -#define OPT_EV_EXIT (0x00000400) -#define OPT_EV_CORE (0x00000800) -#define OPT_EV_COMM (0x00001000) -#define OPT_EV_CLNE (0x00002000) -#define OPT_EV_MASK (0x00003f00) -#define OPT_EV_ALL (OPT_EV_MASK) +#define NULL_PID (pid_t)(-1) +#define NULL_UID (uid_t)(-1) +#define NULL_GID (gid_t)(-1) +#define NULL_TTY (dev_t)(-1) + +#define OPT_CMD_LONG (0x00000001) /* Long command line info */ +#define OPT_CMD_SHORT (0x00000002) /* Short command line info */ +#define OPT_CMD_DIRNAME_STRIP (0x00000004) /* Strip dirpath from command */ +#define OPT_STATS (0x00000008) /* Show stats at end of run */ +#define OPT_QUIET (0x00000010) /* Run quietly */ +#define OPT_REALTIME (0x00000020) /* Run with Real Time scheduling */ +#define OPT_EXTRA (0x00000040) /* Show extra stats */ + +#define OPT_EV_FORK (0x00000100) /* Fork event */ +#define OPT_EV_EXEC (0x00000200) /* Exec event */ +#define OPT_EV_EXIT (0x00000400) /* Exit event */ +#define OPT_EV_CORE (0x00000800) /* Coredump event */ +#define OPT_EV_COMM (0x00001000) /* Comm proc info event */ +#define OPT_EV_CLNE (0x00002000) /* Clone event */ +#define OPT_EV_PTRC (0x00004000) /* Ptrace event */ +#define OPT_EV_UID (0x00008000) /* UID event */ +#define OPT_EV_SID (0x00010000) /* SID event */ +#define OPT_EV_MASK (0x0001ff00) /* Event mask */ +#define OPT_EV_ALL (OPT_EV_MASK) /* All events */ #define GOT_TGID (0x01) #define GOT_PPID (0x02) @@ -79,11 +88,13 @@ #define LINUX_VERSION_CODE KERNEL_VERSION(2,0,0) #endif - /* /proc info cache */ typedef struct proc_info { struct proc_info *next; /* next proc info in hashed linked list */ pid_t pid; /* Process ID */ + uid_t uid; /* User ID */ + gid_t gid; /* GUID */ + dev_t tty; /* TTY dev */ char *cmdline; /* /proc/pid/cmdline text */ bool kernel_thread; /* true if a kernel thread */ struct timeval start; /* time when process started */ @@ -96,13 +107,16 @@ } kernel_task_info; typedef enum { - STAT_FORK = 0, - STAT_EXEC, - STAT_EXIT, - STAT_CORE, - STAT_COMM, - STAT_CLNE, - STAT_LAST + STAT_FORK = 0, /* Fork */ + STAT_EXEC, /* Exec */ + STAT_EXIT, /* Exit */ + STAT_CORE, /* Core dump */ + STAT_COMM, /* Proc comm field change */ + STAT_CLNE, /* Clone */ + STAT_PTRC, /* Ptrace */ + STAT_UID, /* UID change */ + STAT_SID, /* SID change */ + STAT_LAST /* Always last sentinal */ } event_t; typedef struct proc_stats { @@ -127,6 +141,9 @@ { "core", "Coredump", OPT_EV_CORE, STAT_CORE }, { "comm", "Comm", OPT_EV_COMM, STAT_COMM }, { "clone","Clone", OPT_EV_CLNE, STAT_CLNE }, + { "ptrce","Ptrace", OPT_EV_PTRC, STAT_PTRC }, + { "uid", "Uid", OPT_EV_UID, STAT_UID }, + { "sid", "Sid", OPT_EV_SID, STAT_SID }, { "all", "", OPT_EV_ALL, 0 }, { NULL, NULL, 0, 0 } }; @@ -144,6 +161,7 @@ /* Default void no process info struct */ static proc_info_t no_info = { .pid = NULL_PID, + .uid = NULL_UID, .cmdline = "<unknown>", .kernel_thread = false, .start = { 0, 0 }, @@ -206,6 +224,102 @@ static proc_info_t *proc_info_get(pid_t pid); /* + * get_username() + * get username from a given user id + */ +static char *get_username(const uid_t uid) +{ + struct passwd *pwd; + static char buf[12]; + + pwd = getpwuid(uid); + if (pwd) + return pwd->pw_name; + + snprintf(buf, sizeof(buf), "%d", uid); + return buf; +} + +/* + * get_tty() + * get a TTY name with device ID dev + */ +static char *get_tty(const dev_t dev) +{ + DIR *dir; + struct dirent *dirent; + static char tty[16]; + + strncpy(tty, "?", sizeof(tty)); + + dir = opendir("/dev/pts"); + if (!dir) + goto err; + + while ((dirent = readdir(dir))) { + struct stat buf; + char path[PATH_MAX]; + + if (dirent->d_name[0] == '.') + continue; + + snprintf(path, sizeof(path), "/dev/pts/%s", dirent->d_name); + if (stat(path, &buf) < 0) + continue; + + if (buf.st_rdev == dev) { + snprintf(tty, sizeof(tty), "pts/%s", dirent->d_name); + break; + } + } + + (void)closedir(dir); +err: + return tty; +} + +/* + * get_extra() + * quick and dirty way to get UID and GID from a PID, + * note that this does not cater of changes + * because of use of an effective ID. + */ +static void get_extra(const pid_t pid, proc_info_t *info) +{ + ssize_t ret; + long dev; + int fd; + char path[PATH_MAX]; + char buffer[4096]; + struct stat buf; + + info->uid = NULL_UID; + info->gid = NULL_GID; + info->tty = NULL_TTY; + + if (!(opt_flags & OPT_EXTRA)) + return; + + snprintf(path, sizeof(path), "/proc/%u/stat", pid); + fd = open(path, O_RDONLY); + if (fd < 0) + return; + + if (fstat(fd, &buf) == 0) { + info->uid = buf.st_uid; + info->gid = buf.st_gid; + } + + ret = read(fd, buffer, sizeof(buffer)); + (void)close(fd); + if (ret < 0) + return; + + if (sscanf(buffer, "%*d %*s %*s %*d %*d %*d %ld", &dev) == 1) + info->tty = (dev_t)dev; +} + +/* * pid_max_digits() * determine (or guess) maximum digits of pids */ @@ -249,14 +363,15 @@ static pid_t get_parent_pid(const pid_t pid, bool *is_thread) { FILE *fp; - char path[PATH_MAX]; - char buffer[4096]; pid_t tgid = 0, ppid = 0; unsigned int got = 0; + char path[PATH_MAX]; + char buffer[4096]; *is_thread = false; snprintf(path, sizeof(path), "/proc/%u/status", pid); - if ((fp = fopen(path, "r")) == NULL) + fp = fopen(path, "r"); + if (!fp) return 0; while (((got & GOT_ALL) != GOT_ALL) && @@ -300,10 +415,10 @@ */ static bool sane_proc_pid_info(void) { - FILE *fp; static const char pattern[] = "container="; - const char *ptr = pattern; + FILE *fp; bool ret = true; + const char *ptr = pattern; fp = fopen("/proc/1/environ", "r"); if (!fp) @@ -344,7 +459,7 @@ } else { /* In side a container, make a guess at kernel threads */ int i; - pid_t pgid = getpgid(id); + const pid_t pgid = getpgid(id); /* This fails for kernel threads inside a container */ if (pgid >= 0) @@ -397,7 +512,7 @@ static int tty_height(void) { #ifdef TIOCGWINSZ - int fd = 0; + const int fd = 0; struct winsize ws; /* if tty and we can get a sane width, return it */ @@ -423,8 +538,9 @@ pid_size = pid_max_digits(); - printf("Time Event %*.*s Info Duration Process\n", - pid_size, pid_size, "PID"); + printf("Time Event %*.*s %sInfo Duration Process\n", + pid_size, pid_size, "PID", + (opt_flags & OPT_EXTRA) ? " UID TTY " : ""); } /* @@ -433,7 +549,7 @@ */ static void row_increment(void) { - int tty_rows = tty_height(); + const int tty_rows = tty_height(); row++; if ((tty_rows > 2) && (row >= tty_rows)) { @@ -504,11 +620,11 @@ stats = stats->next; } stats = calloc(1, sizeof(*stats)); - if (stats == NULL) + if (!stats) return; /* silently ignore */ stats->name = strdup(name); - if (stats->name == NULL) { + if (!stats->name) { free(stats); return; } @@ -560,7 +676,7 @@ printf(" Total Process\n"); sorted = calloc(n, sizeof(proc_stats_t *)); - if (sorted == NULL) { + if (!sorted) { fprintf(stderr, "Cannot sort statistics, out of memory.\n"); return; } @@ -609,9 +725,9 @@ */ static char *proc_comm(const pid_t pid) { - char buffer[4096]; int fd; ssize_t ret; + char buffer[4096]; snprintf(buffer, sizeof(buffer), "/proc/%d/comm", pid); if ((fd = open(buffer, O_RDONLY)) < 0) { @@ -632,17 +748,17 @@ */ static char *proc_cmdline(const pid_t pid) { - char buffer[4096]; char *ptr; int fd; ssize_t ret; + char buffer[4096]; snprintf(buffer, sizeof(buffer), "/proc/%d/cmdline", pid); if ((fd = open(buffer, O_RDONLY)) < 0) { return proc_comm(pid); } - memset(buffer, 0, sizeof(buffer)); + (void)memset(buffer, 0, sizeof(buffer)); if ((ret = read(fd, buffer, sizeof(buffer) - 1)) <= 0) { (void)close(fd); return proc_comm(pid); @@ -686,11 +802,11 @@ */ static proc_info_t *proc_info_get(const pid_t pid) { - size_t i = proc_info_hash(pid); + const size_t i = proc_info_hash(pid); proc_info_t *info = proc_info[i]; while (info) { - if (proc_info[i]->pid == pid) + if (info->pid == pid) return info; info = info->next; } @@ -703,12 +819,14 @@ */ static void proc_info_free(const pid_t pid) { - size_t i = proc_info_hash(pid); + const size_t i = proc_info_hash(pid); proc_info_t *info = proc_info[i]; while (info) { if (info->pid == pid) { info->pid = NULL_PID; + info->uid = NULL_UID; + info->gid = NULL_GID; free(info->cmdline); info->cmdline = NULL; return; @@ -748,7 +866,8 @@ if (info == &no_info) return &no_info; - if ((newcmd = proc_cmdline(pid)) == NULL) + newcmd = proc_cmdline(pid); + if (!newcmd) return &no_info; free(info->cmdline); @@ -763,7 +882,7 @@ */ static proc_info_t *proc_info_add(const pid_t pid, struct timeval *tv) { - size_t i = proc_info_hash(pid); + const size_t i = proc_info_hash(pid); proc_info_t *info; char *cmdline; @@ -779,8 +898,9 @@ info = info->next; } - if (info == NULL) { - if ((info = calloc(1, sizeof(proc_info_t))) == NULL) { + if (!info) { + info = calloc(1, sizeof(proc_info_t)); + if (!info) { fprintf(stderr, "Cannot allocate all proc info\n"); free(cmdline); return NULL; @@ -790,6 +910,7 @@ } info->cmdline = cmdline; info->pid = pid; + get_extra(pid, info); info->kernel_thread = pid_a_kernel_thread(cmdline, pid); if (tv) @@ -814,7 +935,8 @@ snprintf(path, sizeof(path), "/proc/%i/task", pid); - if ((dir = opendir(path)) == NULL) + dir = opendir(path); + if (!dir) return; while ((dirent = readdir(dir))) { @@ -840,7 +962,8 @@ DIR *dir; struct dirent *dirent; - if ((dir = opendir("/proc")) == NULL) + dir = opendir("/proc"); + if (!dir) return -1; while ((dirent = readdir(dir))) { @@ -860,6 +983,24 @@ return 0; } +static char *extra_info(const uid_t uid) +{ + static char buf[20]; + + *buf = '\0'; + if (opt_flags & OPT_EXTRA) { + const proc_info_t *info = proc_info_get(uid); + + if (info && info->uid != NULL_UID) + snprintf(buf, sizeof(buf), "%6d %-6.6s ", info->uid, get_tty(info->tty)); + else + snprintf(buf, sizeof(buf), "%14s", ""); + } + + return buf; +} + + /* * handle_sig() * catch signal and flag a stop @@ -887,7 +1028,7 @@ return -1; } - memset(&addr, 0, sizeof(addr)); + (void)memset(&addr, 0, sizeof(addr)); addr.nl_pid = getpid(); addr.nl_family = AF_NETLINK; addr.nl_groups = CN_IDX_PROC; @@ -908,19 +1049,19 @@ */ static int netlink_listen(const int sock) { - struct iovec iov[3]; + enum proc_cn_mcast_op op; struct nlmsghdr nlmsghdr; struct cn_msg cn_msg; - enum proc_cn_mcast_op op; + struct iovec iov[3]; - memset(&nlmsghdr, 0, sizeof(nlmsghdr)); + (void)memset(&nlmsghdr, 0, sizeof(nlmsghdr)); nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(cn_msg) + sizeof(op)); nlmsghdr.nlmsg_pid = getpid(); nlmsghdr.nlmsg_type = NLMSG_DONE; iov[0].iov_base = &nlmsghdr; iov[0].iov_len = sizeof(nlmsghdr); - memset(&cn_msg, 0, sizeof(cn_msg)); + (void)memset(&cn_msg, 0, sizeof(cn_msg)); cn_msg.id.idx = CN_IDX_PROC; cn_msg.id.val = CN_VAL_PROC; cn_msg.len = sizeof(enum proc_cn_mcast_op); @@ -935,34 +1076,6 @@ } /* - * set_prioity - * set high priority to try and get netlink activty - * before short lived processes die - */ -static void set_priority(void) -{ - int max; - struct sched_param param; - int sched; - -#if defined(SCHED_DEADLINE) - sched = SCHED_DEADLINE; -#elif defined(SCHED_SCHED_FIFO) - sched = SCHED_FIFO; -#elif defined(SCHED_RR) - sched = SCHED_FIFO; -#else - sched = SCHED_OTHER; /* Oh well */ -#endif - if ((max = sched_get_priority_max(sched)) < 0) - return; - - memset(¶m, 0, sizeof(param)); - param.sched_priority = max; - (void)sched_setscheduler(getpid(), sched, ¶m); -} - -/* * monitor() * monitor system activity */ @@ -972,7 +1085,6 @@ const int pid_size = pid_max_digits(); print_heading(); - set_priority(); while (!stop_recv) { ssize_t len; @@ -982,7 +1094,8 @@ return 0; } if (len == -1) { - int err = errno; + const int err = errno; + switch (err) { case EINTR: return 0; @@ -1020,7 +1133,7 @@ struct tm tm; char when[10]; time_t now; - pid_t ppid; + pid_t pid, ppid; bool is_thread; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) @@ -1059,32 +1172,35 @@ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) case PROC_EVENT_FORK: ppid = get_parent_pid(proc_ev->event_data.fork.child_pid, &is_thread); + pid = proc_ev->event_data.fork.child_pid; proc_stats_account(proc_ev->event_data.fork.parent_pid, is_thread ? STAT_CLNE : STAT_FORK); if (gettimeofday(&tv, NULL) < 0) { - memset(&tv, 0, sizeof tv); + (void)memset(&tv, 0, sizeof tv); } info1 = proc_info_get(ppid); - info2 = proc_info_add(proc_ev->event_data.fork.child_pid, &tv); + info2 = proc_info_add(pid, &tv); if (!(opt_flags & OPT_QUIET) && (((opt_flags & OPT_EV_FORK) && !is_thread) || ((opt_flags & OPT_EV_CLNE) && is_thread))) { if (info1 != NULL && info2 != NULL) { char *type = is_thread ? "clone" : "fork"; row_increment(); - printf("%s %-5.5s %*d parent %8s %s%s%s\n", + printf("%s %-5.5s %*d %sparent %8s %s%s%s\n", when, type, pid_size, ppid, + extra_info(ppid), "", info1->kernel_thread ? "[" : "", info1->cmdline, info1->kernel_thread ? "]" : ""); row_increment(); - printf("%s %-5.5s %*d %-6.6s %8s %s%s%s\n", + printf("%s %-5.5s %*d %s%6.6s %8s %s%s%s\n", when, type, - pid_size, proc_ev->event_data.fork.child_pid, + pid_size, pid, + extra_info(pid), is_thread ? "thread" : "child", "", info1->kernel_thread ? "[" : "", @@ -1095,12 +1211,14 @@ break; case PROC_EVENT_EXEC: proc_stats_account(proc_ev->event_data.exec.process_pid, STAT_EXEC); - info1 = proc_info_update(proc_ev->event_data.exec.process_pid); + pid = proc_ev->event_data.exec.process_pid; + info1 = proc_info_update(pid); if (!(opt_flags & OPT_QUIET) && (opt_flags & OPT_EV_EXEC)) { row_increment(); - printf("%s exec %*d %8s %s%s%s\n", + printf("%s exec %*d %s %8s %s%s%s\n", when, - pid_size, proc_ev->event_data.exec.process_pid, + pid_size, pid, + extra_info(pid), "", info1->kernel_thread ? "[" : "", info1->cmdline, @@ -1110,12 +1228,13 @@ case PROC_EVENT_EXIT: proc_stats_account(proc_ev->event_data.exit.process_pid, STAT_EXIT); if (!(opt_flags & OPT_QUIET) && (opt_flags & OPT_EV_EXIT)) { - info1 = proc_info_get(proc_ev->event_data.exit.process_pid); + pid = proc_ev->event_data.exit.process_pid; + info1 = proc_info_get(pid); if (info1->start.tv_sec) { double d1, d2; if (gettimeofday(&tv, NULL) < 0) { - memset(&tv, 0, sizeof tv); + (void)memset(&tv, 0, sizeof tv); } d1 = timeval_to_double(&info1->start); d2 = timeval_to_double(&tv); @@ -1124,9 +1243,10 @@ snprintf(duration, sizeof(duration), "unknown"); } row_increment(); - printf("%s exit %*d %5d %8s %s%s%s\n", + printf("%s exit %*d %s%6d %8s %s%s%s\n", when, - pid_size, proc_ev->event_data.exit.process_pid, + pid_size, pid, + extra_info(pid), proc_ev->event_data.exit.exit_code, duration, info1->kernel_thread ? "[" : "", @@ -1135,16 +1255,64 @@ } proc_info_free(proc_ev->event_data.exit.process_pid); break; + case PROC_EVENT_UID: + proc_stats_account(proc_ev->event_data.exec.process_pid, STAT_UID); + info1 = proc_info_update(proc_ev->event_data.exec.process_pid); + if (!(opt_flags & OPT_QUIET) && (opt_flags & OPT_EV_UID)) { + row_increment(); + pid = proc_ev->event_data.exec.process_pid; + if (proc_ev->what == PROC_EVENT_UID) { + printf("%s uid %*d %s%6s %8s %s%s%s\n", + when, + pid_size, pid, + extra_info(pid), + get_username(proc_ev->event_data.id.e.euid), + "", + info1->kernel_thread ? "[" : "", + info1->cmdline, + info1->kernel_thread ? "]" : ""); + } else { + printf("%s gid %*d %6s %s%8s %s%s%s\n", + when, + pid_size, pid, + extra_info(pid), + get_username(proc_ev->event_data.id.e.euid), + "", + info1->kernel_thread ? "[" : "", + info1->cmdline, + info1->kernel_thread ? "]" : ""); + } + } + break; + case PROC_EVENT_SID: + proc_stats_account(proc_ev->event_data.exec.process_pid, STAT_SID); + info1 = proc_info_update(proc_ev->event_data.exec.process_pid); + if (!(opt_flags & OPT_QUIET) && (opt_flags & OPT_EV_UID)) { + row_increment(); + pid = proc_ev->event_data.exec.process_pid; + printf("%s sid %*d %s%6d %8s %s%s%s\n", + when, + pid_size, pid, + extra_info(pid), + proc_ev->event_data.sid.process_pid, + "", + info1->kernel_thread ? "[" : "", + info1->cmdline, + info1->kernel_thread ? "]" : ""); + } + break; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0) case PROC_EVENT_COREDUMP: proc_stats_account(proc_ev->event_data.coredump.process_pid, STAT_CORE); if (!(opt_flags & OPT_QUIET) && (opt_flags & OPT_EV_CORE)) { - info1 = proc_info_get(proc_ev->event_data.coredump.process_pid); + pid = proc_ev->event_data.coredump.process_pid; + info1 = proc_info_get(pid); row_increment(); - printf("%s core %*d %8s %s%s%s\n", + printf("%s core %*d %s %8s %s%s%s\n", when, - pid_size, proc_ev->event_data.exit.process_pid, + pid_size, pid, + extra_info(pid), "", info1->kernel_thread ? "[" : "", info1->cmdline, @@ -1152,19 +1320,47 @@ } break; #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) + case PROC_EVENT_PTRACE: + proc_stats_account(proc_ev->event_data.comm.process_pid, STAT_PTRC); + if (!(opt_flags & OPT_QUIET) && (opt_flags & OPT_EV_PTRC)) { + const bool attach = (proc_ev->event_data.ptrace.tracer_pid != 0); + +#if 0 + pid = attach ? proc_ev->event_data.ptrace.tracer_pid : + proc_ev->event_data.ptrace.process_pid; +#else + pid = proc_ev->event_data.ptrace.process_pid; +#endif + info1 = proc_info_get(pid); + row_increment(); + printf("%s ptrce %*d %s%6s %8s %s%s%s\n", + when, + pid_size, pid, + extra_info(pid), + attach ? "attach" : "detach", + "", + info1->kernel_thread ? "[" : "", + attach ? info1->cmdline : "", + info1->kernel_thread ? "]" : ""); + } + break; +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0) case PROC_EVENT_COMM: proc_stats_account(proc_ev->event_data.comm.process_pid, STAT_COMM); if (!(opt_flags & OPT_QUIET) && (opt_flags & OPT_EV_COMM)) { - info1 = proc_info_get(proc_ev->event_data.comm.process_pid); - comm = proc_comm(proc_ev->event_data.comm.process_pid); - if (comm == NULL) + pid = proc_ev->event_data.comm.process_pid; + info1 = proc_info_get(pid); + comm = proc_comm(pid); + if (!comm) break; row_increment(); - printf("%s comm %*d %8s %s%s%s -> %s\n", + printf("%s comm %*d %s %8s %s%s%s -> %s\n", when, - pid_size, proc_ev->event_data.exit.process_pid, + pid_size, pid, + extra_info(pid), "", info1->kernel_thread ? "[" : "", info1->cmdline, @@ -1190,14 +1386,15 @@ { printf("%s, version %s\n\n", APP_NAME, VERSION); printf("usage: %s [-d|-D|-e|-h|-l|-s|-S|-q]\n", argv[0]); - printf("-d\tstrip off directory path from process name.\n"); - printf("-D\tspecify run duration in seconds.\n"); - printf("-e\tselect which events to monitor.\n"); - printf("-h\tshow this help.\n"); - printf("-l\tforce stdout line buffering.\n"); - printf("-s\tshow short process name.\n"); - printf("-S\tshow event statistics at end of the run.\n"); - printf("-q\trun quietly and enable -S option.\n"); + printf("-d\tstrip off directory path from process name.\n" + "-D\tspecify run duration in seconds.\n" + "-e\tselect which events to monitor.\n" + "-h\tshow this help.\n" + "-l\tforce stdout line buffering.\n" + "-r\trun with real time FIFO scheduler.\n" + "-s\tshow short process name.\n" + "-S\tshow event statistics at end of the run.\n" + "-q\trun quietly and enable -S option.\n"); } /* @@ -1219,7 +1416,10 @@ } } if (!found) { - fprintf(stderr, "Unknown event '%s'.\n", token); + fprintf(stderr, "Unknown event '%s'. Allowed events:", token); + for (i = 0; ev_map[i].event; i++) + printf(" %s", ev_map[i].event); + printf("\n"); return -1; } } @@ -1233,7 +1433,7 @@ struct sigaction new_action; for (;;) { - int c = getopt(argc, argv, "dD:e:hlsSq"); + const int c = getopt(argc, argv, "dD:e:hlrsSqx"); if (c == -1) break; switch (c) { @@ -1254,6 +1454,9 @@ case 'h': show_help(argv); exit(EXIT_SUCCESS); + case 'r': + opt_flags |= OPT_REALTIME; + break; case 's': opt_flags &= ~OPT_CMD_LONG; opt_flags |= OPT_CMD_SHORT; @@ -1270,6 +1473,9 @@ exit(EXIT_FAILURE); } break; + case 'x': + opt_flags |= OPT_EXTRA; + break; default: show_help(argv); exit(EXIT_FAILURE); @@ -1277,14 +1483,14 @@ } if ((opt_flags & OPT_EV_MASK) == 0) - opt_flags |= (OPT_EV_FORK | OPT_EV_EXEC | OPT_EV_EXIT | OPT_EV_CLNE); + opt_flags |= (OPT_EV_FORK | OPT_EV_EXEC | OPT_EV_EXIT | OPT_EV_CLNE | OPT_EV_PTRC); if (geteuid() != 0) { fprintf(stderr, "Need to run with root access.\n"); goto abort_sock; } - memset(&new_action, 0, sizeof(new_action)); + (void)memset(&new_action, 0, sizeof(new_action)); for (i = 0; signals[i] != -1; i++) { new_action.sa_handler = handle_sig; sigemptyset(&new_action.sa_mask); @@ -1304,6 +1510,27 @@ goto abort_sock; } + if (opt_flags & OPT_REALTIME) { + struct sched_param param; + int max_prio; + const int policy = SCHED_FIFO; + + max_prio = sched_get_priority_max(policy); + if (max_prio < 0) { + fprintf(stderr, "sched_get_priority_max failed: errno=%d (%s)\n", + errno, strerror(errno)); + goto abort_sock; + } + + (void)memset(¶m, 0, sizeof(param)); + param.sched_priority = max_prio; + if (sched_setscheduler(getpid(), policy, ¶m) < 0) { + fprintf(stderr, "sched_setscheduler failed: errno=%d (%s)\n", + errno, strerror(errno)); + goto abort_sock; + } + } + sock = netlink_connect(); if (sock == -EPROTONOSUPPORT) { fprintf(stderr, "Cannot show process activity with this kernel, netlink required.\n");
