diff options
l--------- | data/pstree/_metadata/adv-cmds.dep | 1 | ||||
-rw-r--r-- | data/pstree/_metadata/description | 1 | ||||
l--------- | data/pstree/_metadata/license | 1 | ||||
l--------- | data/pstree/_metadata/maintainer | 1 | ||||
-rw-r--r-- | data/pstree/_metadata/priority | 1 | ||||
-rw-r--r-- | data/pstree/_metadata/role | 1 | ||||
-rw-r--r-- | data/pstree/_metadata/section | 1 | ||||
-rw-r--r-- | data/pstree/_metadata/tags | 1 | ||||
-rw-r--r-- | data/pstree/_metadata/version | 1 | ||||
-rw-r--r-- | data/pstree/pstree.c | 1104 |
10 files changed, 1113 insertions, 0 deletions
diff --git a/data/pstree/_metadata/adv-cmds.dep b/data/pstree/_metadata/adv-cmds.dep new file mode 120000 index 000000000..21d4a2a77 --- /dev/null +++ b/data/pstree/_metadata/adv-cmds.dep @@ -0,0 +1 @@ +../../adv-cmds
\ No newline at end of file diff --git a/data/pstree/_metadata/description b/data/pstree/_metadata/description new file mode 100644 index 000000000..6292641ac --- /dev/null +++ b/data/pstree/_metadata/description @@ -0,0 +1 @@ +process forests are hard to see for the trees diff --git a/data/pstree/_metadata/license b/data/pstree/_metadata/license new file mode 120000 index 000000000..9c13a9a0f --- /dev/null +++ b/data/pstree/_metadata/license @@ -0,0 +1 @@ +../../../licenses/gpl-2
\ No newline at end of file diff --git a/data/pstree/_metadata/maintainer b/data/pstree/_metadata/maintainer new file mode 120000 index 000000000..0fa66e077 --- /dev/null +++ b/data/pstree/_metadata/maintainer @@ -0,0 +1 @@ +../../../people/saurik
\ No newline at end of file diff --git a/data/pstree/_metadata/priority b/data/pstree/_metadata/priority new file mode 100644 index 000000000..134d9bc32 --- /dev/null +++ b/data/pstree/_metadata/priority @@ -0,0 +1 @@ +optional diff --git a/data/pstree/_metadata/role b/data/pstree/_metadata/role new file mode 100644 index 000000000..52b4d7301 --- /dev/null +++ b/data/pstree/_metadata/role @@ -0,0 +1 @@ +hacker diff --git a/data/pstree/_metadata/section b/data/pstree/_metadata/section new file mode 100644 index 000000000..19814b684 --- /dev/null +++ b/data/pstree/_metadata/section @@ -0,0 +1 @@ +Utilities diff --git a/data/pstree/_metadata/tags b/data/pstree/_metadata/tags new file mode 100644 index 000000000..ad5d50831 --- /dev/null +++ b/data/pstree/_metadata/tags @@ -0,0 +1 @@ +purpose::console diff --git a/data/pstree/_metadata/version b/data/pstree/_metadata/version new file mode 100644 index 000000000..ccdaba5bb --- /dev/null +++ b/data/pstree/_metadata/version @@ -0,0 +1 @@ +2.35 diff --git a/data/pstree/pstree.c b/data/pstree/pstree.c new file mode 100644 index 000000000..242c11ff9 --- /dev/null +++ b/data/pstree/pstree.c @@ -0,0 +1,1104 @@ +/* This is pstree written by Fred Hucht (c) 1993-2013 * + * EMail: fred AT thp.uni-due.de * + * Feel free to copy and redistribute in terms of the * + * GNU public license. * + * + * $Id: pstree.c,v 2.35 2013-02-28 08:33:02+01 fred Exp $ + */ +static char *WhatString[]= { + "@(#)pstree $Revision: 2.35 $ by Fred Hucht (C) 1993-2013", + "@(#)EMail: fred AT thp.uni-due.de", + "$Id: pstree.c,v 2.35 2013-02-28 08:33:02+01 fred Exp $" +}; + +#define MAXLINE 8192 + +#if defined(_AIX) || defined(___AIX) /* AIX >= 3.1 */ +/* Under AIX, we directly read the process table from the kernel */ +# ifndef _AIX50 +/* problems with getprocs() under AIX 5L + * workaround contributed by Chris Benesch <chris AT fdbs.com> */ +# define USE_GetProcessesDirect +# endif /*_AIX50*/ +# define HAS_TERMDEF +extern char *termdef(int, char); +# define _ALL_SOURCE +# include <procinfo.h> +# define USE_GETPROCS + +# ifdef USE_GETPROCS +# define IFNEW(a,b) a +# define ProcInfo procsinfo +extern getprocs(struct procsinfo *, int, struct fdsinfo *, int, pid_t *, int); +# else /*USE_GETPROCS*/ +# define IFNEW(a,b) b +# define ProcInfo procinfo +extern getproc(struct procinfo *, int, int); +extern getuser(struct procinfo *, int, void *, int); +# endif /*USE_GETPROCS*/ + +extern getargs(struct ProcInfo *, int, char *, int); + +/*# define PSCMD "ps -ekf" + # define PSFORMAT "%s %ld %ld %*20c %*s %[^\n]"*/ +# define HAS_PGID +# define UID2USER +# define PSCMD "ps -eko uid,pid,ppid,pgid,thcount,args" +# define PSFORMAT "%ld %ld %ld %ld %ld %[^\n]" +# define PSVARS &P[i].uid, &P[i].pid, &P[i].ppid, &P[i].pgid, &P[i].thcount, P[i].cmd +# define PSVARSN 6 +/************************************************************************/ +#elif defined(__linux) || (defined __alpha && defined(_SYSTYPE_BSD) || defined (Tru64)) +/* TRU64 contributed by Frank Parkin <fparki AT acxiom.co.uk> + */ +# ifdef __linux +# define USE_GetProcessesDirect +# include <glob.h> +# include <sys/stat.h> +# endif +# define UID2USER +# define HAS_PGID +# define PSCMD "ps -eo uid,pid,ppid,pgid,args" +# define PSFORMAT "%ld %ld %ld %ld %[^\n]" +# define PSVARS &P[i].uid, &P[i].pid, &P[i].ppid, &P[i].pgid, P[i].cmd +# define PSVARSN 5 +/************************************************************************/ +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) +/* NetBSD contributed by Gary D. Duzan <gary AT wheel.tiac.net> + * FreeBSD contributed by Randall Hopper <rhh AT ct.picker.com> + * Darwin / Mac OS X patch by Yuji Yamano <yyamano AT kt.rim.or.jp> + * wide output format fix for NetBSD by Jeff Brown <jabrown AT caida.org> + * (Net|Open|Free)BSD & Darwin merged by Ralf Meyer <ralf AT thp.Uni-Duisburg.DE> + */ +# define HAS_PGID +# define PSCMD "ps -axwwo user,pid,ppid,pgid,command" +# define PSFORMAT "%s %ld %ld %ld %[^\n]" +# define PSVARS P[i].name, &P[i].pid, &P[i].ppid, &P[i].pgid, P[i].cmd +# define PSVARSN 5 +# define ZOMBIES_HAVE_PID_0 +/************************************************************************/ +#elif defined(sun) && (!defined(__SVR4)) /* Solaris 1.x */ +/* contributed by L. Mark Larsen <mlarsen AT ptdcs2.intel.com> */ +/* new cpp criteria by Pierre Belanger <belanger AT risq.qc.ca> */ +# define solaris1x +# define UID2USER +# ifdef mc68000 +/* contributed by Paul Kern <pkern AT utcc.utoronto.ca> */ +# define PSCMD "ps laxw" +# define PSFORMAT "%*7c%ld %ld %ld %*d %*d %*d %*x %*d %*d %*x %*14c %[^\n]" +# define uid_t int +# define NEED_STRSTR +# else +# define PSCMD "ps jaxw" +# define PSFORMAT "%ld %ld %*d %*d %*s %*d %*s %ld %*s %[^\n]" +# define PSVARS &P[i].ppid, &P[i].pid, &P[i].uid, P[i].cmd +# define PSVARSN 4 +# endif +/************************************************************************/ +#elif defined(sun) && (defined(__SVR4)) /* Solaris 2.x */ +/* contributed by Pierre Belanger <belanger AT risq.qc.ca> */ +# define solaris2x +# define PSCMD "ps -ef" +# define PSFORMAT "%s %ld %ld %*d %*s %*s %*s %[^\n]" +/************************************************************************/ +#elif defined(bsdi) +/* contributed by Dean Gaudet <dgaudet AT hotwired.com> */ +# define UID2USER +# define PSCMD "ps laxw" +# define PSFORMAT "%ld %ld %ld %*d %*d %*d %*d %*d %*s %*s %*s %*s %[^\n]" +/************************************************************************/ +#elif defined(_BSD) /* Untested */ +# define UID2USER +# define PSCMD "ps laxw" +# define PSFORMAT "%*d %*c %ld %ld %ld %*d %*d %*d %*x %*d %d %*15c %*s %[^\n]" +/************************************************************************/ +#elif defined(__convex) /* ConvexOS */ +# define UID2USER +# define PSCMD "ps laxw" +# define PSFORMAT "%*s %ld %ld %ld %*d %*g %*d %*d %*21c %*s %[^\n]" +/************************************************************************/ +#else /* HP-UX, A/UX etc. */ +# define PSCMD "ps -ef" +# define PSFORMAT "%s %ld %ld %*20c %*s %[^\n]" +#endif +/*********************** end of configurable part ***********************/ + +#ifndef PSVARS /* Set default */ +# ifdef UID2USER +# define PSVARS &P[i].uid, &P[i].pid, &P[i].ppid, P[i].cmd +# else +# define PSVARS P[i].name, &P[i].pid, &P[i].ppid, P[i].cmd +# endif +# define PSVARSN 4 +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> /* For str...() */ +#ifdef NEED_SNPRINTF +#include <stdarg.h> +int snprintf(char *, int, char *, ...); +#endif +#include <unistd.h> /* For getopt() */ +#include <pwd.h> /* For getpwnam() */ + +#include <sys/ioctl.h> /* For TIOCGSIZE/TIOCGWINSZ */ +/* #include <termios.h> */ + +#ifdef DEBUG +# include <errno.h> +#endif + +#ifdef NEED_STRSTR +static char *strstr(char *, char *); +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +struct TreeChars { + char *s2, /* SS String between header and pid */ + *p, /* PP dito, when parent of printed childs */ + *pgl, /* G Process group leader */ + *npgl, /* N No process group leader */ + *barc, /* C bar for line with child */ + *bar, /* B bar for line without child */ + *barl, /* L bar for last child */ + *sg, /* Start graphics (alt char set) */ + *eg, /* End graphics (alt char set) */ + *init; /* Init string sent at the beginning */ +}; + +/* Example: + * |-+- 01111 ... CPPN 01111 ... + * | \-+= 01112 ... B LPPG 01112 ... + * | |--= 01113 ... B CSSG 01113 ... + * | \--= 01114 ... B LSSG 01114 ... + * \--- 01115 ... LSSN 01115 ... + */ + +enum { G_ASCII = 0, G_PC850 = 1, G_VT100 = 2, G_UTF8 = 3, G_LAST }; + +/* VT sequences contributed by Randall Hopper <rhh AT ct.picker.com> */ +/* UTF8 sequences contributed by Mark-Andre Hopf <mhopf AT mark13.org> */ +static struct TreeChars TreeChars[] = { + /* SS PP G N C B L sg eg init */ + { "--", "-+", "=", "-", "|", "|", "\\", "", "", "" }, /*Ascii*/ + { "\304\304", "\304\302", "\372", "\304", "\303", "\263", "\300", "", "", "" }, /*Pc850*/ + { "qq", "qw", "`", "q", "t", "x", "m", "\016", "\017", "\033(B\033)0" }, /*Vt100*/ + { "\342\224\200\342\224\200", + /**/ "\342\224\200\342\224\254", + /**/ "=", + /**/ "\342\224\200", + /**/ "\342\224\234", + /**/ "\342\224\202", + /**/ "\342\224\224", + /**/ "", "", "" } /*UTF8*/ +}, *C; + +int MyPid, NProc, Columns, RootPid; +short showall = TRUE, soption = FALSE, Uoption = FALSE; +char *name = "", *str = NULL, *Progname; +long ipid = -1; +char *input = NULL; + +int atLdepth=0; /* LOPTION - track how deep in the print chain we are */ +int maxLdepth=100; /* LOPTION - will be changed by -l n option */ + +#ifdef DEBUG +int debug = FALSE; +#endif + +struct Proc { + long uid, pid, ppid, pgid; + char name[32], cmd[MAXLINE]; + int print; + long parent, child, sister; + unsigned long thcount; +} *P; + +#ifdef UID2USER +void uid2user(uid_t uid, char *name, int len) { +#define NUMUN 128 + static struct un_ { + uid_t uid; + char name[32]; + } un[NUMUN]; + static short n = 0; + short i; + char uid_name[32]; + char *found; +#ifdef DEBUG + if (name == NULL) { + for (i = 0; i < n; i++) + fprintf(stderr, "uid = %3d, name = %s\n", un[i].uid, un[i].name); + return; + } +#endif + for (i = n - 1; i >= 0 && un[i].uid != uid; i--); + if (i >= 0) { /* found locally */ + found = un[i].name; + } else { + struct passwd *pw = getpwuid(uid); + if (pw) { + found = pw->pw_name; + } else { + /* fix by Stan Sieler & Philippe Torche */ + snprintf(uid_name, sizeof(uid_name), "#%d", uid); + found = uid_name; + } + if (n < NUMUN) { + un[n].uid = uid; + strncpy(un[n].name, found, 9); + un[n].name[8] = '\0'; + n++; + } + } + strncpy(name, found, len); + name[len-1] = '\0'; +} +#endif + +#if defined(_AIX) || defined(___AIX) /* AIX 3.x / 4.x */ +int GetProcessesDirect(void) { + int i, nproc, maxnproc = 1024; + + struct ProcInfo *proc; + int idx; +#ifndef USE_GETPROCS + struct userinfo user; +#endif + + do { + proc = malloc(maxnproc * sizeof(struct ProcInfo)); + if (proc == NULL) { + fprintf(stderr, "Problems with malloc.\n"); + exit(1); + } + + /* Get process table */ + idx = 0; + nproc = IFNEW(getprocs(proc, sizeof(struct procsinfo), NULL, 0, + &idx, maxnproc), + getproc(proc, maxnproc, sizeof(struct procinfo)) + ); +#ifdef DEBUG + idx = errno; /* Don't ask... */ + if (debug) + fprintf(stderr, + "nproc = %d maxnproc = %d" IFNEW(" idx = %d ","") "\n", + nproc, maxnproc, idx); + errno = idx; +#endif +#ifdef USE_GETPROCS + if (nproc == -1) { + perror("getprocs"); + exit(1); + } else if (nproc == maxnproc) { + nproc = -1; + } +#endif + if (nproc == -1) { + free(proc); + maxnproc *= 2; + } + } while (nproc == -1); + + P = malloc((nproc+1) * sizeof(struct Proc)); + if (P == NULL) { + fprintf(stderr, "Problems with malloc.\n"); + exit(1); + } + + for (i = 0; i < nproc; i++) { +#ifndef USE_GETPROCS + getuser(&proc[i],sizeof(struct procinfo), + &user, sizeof(struct userinfo)); +#endif + P[i].uid = proc[i].pi_uid; + P[i].pid = proc[i].pi_pid; + P[i].ppid = proc[i].pi_ppid; + P[i].pgid = proc[i].pi_pgrp; + P[i].thcount = IFNEW(proc[i].pi_thcount, 1); + + uid2user(P[i].uid, P[i].name, sizeof(P[i].name)); + + if (IFNEW(proc[i].pi_state,proc[i].pi_stat) == SZOMB) { + strcpy(P[i].cmd, "<defunct>"); + } else { + char *c = P[i].cmd; + int ci = 0; + getargs(&proc[i], sizeof(struct procinfo), c, MAXLINE - 2); + c[MAXLINE-2] = c[MAXLINE-1] = '\0'; + + /* Collect args. Stop when we encounter two '\0' */ + while (c[ci] != '\0' && (ci += strlen(&c[ci])) < MAXLINE - 2) + c[ci++] = ' '; + + /* Drop trailing blanks */ + ci = strlen(c); + while (ci > 0 && c[ci-1] == ' ') ci--; + c[ci] = '\0'; + + /* Replace some unprintables with '?' */ + for (ci = 0; c[ci] != '\0'; ci++) + if (c[ci] == '\n' || c[ci] == '\t') c[ci] = '?'; + + /* Insert [ui_comm] when getargs returns nothing */ + if (c[0] == '\0') { + int l = strlen(IFNEW(proc[i].pi_comm,user.ui_comm)); + c[0] = '['; + strcpy(c+1, IFNEW(proc[i].pi_comm,user.ui_comm)); + c[l+1] = ']'; + c[l+2] = '\0'; + } + } +#ifdef DEBUG + if (debug) + fprintf(stderr, + "%d: uid=%5ld, name=%8s, pid=%5ld, ppid=%5ld, pgid=%5ld, tsize=%7u, dvm=%4u, " + "thcount=%2d, cmd[%d]='%s'\n", + i, P[i].uid, P[i].name, P[i].pid, P[i].ppid, P[i].pgid, + IFNEW(proc[i].pi_tsize,user.ui_tsize), + IFNEW(proc[i].pi_dvm,user.ui_dvm), + proc[i].pi_thcount, + strlen(P[i].cmd),P[i].cmd); +#endif + P[i].parent = P[i].child = P[i].sister = -1; + P[i].print = FALSE; + } + free(proc); + return nproc; +} + +#endif /* _AIX */ + +#ifdef __linux +int GetProcessesDirect(void) { + glob_t globbuf; + unsigned int i, j; + + glob("/proc/[0-9]*", GLOB_NOSORT, NULL, &globbuf); + + P = calloc(globbuf.gl_pathc, sizeof(struct Proc)); + if (P == NULL) { + fprintf(stderr, "Problems with malloc.\n"); + exit(1); + } + + for (i = j = 0; i < globbuf.gl_pathc; i++) { + char name[32]; + int c; + FILE *tn; + struct stat stat; + int k = 0; + + snprintf(name, sizeof(name), "%s%s", + globbuf.gl_pathv[globbuf.gl_pathc - i - 1], "/stat"); + tn = fopen(name, "r"); + if (tn == NULL) continue; /* process vanished since glob() */ + fscanf(tn, "%ld %s %*c %ld %ld", + &P[j].pid, P[j].cmd, &P[j].ppid, &P[j].pgid); + fstat(fileno(tn), &stat); + P[j].uid = stat.st_uid; + fclose(tn); + P[j].thcount = 1; + + snprintf(name, sizeof(name), "%s%s", + globbuf.gl_pathv[globbuf.gl_pathc - i - 1], "/cmdline"); + tn = fopen(name, "r"); + if (tn == NULL) continue; + while (k < MAXLINE - 1 && EOF != (c = fgetc(tn))) { + P[j].cmd[k++] = c == '\0' ? ' ' : c; + } + if (k > 0) P[j].cmd[k] = '\0'; + fclose(tn); + + uid2user(P[j].uid, P[j].name, sizeof(P[j].name)); + +#ifdef DEBUG + if (debug) fprintf(stderr, + "uid=%5ld, name=%8s, pid=%5ld, ppid=%5ld, pgid=%5ld, thcount=%ld, cmd='%s'\n", + P[j].uid, P[j].name, P[j].pid, P[j].ppid, P[j].pgid, P[j].thcount, P[j].cmd); +#endif + P[j].parent = P[j].child = P[j].sister = -1; + P[j].print = FALSE; + j++; + } + globfree(&globbuf); + return j; +} +#endif /* __linux */ + +int GetProcesses(void) { + FILE *tn; + int i = 0; + char line[MAXLINE], command[] = PSCMD; + + /* file read code contributed by Paul Kern <pkern AT utcc.utoronto.ca> */ + if (input != NULL) { + if (strcmp(input, "-") == 0) + tn = stdin; + else if (NULL == (tn = fopen(input,"r"))) { + perror(input); + exit(1); + } + } else { +#ifdef DEBUG + if (debug) fprintf(stderr, "calling '%s'\n", command); +#endif + if (NULL == (tn = (FILE*)popen(command,"r"))) { + perror("Problems with pipe"); + exit(1); + } + } +#ifdef DEBUG + if (debug) fprintf(stderr, "popen:errno = %d\n", errno); +#endif + + if (NULL == fgets(line, MAXLINE, tn)) { /* Throw away header line */ + fprintf(stderr, "No input.\n"); + exit(1); + } + +#ifdef DEBUG + if (debug) fputs(line, stderr); +#endif + + P = malloc(sizeof(struct Proc)); + if (P == NULL) { + fprintf(stderr, "Problems with malloc.\n"); + exit(1); + } + + while (NULL != fgets(line, MAXLINE, tn)) { + int len, num; + len = strlen(line); +#ifdef DEBUG + if (debug) { + fprintf(stderr, "len=%3d ", len); + fputs(line, stderr); + } +#endif + + if (len == MAXLINE - 1) { /* line too long, drop remaining stuff */ + char tmp[MAXLINE]; + while (MAXLINE - 1 == strlen(fgets(tmp, MAXLINE, tn))); + } + + P = realloc(P, (i+1) * sizeof(struct Proc)); + if (P == NULL) { + fprintf(stderr, "Problems with realloc.\n"); + exit(1); + } + + memset(&P[i], 0, sizeof(*P)); + +#ifdef solaris1x + { /* SunOS allows columns to run together. With the -j option, the CPU + * time used can run into the numeric user id, so make sure there is + * space between these two columns. Also, the order of the desired + * items is different. (L. Mark Larsen <mlarsen AT ptdcs2.intel.com>) + */ + char buf1[45], buf2[MAXLINE]; + buf1[44] = '\0'; + sscanf(line, "%44c%[^\n]", buf1, buf2); + snprintf(line, sizeof(line), "%s %s", buf1, buf2); + } +#endif + + num = sscanf(line, PSFORMAT, PSVARS); + + if (num != PSVARSN) { +#ifdef DEBUG + if (debug) fprintf(stderr, "dropped line, num=%d != %d\n", num, PSVARSN); +#endif + continue; + } + +#ifdef UID2USER /* get username */ + uid2user(P[i].uid, P[i].name, sizeof(P[i].name)); +#endif + +#ifdef DEBUG + if (debug) fprintf(stderr, + "uid=%5ld, name=%8s, pid=%5ld, ppid=%5ld, pgid=%5ld, thcount=%ld, cmd='%s'\n", + P[i].uid, P[i].name, P[i].pid, P[i].ppid, P[i].pgid, P[i].thcount, P[i].cmd); +#endif + P[i].parent = P[i].child = P[i].sister = -1; + P[i].print = FALSE; + i++; + } + if (input != NULL) + fclose(tn); + else + pclose(tn); + return i; +} + +int GetRootPid(void) { + int me; + for (me = 0; me < NProc; me++) { + if (P[me].pid == 1) return P[me].pid; + } + /* PID == 1 not found, so we'll take process with PPID == 0 + * Fix for TRU64 TruCluster with uniq PIDs + * reported by Frank Parkin <fparki AT acxiom.co.uk> + * re-reported by Eric van Doorn <Eric.van.Doorn AT isc.politie.nl>, + * because fix was not published by me :-/ */ + for (me = 0; me < NProc; me++) { + if (P[me].ppid == 0) return P[me].pid; + } + /* OK, still nothing found. Maybe it is FreeBSD and won't show foreign + * processes. So we also accept PPID == 1 */ + for (me = 0; me < NProc; me++) { + if (P[me].ppid == 1) return P[me].pid; + } + /* Still nothing. Maybe it is something like Solaris Zone. We'll take + * the process with PID == PPID */ + for (me = 0; me < NProc; me++) { + if (P[me].pid == P[me].ppid) return P[me].pid; + } + /* Should not happen */ + fprintf(stderr, + "%s: No process found with PID == 1 || PPID == 0 || PPID == 1\n" + " || PID == PPID, contact author.\n", + Progname); + exit(1); +} + +#ifdef ZOMBIES_HAVE_PID_0 +void FixZombies(void) { + int me, num = 0; + for (me = 0; me < NProc; me++) { + if (P[me].pid == 0) num++; + } + if (num > 1) for (me = 0; me < NProc; me++) { + if (P[me].pid == 0 && P[me].ppid != 0 && P[me].ppid != -1) { + P[me].pid = -1; +#ifdef DEBUG + if (debug) fprintf(stderr, + "fixed zombie %s with ppid %d\n", + P[me].cmd, P[me].ppid); +#endif + } + } +} +#endif + +int get_pid_index(long pid) { + int me; + for (me = NProc - 1;me >= 0 && P[me].pid != pid; me--); /* Search process */ + return me; +} + +#define EXIST(idx) ((idx) != -1) + +void MakeTree(void) { + /* Build the process hierarchy. Every process marks itself as first child + * of it's parent or as sister of first child of it's parent */ + int me; + for (me = 0; me < NProc; me++) { + int parent; + parent = get_pid_index(P[me].ppid); + if (parent != me && parent != -1) { /* valid process, not me */ + P[me].parent = parent; + if (P[parent].child == -1) /* first child */ + P[parent].child = me; + else { + int sister; + for (sister = P[parent].child; EXIST(P[sister].sister); sister = P[sister].sister); + P[sister].sister = me; + } + } + } +} + +void MarkChildren(int me) { + int child; + P[me].print = TRUE; + for (child = P[me].child; EXIST(child); child = P[child].sister) + MarkChildren(child); +} + +void MarkProcs(void) { + int me; + for (me = 0; me < NProc; me++) { + if (showall) { + P[me].print = TRUE; + } else { + int parent; + if (0 == strcmp(P[me].name, name) /* for -u */ + || (Uoption && + 0 != strcmp(P[me].name, "root")) /* for -U */ + || P[me].pid == ipid /* for -p */ + || (soption + && NULL != strstr(P[me].cmd, str) + && P[me].pid != MyPid) /* for -s */ + ) { + /* Mark parents */ + for (parent = P[me].parent; EXIST(parent); parent = P[parent].parent) { + P[parent].print = TRUE; + } + /* Mark children */ + MarkChildren(me); + } + } +#if 0 /* experimental thread compression */ + { + int parent = P[me].parent; + int ancestor; /* oldest parent with same cmd */ + if (0 == strcmp(P[me].cmd, P[parent].cmd)) { + P[me].print = FALSE; + for (parent = P[me].parent; + EXIST(parent) && (0 == strcmp(P[me].cmd, P[parent].cmd)); + parent = P[parent].parent) { + ancestor = parent; + } + fprintf(stderr, "%d: %d\n", + P[me].pid, + P[ancestor].pid); + P[ancestor].thcount++; + } + } +#endif + } +} + +void DropProcs(void) { + int me; + for (me = 0; me < NProc; me++) if (P[me].print) { + int child, sister; + /* Drop children that won't print */ + for (child = P[me].child; + EXIST(child) && !P[child].print; child = P[child].sister); + P[me].child = child; + /* Drop sisters that won't print */ + for (sister = P[me].sister; + EXIST(sister) && !P[sister].print; sister = P[sister].sister); + P[me].sister = sister; + } +} + +void PrintTree(int idx, const char *head) { + char nhead[MAXLINE], out[4 * MAXLINE], thread[16] = {'\0'}; + int child; + + if (head[0] == '\0' && !P[idx].print) return; + + if (P[idx].thcount > 1) snprintf(thread, sizeof(thread), "[%ld]", P[idx].thcount); + + if(atLdepth == maxLdepth) return; /* LOPTION */ + ++atLdepth; /* LOPTION */ + + + snprintf(out, sizeof(out), + "%s%s%s%s%s%s %05ld %s %s%s" /*" (ch=%d, si=%d, pr=%d)"*/, + C->sg, + head, + head[0] == '\0' ? "" : EXIST(P[idx].sister) ? C->barc : C->barl, + EXIST(P[idx].child) ? C->p : C->s2, + P[idx].pid == P[idx].pgid ? C->pgl : C->npgl, + C->eg, + P[idx].pid, P[idx].name, + thread, + P[idx].cmd + /*,P[idx].child,P[idx].sister,P[idx].print*/); + + out[Columns-1] = '\0'; + puts(out); + + /* Process children */ + snprintf(nhead, sizeof(nhead), "%s%s ", head, + head[0] == '\0' ? "" : EXIST(P[idx].sister) ? C->bar : " "); + + for (child = P[idx].child; EXIST(child); child = P[child].sister) + PrintTree(child, nhead); + + --atLdepth; /* LOPTION */ + +} + +void Usage(void) { + fprintf(stderr, + "%s\n" + "%s\n\n" + "Usage: %s " +#ifdef DEBUG + "[-d] " +#endif + "[-f file] [-g n] [-u user] [-U] [-s string] [-p pid] [-w] [pid ...]\n" + /*" -a align output\n"*/ +#ifdef DEBUG + " -d print debugging info to stderr\n" +#endif + " -f file read input from <file> (- is stdin) instead of running\n" + " \"%s\"\n" + " -g n use graphics chars for tree. n=1: IBM-850, n=2: VT100, n=3: UTF-8\n" + " -l n print tree to n level deep\n" + " -u user show only branches containing processes of <user>\n" + " -U don't show branches containing only root processes\n" + " -s string show only branches containing process with <string> in commandline\n" + " -p pid show only branches containing process <pid>\n" + " -w wide output, not truncated to window width\n" + " pid ... process ids to start from, default is 1 (init)\n" + " use 0 to also show kernel processes\n" + , WhatString[0] + 4, WhatString[1] + 4, Progname, PSCMD); +#ifdef HAS_PGID + fprintf(stderr, "\n%sProcess group leaders are marked with '%s%s%s'.\n", + C->init, C->sg, C->pgl, C->eg); +#endif + exit(1); +} + +int main(int argc, char **argv) { + extern int optind; + extern char *optarg; + int ch; + long pid; + int graph = G_ASCII, wide = FALSE; + + C = &TreeChars[graph]; + + Progname = strrchr(argv[0],'/'); + Progname = (NULL == Progname) ? argv[0] : Progname + 1; + + while ((ch = getopt(argc, argv, "df:g:hl:p:s:u:Uw?")) != EOF) + switch(ch) { + /*case 'a': + align = TRUE; + break;*/ +#ifdef DEBUG + case 'd': + debug = TRUE; + break; +#endif + case 'f': + input = optarg; + break; + case 'g': + graph = atoi(optarg); + if (graph < 0 || graph >= G_LAST) { + fprintf(stderr, "%s: Invalid graph parameter.\n", + Progname); + exit(1); + } + C = &TreeChars[graph]; + break; + case 'l': /* LOPTION */ + maxLdepth = atoi(optarg); /* LOPTION */ + if(maxLdepth < 1) maxLdepth = 1; /* LOPTION */ + break; /* LOPTION */ + case 'p': + showall = FALSE; + ipid = atoi(optarg); + break; + case 's': + showall = FALSE; + soption = TRUE; + str = optarg; + break; + case 'u': + showall = FALSE; + name = optarg; + if ( +#ifdef solaris2x + (int) +#endif + NULL == getpwnam(name)) { + fprintf(stderr, "%s: User '%s' does not exist.\n", + Progname, name); + exit(1); + } + break; + case 'U': + showall = FALSE; + Uoption = TRUE; + break; + case 'w': + wide = TRUE; + break; + case 'h': + case '?': + default : + Usage(); + break; + } + +#ifdef USE_GetProcessesDirect + NProc = input == NULL ? GetProcessesDirect() : GetProcesses(); +#else + NProc = GetProcesses(); +#endif + +#ifdef ZOMBIES_HAVE_PID_0 + FixZombies(); +#endif + + if (NProc == 0) { + fprintf(stderr, "%s: No processes read.\n", Progname); + exit(1); + } + +#ifdef DEBUG + if (debug) fprintf(stderr, "NProc = %d processes found.\n", NProc); +#endif + + RootPid = GetRootPid(); + +#ifdef DEBUG + if (debug) fprintf(stderr, "RootPid = %d.\n", RootPid); +#endif + +#if defined(UID2USER) && defined(DEBUG) + if (debug) uid2user(0,NULL,0); +#endif + MyPid = getpid(); + + if (wide) + Columns = MAXLINE - 1; + else { +#if defined (HAS_TERMDEF) + Columns = atoi((char*)termdef(fileno(stdout),'c')); +#elif defined(TIOCGWINSZ) + struct winsize winsize; + ioctl(fileno(stdout), TIOCGWINSZ, &winsize); + Columns = winsize.ws_col; +#elif defined(TIOCGSIZE) + struct ttysize ttysize; + ioctl(fileno(stdout), TIOCGSIZE, &ttysize); + Columns = ttysize.ts_cols; +#else + char *env = getenv("COLUMNS"); + Columns = env ? atoi(env) : 80; +#endif + } + if (Columns == 0) Columns = MAXLINE - 1; + + printf("%s", C->init); + + Columns += strlen(C->sg) + strlen(C->eg); /* Don't count hidden chars */ + + if (Columns >= MAXLINE) Columns = MAXLINE - 1; + +#ifdef DEBUG + if (debug) fprintf(stderr, "Columns = %d\n", Columns); +#endif + + MakeTree(); + MarkProcs(); + DropProcs(); + + if (argc == optind) { /* No pids */ + PrintTree(get_pid_index(RootPid), ""); + } else while (optind < argc) { + int idx; + pid = (long)atoi(argv[optind]); + idx = get_pid_index(pid); + if (idx > -1) PrintTree(idx, ""); + optind++; + } + free(P); + return 0; +} + +#ifdef NEED_STRSTR +/* Contributed by Paul Kern <pkern AT utcc.utoronto.ca> */ +static char * strstr(s1, s2) + register char *s1, *s2; +{ + register int n1, n2; + + if (n2 = strlen(s2)) + for (n1 = strlen(s1); n1 >= n2; s1++, n1--) + if (strncmp(s1, s2, n2) == 0) + return s1; + return NULL; +} +#endif /* NEED_STRSTR */ + +#ifdef NEED_SNPRINTF +int snprintf (char *s, int namesiz, char *format, ...) { + /* Original portable version by Michael E. White. + This version of Stan Sieler (sieler AT allegro.com) */ + + int chars_needed; /* not including trailing null */ + + char bigbuf [1024] = {'\0'}; /* note: 1024 is a guess, and may not be large enough! */ + + va_list ap; /* some systems allow "va_list ap = NULL;", others *do not* (like MACH) */ + + va_start (ap, format); + chars_needed = vsprintf (bigbuf, format, ap); /* note: chars_needed does not include trailing null */ + va_end (ap); + + /* 0 is documented as "don't write anything" ... while not specifically spelled out + (e.g., does it also mean "don't internally call vsprintf"?), one can imply that it simply means + "don't write to the output buffer 's'. (Otherwise, if we didn't call vsprintf, we wouldn't + know what value of chars_needed to return!) */ + + if (namesiz <= 0) + ; /* Don't touch 's' buffer at all! Note: on some systems, a negative namesiz + will cause the process to abort. By checking for <= 0, not just 0, we differ + in that area, but it's a reasonable difference. */ + + else if (chars_needed >= namesiz) + { /* oh oh, output too large for 'name' buffer... */ + memcpy (s, bigbuf, namesiz - 1); + s [namesiz - 1] = '\0'; + } + + else /* size is ok */ + { + memcpy (s, bigbuf, chars_needed); /* chars_needed < namesiz */ + s [chars_needed] = '\0'; + /* note: above two could be replaced by strcpy (s, bigbuf) + since we know strlen (bigbuf) is acceptable. + But, why copy byte at a time, comparing to null, when + we *know* the length? */ + } + + return chars_needed; /* May be larger than namesiz, but that's ok + In fact, not just 'ok', it's *useful*! */ +} +#endif /* NEED_SNPRINTF */ + +/* + * $Log: pstree.c,v $ + * Revision 2.35 2013-02-28 08:33:02+01 fred + * Added Stan Sieler's fix to my adaption of snprintf fix by Stan Sieler :-) + * + * Revision 2.34 2013-02-27 16:57:25+01 fred + * Added snprintf fix by Stan Sieler + * + * Revision 2.33 2009-11-10 22:12:39+01 fred + * Added UTF8, enlarged MAXLINE + * + * Revision 2.32 2007-10-26 21:39:50+02 fred + * Added option -l provided by Michael E. White <mewhite AT us.ibm.com> + * + * Revision 2.31 2007-06-08 17:45:23+02 fred + * Fixed problem with users with long login name (Reported by Oleg A. Mamontov) + * + * Revision 2.30 2007-05-10 23:13:04+02 fred + * *** empty log message *** + * + * Revision 2.29 2007-05-10 22:37:13+02 fred + * Added fix for Solaris Zone and bug fix from Philippe Torche + * + * Revision 2.28 2007-05-10 22:01:07+02 fred + * Added new determination of window width + * + * Revision 2.27 2005-04-08 22:08:45+02 fred + * Also accept PPID==1 if nothing else is found. Should fix problem with + * FreeBSD and security.bsd.see_other_uids=0. + * + * Revision 2.26 2004-10-15 13:59:03+02 fred + * Fixed small bug with char/int variable c + * reported by Tomas Dvorak <tomas_dvorak AT mailcan.com> + * + * Revision 2.25 2004-05-14 16:41:39+02 fred + * Added workaround for spurious blank lines in ps output under AIX 5.2 + * reported by Dean Rowswell <rowswell AT ca.ibm.com> + * + * Revision 2.24 2004-04-14 09:10:29+02 fred + * *** empty log message *** + * + * Revision 2.23 2004-02-16 10:55:20+01 fred + * Fix for zombies (pid == 0) under FreeBSD + * + * Revision 2.22 2003-12-12 10:58:46+01 fred + * Added support for TRU64 v5.1b TruCluster + * + * Revision 2.21 2003-10-06 13:55:47+02 fred + * Fixed SEGV under Linux when process table changes during run + * + * Revision 2.20 2003-07-09 20:07:29+02 fred + * cosmetic + * + * Revision 2.19 2003/05/26 15:33:35 fred + * Merged FreeBSD, (Open|Net)BSD; added Darwin (APPLE), fixed wide output + * in FreeBSD + * + * Revision 2.18 2003/03/13 18:53:22 fred + * Added getenv("COLUMNS"), cosmetic changes + * + * Revision 2.17 2001/12/17 12:18:02 fred + * Changed ps call to something like ps -eo uid,pid,ppid,pgid,args under + * AIX and Linux, workaround for AIX 5L. + * + * Revision 2.17 2001-12-13 08:27:00+08 chris + * Added workaround for AIX Version >= 5 + * + * Revision 2.16 2000-03-01 10:42:22+01 fred + * Added support for thread count (thcount) in other OSs than AIX + * + * Revision 2.15 2000-03-01 10:18:56+01 fred + * Added process group support for {Net|Open}BSD following a suggestion + * by Ralf Meyer <ralf AT thp.Uni-Duisburg.de> + * + * Revision 2.14 1999-03-22 20:45:02+01 fred + * Fixed bug when line longer than MAXLINE, set MAXLINE=512 + * + * Revision 2.13 1998-12-17 19:31:53+01 fred + * Fixed problem with option -f when input file is empty + * + * Revision 2.12 1998-12-07 17:08:59+01 fred + * Added -f option and sun 68000 support by Paul Kern + * <pkern AT utcc.utoronto.ca> + * + * Revision 2.11 1998-05-23 13:30:28+02 fred + * Added vt100 sequences, NetBSD support + * + * Revision 2.10 1998-02-02 15:04:57+01 fred + * Fixed bug in MakeTree()/get_pid_index() when parent doesn't + * exist. Thanks to Igor Schein <igor AT andrew.air-boston.com> for the bug + * report. + * + * Revision 2.9 1998-01-07 16:55:26+01 fred + * Added support for getprocs() + * + * Revision 2.9 1998-01-06 17:13:19+01 fred + * Added support for getprocs() under AIX + * + * Revision 2.8 1997-10-22 15:09:39+02 fred + * Cosmetic + * + * Revision 2.7 1997-10-22 15:01:40+02 fred + * Minor changes in getprocs for AIX + * + * Revision 2.6 1997/10/16 16:35:19 fred + * Added uid2name() caching username lookup, added patch for Solaris 2.x + * + * Revision 2.5 1997-02-05 14:24:53+01 fred + * return PrintTree when nothing to do. + * + * Revision 2.4 1997/02/05 09:54:08 fred + * Fixed bug when P[i].cmd is empty + * + * Revision 2.3 1997-02-04 18:40:54+01 fred + * Cosmetic + * + * Revision 2.2 1997-02-04 14:11:17+01 fred + * *** empty log message *** + * + * Revision 2.1 1997-02-04 13:55:14+01 fred + * Rewritten + * + * Revision 1.13 1997-02-04 09:01:59+01 fred + * Start of rewrite + * + * Revision 1.12 1996-09-17 21:54:05+02 fred + * *** empty log message *** + * + * Revision 1.11 1996-09-17 21:52:52+02 fred + * revision added + * + * Revision 1.10 1996-09-17 21:45:35+02 fred + * replace \n and \t with ? in output + * + * Revision 1.4 1996-09-17 21:43:14+02 fred + * Moved under RCS, replace \n and \t with ? + */ |