1 /*	This is pstree written by Fred Hucht (c) 1993-2015	*
2  *	EMail: fred AT thp.uni-due.de				*
3  *	Feel free to copy and redistribute in terms of the	*
4  * 	GNU public license. 					*
5  *
6  * $Id: pstree.c,v 2.39 2015/05/13 12:24:47 fred Exp $
7  */
8 static char *WhatString[]= {
9   "@(#)pstree $Revision: 2.39 $ by Fred Hucht (C) 1993-2015",
10   "@(#)EMail: fred AT thp.uni-due.de",
11   "$Id: pstree.c,v 2.39 2015/05/13 12:24:47 fred Exp $"
12 };
13 
14 #define MAXLINE 8192
15 
16 #if defined(_AIX) || defined(___AIX)	/* AIX >= 3.1 */
17 /* Under AIX, we directly read the process table from the kernel */
18 # ifndef _AIX50
19 /* problems with getprocs() under AIX 5L
20  * workaround contributed by Chris Benesch <chris AT fdbs.com> */
21 #  define USE_GetProcessesDirect
22 # endif /*_AIX50*/
23 #  define HAS_TERMDEF
24 extern char *termdef(int, char);
25 #  define _ALL_SOURCE
26 #  include <procinfo.h>
27 #  define USE_GETPROCS
28 
29 #  ifdef USE_GETPROCS
30 #    define IFNEW(a,b) a
31 #    define ProcInfo procsinfo
32 #    ifndef _AIX61
33 /* workaround contributed by Michael Staats <michael.staats AT gmx.de> */
34 extern getprocs(struct procsinfo *, int, struct fdsinfo *, int, pid_t *, int);
35 #    endif /*_AIX61*/
36 #  else /*USE_GETPROCS*/
37 #    define IFNEW(a,b) b
38 #    define ProcInfo procinfo
39 extern getproc(struct procinfo *, int, int);
40 extern getuser(struct procinfo *, int, void *, int);
41 #  endif /*USE_GETPROCS*/
42 
43 #  ifndef _AIX61
44 /* workaround contributed by Michael Staats <michael.staats AT gmx.de> */
45 extern getargs(struct ProcInfo *, int, char *, int);
46 #  endif /*_AIX61*/
47 
48 /*#  define PSCMD 	"ps -ekf"
49   #  define PSFORMAT 	"%s %ld %ld %*20c %*s %[^\n]"*/
50 #  define HAS_PGID
51 #  define UID2USER
52 #  define PSCMD 	"ps -eko uid,pid,ppid,pgid,thcount,args"
53 #  define PSFORMAT 	"%ld %ld %ld %ld %ld %[^\n]"
54 #  define PSVARS	&P[i].uid, &P[i].pid, &P[i].ppid, &P[i].pgid, &P[i].thcount, P[i].cmd
55 #  define PSVARSN	6
56 /************************************************************************/
57 #elif defined(__linux) || (defined __alpha && defined(_SYSTYPE_BSD) || defined (Tru64))
58 /* TRU64 contributed by Frank Parkin <fparki AT acxiom.co.uk>
59  */
60 #  ifdef __linux
61 #    define USE_GetProcessesDirect
62 #    include <glob.h>
63 #    include <sys/stat.h>
64 #  endif
65 #  define UID2USER
66 #  define HAS_PGID
67 #  define PSCMD 	"ps -eo uid,pid,ppid,pgid,args"
68 #  define PSFORMAT 	"%ld %ld %ld %ld %[^\n]"
69 #  define PSVARS	&P[i].uid, &P[i].pid, &P[i].ppid, &P[i].pgid, P[i].cmd
70 #  define PSVARSN	5
71 /************************************************************************/
72 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__DragonFly__)
73 /* NetBSD contributed by Gary D. Duzan <gary AT wheel.tiac.net>
74  * FreeBSD contributed by Randall Hopper <rhh AT ct.picker.com>
75  * Darwin / Mac OS X patch by Yuji Yamano <yyamano AT kt.rim.or.jp>
76  * wide output format fix for NetBSD by Jeff Brown <jabrown AT caida.org>
77  * (Net|Open|Free)BSD & Darwin merged by Ralf Meyer <ralf AT thp.Uni-Duisburg.DE>
78  * DragonFlyBSD contributed by Krzysztof Piecuch <piecuch AT kpiecuch.pl>
79  */
80 #  define HAS_PGID
81 #  define PSCMD 	"ps -axwwo user,pid,ppid,pgid,command"
82 #  define PSFORMAT 	"%s %ld %ld %ld %[^\n]"
83 #  define PSVARS	P[i].name, &P[i].pid, &P[i].ppid, &P[i].pgid, P[i].cmd
84 #  define PSVARSN	5
85 #  ifdef __DragonFly__
86 #    define PARENT_CYCLE_PID_MINUS_ONE
87 #  else
88 #    define ZOMBIES_HAVE_PID_0
89 #  endif
90 /************************************************************************/
91 #elif defined(sun) && (!defined(__SVR4)) /* Solaris 1.x */
92 /* contributed by L. Mark Larsen <mlarsen AT ptdcs2.intel.com> */
93 /* new cpp criteria by Pierre Belanger <belanger AT risq.qc.ca> */
94 #  define solaris1x
95 #  define UID2USER
96 #  ifdef mc68000
97 /* contributed by Paul Kern <pkern AT utcc.utoronto.ca> */
98 #    define PSCMD 	"ps laxw"
99 #    define PSFORMAT 	"%*7c%ld %ld %ld %*d %*d %*d %*x %*d %*d %*x %*14c %[^\n]"
100 #    define uid_t	int
101 #    define NEED_STRSTR
102 #  else
103 #    define PSCMD 	"ps jaxw"
104 #    define PSFORMAT 	"%ld %ld %*d %*d %*s %*d %*s %ld %*s %[^\n]"
105 #    define PSVARS 	&P[i].ppid, &P[i].pid, &P[i].uid, P[i].cmd
106 #    define PSVARSN	4
107 #  endif
108 /************************************************************************/
109 #elif defined(sun) && (defined(__SVR4)) /* Solaris 2.x */
110 /* contributed by Pierre Belanger <belanger AT risq.qc.ca> */
111 #  define solaris2x
112 #  define PSCMD         "ps -ef"
113 #  define PSFORMAT      "%s %ld %ld %*d %*s %*s %*s %[^\n]"
114 /************************************************************************/
115 #elif defined(bsdi)
116 /* contributed by Dean Gaudet <dgaudet AT hotwired.com> */
117 #  define UID2USER
118 #  define PSCMD 	"ps laxw"
119 #  define PSFORMAT 	"%ld %ld %ld %*d %*d %*d %*d %*d %*s %*s %*s %*s %[^\n]"
120 /************************************************************************/
121 #elif defined(_BSD)	/* Untested */
122 #  define UID2USER
123 #  define PSCMD 	"ps laxw"
124 #  define PSFORMAT 	"%*d %*c %ld %ld %ld %*d %*d %*d %*x %*d %d %*15c %*s %[^\n]"
125 /************************************************************************/
126 #elif defined(__convex)	/* ConvexOS */
127 #  define UID2USER
128 #  define PSCMD 	"ps laxw"
129 #  define PSFORMAT 	"%*s %ld %ld %ld %*d %*g %*d %*d %*21c %*s %[^\n]"
130 /************************************************************************/
131 #else			/* HP-UX, A/UX etc. */
132 #  define PSCMD 	"ps -ef"
133 #  define PSFORMAT 	"%s %ld %ld %*20c %*s %[^\n]"
134 #endif
135 /*********************** end of configurable part ***********************/
136 
137 #ifndef PSVARS 		/* Set default */
138 # ifdef UID2USER
139 #  define PSVARS	&P[i].uid, &P[i].pid, &P[i].ppid, P[i].cmd
140 # else
141 #  define PSVARS	P[i].name, &P[i].pid, &P[i].ppid, P[i].cmd
142 # endif
143 # define PSVARSN	4
144 #endif
145 
146 #include <stdio.h>
147 #include <stdlib.h>
148 #include <string.h>		/* For str...() */
149 #ifdef NEED_SNPRINTF
150 #include <stdarg.h>
151 int snprintf(char *, int, char *, ...);
152 #endif
153 #include <unistd.h>		/* For getopt() */
154 #include <pwd.h>		/* For getpwnam() */
155 
156 #include <sys/ioctl.h>		/* For TIOCGSIZE/TIOCGWINSZ */
157 /* #include <termios.h> */
158 
159 #ifdef DEBUG
160 # include <errno.h>
161 #endif
162 
163 #ifdef NEED_STRSTR
164 static char *strstr(char *, char *);
165 #endif
166 
167 #ifndef TRUE
168 #define TRUE  1
169 #define FALSE 0
170 #endif
171 
172 struct TreeChars {
173   char *s2, 		/* SS String between header and pid */
174     *p, 		/* PP dito, when parent of printed childs */
175     *pgl,		/* G  Process group leader */
176     *npgl,		/* N  No process group leader */
177     *barc, 		/* C  bar for line with child */
178     *bar, 		/* B  bar for line without child */
179     *barl,		/* L  bar for last child */
180     *sg,		/*    Start graphics (alt char set) */
181     *eg,		/*    End graphics (alt char set) */
182     *init;		/*    Init string sent at the beginning */
183 };
184 
185 /* Example:
186  * |-+- 01111 ...        CPPN 01111 ...
187  * | \-+=   01112 ...    B LPPG 01112 ...
188  * |   |--= 01113 ...    B   CSSG 01113 ...
189  * |   \--= 01114 ...    B   LSSG 01114 ...
190  * \--- 01115 ...        LSSN 01115 ...
191  */
192 
193 enum { G_ASCII = 0, G_PC850 = 1, G_VT100 = 2, G_UTF8 = 3, G_LAST };
194 
195 /* VT sequences contributed by Randall Hopper <rhh AT ct.picker.com> */
196 /* UTF8 sequences contributed by Mark-Andre Hopf <mhopf AT mark13.org> */
197 static struct TreeChars TreeChars[] = {
198   /* SS          PP          G       N       C       B       L      sg      eg      init */
199   { "--",       "-+",       "=",    "-",    "|",    "|",    "\\",   "",     "",     ""             }, /*Ascii*/
200   { "\304\304", "\304\302", "\372", "\304", "\303", "\263", "\300", "",     "",     ""             }, /*Pc850*/
201   { "qq",       "qw",       "`",    "q",    "t",    "x",    "m",    "\016", "\017", "\033(B\033)0" }, /*Vt100*/
202   { "\342\224\200\342\224\200",
203     /**/        "\342\224\200\342\224\254",
204     /**/                    "=",
205     /**/                            "\342\224\200",
206     /**/                                    "\342\224\234",
207     /**/                                            "\342\224\202",
208     /**/                                                    "\342\224\224",
209     /**/                                                            "",     "",     ""             }  /*UTF8*/
210 }, *C;
211 
212 static int MyPid, NProc, Columns, RootPid;
213 static short showall = TRUE, soption = FALSE, Uoption = FALSE;
214 static char *name = "", *str = NULL, *Progname;
215 static long ipid = -1;
216 static char *input = NULL;
217 
218 static int atLdepth=0;    /* LOPTION - track how deep in the print chain we are */
219 static int maxLdepth=100; /* LOPTION - will be changed by -l n option */
220 static int compress = FALSE;
221 
222 #ifdef DEBUG
223 static int debug = TRUE;
224 #endif
225 
226 struct Proc {
227   long uid, pid, ppid, pgid;
228   char name[32], cmd[MAXLINE];
229   int  print;
230   long parent, child, sister;
231   unsigned long thcount;
232 } *P;
233 
234 #ifdef UID2USER
uid2user(uid_t uid,char * name,int len)235 static void uid2user(uid_t uid, char *name, int len) {
236 #define NUMUN 128
237   static struct un_ {
238     uid_t uid;
239     char name[32];
240   } un[NUMUN];
241   static short n = 0;
242   short i;
243   char uid_name[32];
244   char *found;
245 #ifdef DEBUG
246   if (name == NULL) {
247     for (i = 0; i < n; i++)
248       fprintf(stderr, "uid = %3d, name = %s\n", un[i].uid, un[i].name);
249     return;
250   }
251 #endif
252   for (i = n - 1; i >= 0 && un[i].uid != uid; i--);
253   if (i >= 0) { /* found locally */
254     found = un[i].name;
255   } else {
256     struct passwd *pw = getpwuid(uid);
257     if (pw) {
258       found = pw->pw_name;
259     } else {
260       /* fix by Stan Sieler & Philippe Torche */
261       snprintf(uid_name, sizeof(uid_name), "#%d", uid);
262       found = uid_name;
263     }
264     if (n < NUMUN) {
265       un[n].uid = uid;
266       strncpy(un[n].name, found, 9);
267       un[n].name[8] = '\0';
268       n++;
269     }
270   }
271   strncpy(name, found, len);
272   name[len-1] = '\0';
273 }
274 #endif
275 
276 #if defined(_AIX) || defined(___AIX)	/* AIX 3.x / 4.x */
GetProcessesDirect(void)277 static int GetProcessesDirect(void) {
278   int i, nproc, maxnproc = 1024;
279 
280   struct ProcInfo *proc;
281   int idx;
282 #ifndef USE_GETPROCS
283   struct userinfo user;
284 #endif
285 
286   do {
287     proc = malloc(maxnproc * sizeof(struct ProcInfo));
288     if (proc == NULL) {
289       fprintf(stderr, "Problems with malloc.\n");
290       exit(1);
291     }
292 
293     /* Get process table */
294     idx = 0;
295     nproc = IFNEW(getprocs(proc, sizeof(struct procsinfo), NULL, 0,
296 			   &idx, maxnproc),
297 		  getproc(proc, maxnproc, sizeof(struct procinfo))
298 		  );
299 #ifdef DEBUG
300     idx = errno; /* Don't ask... */
301     if (debug)
302       fprintf(stderr,
303 	      "nproc = %d maxnproc = %d" IFNEW(" idx = %d ","") "\n",
304 	      nproc, maxnproc, idx);
305     errno = idx;
306 #endif
307 #ifdef USE_GETPROCS
308     if (nproc == -1) {
309       perror("getprocs");
310       exit(1);
311     } else if (nproc == maxnproc) {
312       nproc = -1;
313     }
314 #endif
315     if (nproc == -1) {
316       free(proc);
317       maxnproc *= 2;
318     }
319   } while (nproc == -1);
320 
321   P = malloc((nproc+1) * sizeof(struct Proc));
322   if (P == NULL) {
323     fprintf(stderr, "Problems with malloc.\n");
324     exit(1);
325   }
326 
327   for (i = 0; i < nproc; i++) {
328 #ifndef USE_GETPROCS
329     getuser(&proc[i],sizeof(struct procinfo),
330 	    &user,   sizeof(struct userinfo));
331 #endif
332     P[i].uid     = proc[i].pi_uid;
333     P[i].pid     = proc[i].pi_pid;
334     P[i].ppid    = proc[i].pi_ppid;
335     P[i].pgid    = proc[i].pi_pgrp;
336     P[i].thcount = IFNEW(proc[i].pi_thcount, 1);
337 
338     uid2user(P[i].uid, P[i].name, sizeof(P[i].name));
339 
340     if (IFNEW(proc[i].pi_state,proc[i].pi_stat) == SZOMB) {
341       strcpy(P[i].cmd, "<defunct>");
342     } else {
343       char *c = P[i].cmd;
344       int ci = 0;
345       getargs(&proc[i], sizeof(struct procinfo), c, MAXLINE - 2);
346       c[MAXLINE-2] = c[MAXLINE-1] = '\0';
347 
348       /* Collect args. Stop when we encounter two '\0' */
349       while (c[ci] != '\0' && (ci += strlen(&c[ci])) < MAXLINE - 2)
350 	c[ci++] = ' ';
351 
352       /* Drop trailing blanks */
353       ci = strlen(c);
354       while (ci > 0 && c[ci-1] == ' ') ci--;
355       c[ci] = '\0';
356 
357       /* Replace some unprintables with '?' */
358       for (ci = 0; c[ci] != '\0'; ci++)
359 	if (c[ci] == '\n' || c[ci] == '\t') c[ci] = '?';
360 
361       /* Insert [ui_comm] when getargs returns nothing */
362       if (c[0] == '\0') {
363 	int l = strlen(IFNEW(proc[i].pi_comm,user.ui_comm));
364 	c[0] = '[';
365 	strcpy(c+1, IFNEW(proc[i].pi_comm,user.ui_comm));
366 	c[l+1] = ']';
367 	c[l+2] = '\0';
368       }
369     }
370 #ifdef DEBUG
371     if (debug)
372       fprintf(stderr,
373 	      "%d: uid=%5ld, name=%8s, pid=%5ld, ppid=%5ld, pgid=%5ld, tsize=%7u, dvm=%4u, "
374 	      "thcount=%2d, cmd[%d]='%s'\n",
375 	      i, P[i].uid, P[i].name, P[i].pid, P[i].ppid, P[i].pgid,
376 	      IFNEW(proc[i].pi_tsize,user.ui_tsize),
377 	      IFNEW(proc[i].pi_dvm,user.ui_dvm),
378 	      proc[i].pi_thcount,
379 	      strlen(P[i].cmd),P[i].cmd);
380 #endif
381     P[i].parent = P[i].child = P[i].sister = -1;
382     P[i].print = FALSE;
383   }
384   free(proc);
385   return nproc;
386 }
387 
388 #endif /* _AIX */
389 
390 #ifdef __linux
GetProcessesDirect(void)391 static int GetProcessesDirect(void) {
392   glob_t globbuf;
393   unsigned int i, j;
394 
395   glob("/proc/[0-9]*", GLOB_NOSORT, NULL, &globbuf);
396 
397   P = calloc(globbuf.gl_pathc, sizeof(struct Proc));
398   if (P == NULL) {
399     fprintf(stderr, "Problems with malloc.\n");
400     exit(1);
401   }
402 
403   for (i = j = 0; i < globbuf.gl_pathc; i++) {
404     char *pdir, name[32];
405     int c;
406     FILE *tn;
407     int k = 0;
408 
409     pdir = globbuf.gl_pathv[globbuf.gl_pathc - i - 1];
410 
411     /* if processes change their UID this change is only reflected in the owner of pdir.
412      * fixed since version 2.36 */
413     {
414       struct stat st;
415       if (stat(pdir, &st) != 0) { /* get uid */
416 	continue; /* process vanished since glob() */
417       }
418       P[j].uid = st.st_uid;
419       uid2user(P[j].uid, P[j].name, sizeof(P[j].name));
420     }
421 
422     snprintf(name, sizeof(name), "%s%s",
423 	     globbuf.gl_pathv[globbuf.gl_pathc - i - 1], "/stat");
424     tn = fopen(name, "r");
425     if (tn == NULL) continue; /* process vanished since glob() */
426     fscanf(tn, "%ld %s %*c %ld %ld",
427 	   &P[j].pid, P[j].cmd, &P[j].ppid, &P[j].pgid);
428     fclose(tn);
429     P[j].thcount = 1;
430 
431     snprintf(name, sizeof(name), "%s%s",
432 	     globbuf.gl_pathv[globbuf.gl_pathc - i - 1], "/cmdline");
433     tn = fopen(name, "r");
434     if (tn == NULL) continue; /* process vanished since glob() */
435     while (k < MAXLINE - 1 && EOF != (c = fgetc(tn))) {
436       P[j].cmd[k++] = c == '\0' ? ' ' : c;
437     }
438     if (k > 0) P[j].cmd[k] = '\0';
439     fclose(tn);
440 
441 #ifdef DEBUG
442     if (debug) fprintf(stderr,
443 		       "uid=%5ld, name=%8s, pid=%5ld, ppid=%5ld, pgid=%5ld, thcount=%ld, cmd='%s'\n",
444 		       P[j].uid, P[j].name, P[j].pid, P[j].ppid, P[j].pgid, P[j].thcount, P[j].cmd);
445 #endif
446     P[j].parent = P[j].child = P[j].sister = -1;
447     P[j].print  = FALSE;
448     j++;
449   }
450   globfree(&globbuf);
451   return j;
452 }
453 #endif /* __linux */
454 
GetProcesses(void)455 static int GetProcesses(void) {
456   FILE *tn;
457   int i = 0;
458   char line[MAXLINE], command[] = PSCMD;
459 
460   /* file read code contributed by Paul Kern <pkern AT utcc.utoronto.ca> */
461   if (input != NULL) {
462     if (strcmp(input, "-") == 0)
463       tn = stdin;
464     else if (NULL == (tn = fopen(input,"r"))) {
465       perror(input);
466       exit(1);
467     }
468   } else {
469 #ifdef DEBUG
470     if (debug) fprintf(stderr, "calling '%s'\n", command);
471 #endif
472     if (NULL == (tn = (FILE*)popen(command,"r"))) {
473       perror("Problems with pipe");
474       exit(1);
475     }
476   }
477 #ifdef DEBUG
478   if (debug) fprintf(stderr, "popen:errno = %d\n", errno);
479 #endif
480 
481   if (NULL == fgets(line, MAXLINE, tn)) { /* Throw away header line */
482     fprintf(stderr, "No input.\n");
483     exit(1);
484   }
485 
486 #ifdef DEBUG
487   if (debug) fputs(line, stderr);
488 #endif
489 
490   P = malloc(sizeof(struct Proc));
491   if (P == NULL) {
492     fprintf(stderr, "Problems with malloc.\n");
493     exit(1);
494   }
495 
496   while (NULL != fgets(line, MAXLINE, tn)) {
497     int len, num;
498     len = strlen(line);
499 #ifdef DEBUG
500     if (debug) {
501       fprintf(stderr, "len=%3d ", len);
502       fputs(line, stderr);
503     }
504 #endif
505 
506     if (len == MAXLINE - 1) { /* line too long, drop remaining stuff */
507       char tmp[MAXLINE];
508       while (MAXLINE - 1 == strlen(fgets(tmp, MAXLINE, tn)));
509     }
510 
511     P = realloc(P, (i+1) * sizeof(struct Proc));
512     if (P == NULL) {
513       fprintf(stderr, "Problems with realloc.\n");
514       exit(1);
515     }
516 
517     memset(&P[i], 0, sizeof(*P));
518 
519 #ifdef solaris1x
520     { /* SunOS allows columns to run together.  With the -j option, the CPU
521        * time used can run into the numeric user id, so make sure there is
522        * space between these two columns.  Also, the order of the desired
523        * items is different. (L. Mark Larsen <mlarsen AT ptdcs2.intel.com>)
524        */
525       char buf1[45], buf2[MAXLINE];
526       buf1[44] = '\0';
527       sscanf(line, "%44c%[^\n]", buf1, buf2);
528       snprintf(line, sizeof(line), "%s %s", buf1, buf2);
529     }
530 #endif
531 
532     num = sscanf(line, PSFORMAT, PSVARS);
533 
534     if (num != PSVARSN) {
535 #ifdef DEBUG
536       if (debug) fprintf(stderr, "dropped line, num=%d != %d\n", num, PSVARSN);
537 #endif
538       continue;
539     }
540 
541 #ifdef UID2USER	/* get username */
542     uid2user(P[i].uid, P[i].name, sizeof(P[i].name));
543 #endif
544 
545 #ifdef DEBUG
546     if (debug) fprintf(stderr,
547 		      "uid=%5ld, name=%8s, pid=%5ld, ppid=%5ld, pgid=%5ld, thcount=%ld, cmd='%s'\n",
548 		      P[i].uid, P[i].name, P[i].pid, P[i].ppid, P[i].pgid, P[i].thcount, P[i].cmd);
549 #endif
550     P[i].parent = P[i].child = P[i].sister = -1;
551     P[i].print  = FALSE;
552     i++;
553   }
554   if (input != NULL)
555     fclose(tn);
556   else
557     pclose(tn);
558   return i;
559 }
560 
GetRootPid(void)561 static int GetRootPid(void) {
562   int me;
563   for (me = 0; me < NProc; me++) {
564     if (P[me].pid == 1) return P[me].pid;
565   }
566   /* PID == 1 not found, so we'll take process with PPID == 0
567    * Fix for TRU64 TruCluster with uniq PIDs
568    * reported by Frank Parkin <fparki AT acxiom.co.uk>
569    * re-reported by Eric van Doorn <Eric.van.Doorn AT isc.politie.nl>,
570    * because fix was not published by me :-/ */
571   for (me = 0; me < NProc; me++) {
572     if (P[me].ppid == 0) return P[me].pid;
573   }
574   /* OK, still nothing found. Maybe it is FreeBSD and won't show foreign
575    * processes. So we also accept PPID == 1 */
576   for (me = 0; me < NProc; me++) {
577     if (P[me].ppid == 1) return P[me].pid;
578   }
579   /* Still nothing. Maybe it is something like Solaris Zone. We'll take
580    * the process with PID == PPID */
581   for (me = 0; me < NProc; me++) {
582     if (P[me].pid == P[me].ppid) return P[me].pid;
583   }
584   /* Should not happen */
585   fprintf(stderr,
586 	  "%s: No process found with PID == 1 || PPID == 0 || PPID == 1\n"
587 	  "          || PID == PPID, contact author.\n",
588 	  Progname);
589   exit(1);
590 }
591 
592 #ifdef ZOMBIES_HAVE_PID_0
FixZombies(void)593 void FixZombies(void) {
594   int me, num = 0;
595   for (me = 0; me < NProc; me++) {
596     if (P[me].pid == 0) num++;
597   }
598   if (num > 1) for (me = 0; me < NProc; me++) {
599     if (P[me].pid == 0 && P[me].ppid != 0 && P[me].ppid != -1) {
600       P[me].pid = -1;
601 #ifdef DEBUG
602       if (debug) fprintf(stderr,
603 			 "fixed zombie %s with ppid %ld\n",
604 			 P[me].cmd, (long)P[me].ppid);
605 #endif
606     }
607   }
608 }
609 #endif
610 
611 #ifdef PARENT_CYCLE_PID_MINUS_ONE
FixParentCycle(void)612 void FixParentCycle(void) {
613   int me, num = 0;
614   for (me = 0; me < NProc; me++) {
615     if (P[me].pid == -1) num++;
616   }
617   if (num > 1) for (me = 0; me < NProc; me++) {
618     if (P[me].pid == -1) {
619       P[me].pid -= num;
620       --num;
621 #ifdef DEBUG
622       if (debug) fprintf(stderr,
623 			 "changed process %s pid from -1 to %ld\n",
624 			 P[me].cmd, (long)P[me].pid);
625 #endif
626     }
627   }
628 }
629 #endif
get_pid_index(long pid)630 int get_pid_index(long pid) {
631   int me;
632   for (me = NProc - 1;me >= 0 && P[me].pid != pid; me--); /* Search process */
633   return me;
634 }
635 
636 #define EXIST(idx) ((idx) != -1)
637 
MakeTree(void)638 static void MakeTree(void) {
639   /* Build the process hierarchy. Every process marks itself as first child
640    * of it's parent or as sister of first child of it's parent */
641   int me;
642   for (me = 0; me < NProc; me++) {
643     int parent;
644     parent = get_pid_index(P[me].ppid);
645     if (parent != me && parent != -1) { /* valid process, not me */
646       P[me].parent = parent;
647       if (P[parent].child == -1) /* first child */
648 	P[parent].child = me;
649       else {
650 	int sister;
651 	for (sister = P[parent].child; EXIST(P[sister].sister); sister = P[sister].sister);
652 	P[sister].sister = me;
653       }
654     }
655   }
656 }
657 
MarkChildren(int me)658 static void MarkChildren(int me) {
659   int child;
660   P[me].print = TRUE;
661   for (child = P[me].child; EXIST(child); child = P[child].sister)
662     MarkChildren(child);
663 }
664 
MarkProcs(void)665 static void MarkProcs(void) {
666   int me;
667   for (me = 0; me < NProc; me++) {
668     if (showall) {
669       P[me].print = TRUE;
670     } else {
671       int parent;
672       if (0 == strcmp(P[me].name, name)		/* for -u */
673 	 || (Uoption &&
674 	     0 != strcmp(P[me].name, "root"))	/* for -U */
675 	 || P[me].pid == ipid			/* for -p */
676 	 || (soption
677 	     && NULL != strstr(P[me].cmd, str)
678 	     && P[me].pid != MyPid)		/* for -s */
679 	 ) {
680 	/* Mark parents */
681 	for (parent = P[me].parent; EXIST(parent); parent = P[parent].parent) {
682 	  P[parent].print = TRUE;
683 	}
684 	/* Mark children */
685 	MarkChildren(me);
686       }
687     }
688 #if 0 /* experimental thread compression */
689     {
690       int parent = P[me].parent;
691       int ancestor; /* oldest parent with same cmd */
692       if (0 == strcmp(P[me].cmd, P[parent].cmd)) {
693 	P[me].print = FALSE;
694 	for (parent = P[me].parent;
695 	     EXIST(parent) && (0 == strcmp(P[me].cmd, P[parent].cmd));
696 	     parent = P[parent].parent) {
697 	  ancestor = parent;
698 	}
699 	fprintf(stderr, "%d: %d\n",
700 		P[me].pid,
701 		P[ancestor].pid);
702 	P[ancestor].thcount++;
703       }
704     }
705 #endif
706   }
707 }
708 
DropProcs(void)709 static void DropProcs(void) {
710   int me;
711   for (me = 0; me < NProc; me++) if (P[me].print) {
712     int child, sister;
713     /* Drop children that won't print */
714     for (child = P[me].child;
715 	 EXIST(child) && !P[child].print; child = P[child].sister);
716     P[me].child = child;
717     /* Drop sisters that won't print */
718     for (sister = P[me].sister;
719 	 EXIST(sister) && !P[sister].print; sister = P[sister].sister);
720     P[me].sister = sister;
721   }
722 }
723 
PrintTree(int idx,const char * head)724 static void PrintTree(int idx, const char *head) {
725   char nhead[MAXLINE], out[4 * MAXLINE], thread[16] = {'\0'};
726   int child;
727 
728   if (head[0] == '\0' && !P[idx].print) return;
729   /*if (!P[idx].print) return;*/
730 
731   if (P[idx].thcount > 1) snprintf(thread, sizeof(thread), "[%ld]", P[idx].thcount);
732 
733   if(atLdepth == maxLdepth) return;    /* LOPTION */
734   ++atLdepth;                          /* LOPTION */
735 
736 
737   snprintf(out, sizeof(out),
738 	   "%s%s%s%s%s%s %05ld %s %s%s" /*" (ch=%d, si=%d, pr=%d)"*/,
739 	   C->sg,
740 	   head,
741 	   head[0] == '\0' ? "" : EXIST(P[idx].sister) ? C->barc : C->barl,
742 	   EXIST(P[idx].child)       ? C->p   : C->s2,
743 	   P[idx].pid == P[idx].pgid ? C->pgl : C->npgl,
744 	   C->eg,
745 	   P[idx].pid, P[idx].name,
746 	   thread,
747 	   P[idx].cmd
748 	   /*,P[idx].child,P[idx].sister,P[idx].print*/);
749 
750   out[Columns-1] = '\0';
751   puts(out);
752 
753   /* Process children */
754   snprintf(nhead, sizeof(nhead), "%s%s ", head,
755 	   head[0] == '\0' ? "" : EXIST(P[idx].sister) ? C->bar : " ");
756 
757   /*
758     if ( compress ) {
759     int c1, c2, flag = 0;
760     for ( c1 = P[idx].child; EXIST(c1); c1 = P[c1].sister ) {
761       for ( c2 = P[c1].sister; EXIST(c2); c2 = P[c2].sister ) {
762 	if ( 0 == strcmp(P[c1].cmd, P[c2].cmd) ) {
763 	  flag = 1;
764 	  printf("%d:%d ", c1, c2);
765 	  P[c1].pid = -1;
766 	  P[c2].print = FALSE;
767 	}
768       }
769     }
770     if ( flag ) printf("\n");
771   }
772   */
773 
774   for (child = P[idx].child; EXIST(child); child = P[child].sister) {
775     PrintTree(child, nhead);
776   }
777 
778   --atLdepth;                          /* LOPTION */
779 
780 }
781 
Usage(void)782 static void Usage(void) {
783   fprintf(stderr,
784 	  "%s\n"
785 	  "%s\n\n"
786 	  "Usage: %s "
787 #ifdef DEBUG
788 	  "[-d] "
789 #endif
790 	  "[-f file] [-g n] [-l n] [-u user] [-U] [-s string] [-p pid] [-w] [pid ...]\n"
791 	  /*"   -a        align output\n"*/
792 #ifdef DEBUG
793 	  "   -d        print debugging info to stderr\n"
794 #endif
795 	  "   -f file   read input from <file> (- is stdin) instead of running\n"
796 	  "             \"%s\"\n"
797 	  "   -g n      use graphics chars for tree. n=1: IBM-850, n=2: VT100, n=3: UTF-8\n"
798 	  "   -l n      print tree to n level deep\n"
799 	  "   -u user   show only branches containing processes of <user>\n"
800 	  "   -U        don't show branches containing only root processes\n"
801           "   -s string show only branches containing process with <string> in commandline\n"
802           "   -p pid    show only branches containing process <pid>\n"
803 	  "   -w        wide output, not truncated to window width\n"
804 	  "   pid ...   process ids to start from, default is 1 (probably init)\n"
805 	  , WhatString[0] + 4, WhatString[1] + 4, Progname, PSCMD);
806 #ifdef HAS_PGID
807   fprintf(stderr, "\n%sProcess group leaders are marked with '%s%s%s'.\n",
808 	  C->init, C->sg, C->pgl, C->eg);
809 #endif
810   exit(1);
811 }
812 
main(int argc,char ** argv)813 int main(int argc, char **argv) {
814   extern int optind;
815   extern char *optarg;
816   int ch;
817   long pid;
818   int graph = G_ASCII, wide = FALSE;
819 
820   C = &TreeChars[graph];
821 
822   Progname = strrchr(argv[0],'/');
823   Progname = (NULL == Progname) ? argv[0] : Progname + 1;
824 
825   while ((ch = getopt(argc, argv, "cdf:g:hl:p:s:u:Uw?")) != EOF)
826     switch(ch) {
827       /*case 'a':
828 	align   = TRUE;
829 	break;*/
830     case 'c':
831       compress = TRUE;
832       break;
833 #ifdef DEBUG
834     case 'd':
835       debug   = TRUE;
836       break;
837 #endif
838     case 'f':
839       input   = optarg;
840       break;
841     case 'g':
842       graph   = atoi(optarg);
843       if (graph < 0 || graph >= G_LAST) {
844 	fprintf(stderr, "%s: Invalid graph parameter.\n",
845 		Progname);
846 	exit(1);
847       }
848       C = &TreeChars[graph];
849       break;
850     case 'l':                                 /* LOPTION */
851       maxLdepth = atoi(optarg);               /* LOPTION */
852       if(maxLdepth < 1) maxLdepth = 1;        /* LOPTION */
853       break;                                  /* LOPTION */
854     case 'p':
855       showall = FALSE;
856       ipid    = atoi(optarg);
857       break;
858     case 's':
859       showall = FALSE;
860       soption = TRUE;
861       str     = optarg;
862       break;
863     case 'u':
864       showall = FALSE;
865       name    = optarg;
866       if (
867 #ifdef solaris2x
868 	 (int)
869 #endif
870 	 NULL == getpwnam(name)) {
871 	fprintf(stderr, "%s: User '%s' does not exist.\n",
872 		Progname, name);
873 	exit(1);
874       }
875       break;
876     case 'U':
877       showall = FALSE;
878       Uoption = TRUE;
879       break;
880     case 'w':
881       wide    = TRUE;
882       break;
883     case 'h':
884     case '?':
885     default :
886       Usage();
887       break;
888     }
889 
890 #ifdef USE_GetProcessesDirect
891   NProc = input == NULL ? GetProcessesDirect() : GetProcesses();
892 #else
893   NProc = GetProcesses();
894 #endif
895 
896 #ifdef ZOMBIES_HAVE_PID_0
897   FixZombies();
898 #endif
899 
900 #ifdef PARENT_CYCLE_PID_MINUS_ONE
901   FixParentCycle();
902 #endif
903 
904   if (NProc == 0) {
905     fprintf(stderr, "%s: No processes read.\n", Progname);
906     exit(1);
907   }
908 
909 #ifdef DEBUG
910   if (debug) fprintf(stderr, "NProc = %d processes found.\n", NProc);
911 #endif
912 
913   RootPid = GetRootPid();
914 
915 #ifdef DEBUG
916   if (debug) fprintf(stderr, "RootPid = %d.\n", RootPid);
917 #endif
918 
919 #if defined(UID2USER) && defined(DEBUG)
920   if (debug) uid2user(0,NULL,0);
921 #endif
922   MyPid = getpid();
923 
924   if (wide)
925     Columns = MAXLINE - 1;
926   else {
927 #if defined (HAS_TERMDEF)
928     Columns = atoi((char*)termdef(fileno(stdout),'c'));
929 #elif defined(TIOCGWINSZ)
930     struct winsize winsize;
931     if ( ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1 )
932       Columns = winsize.ws_col;
933 #elif defined(TIOCGSIZE)
934     struct ttysize ttysize;
935     if ( ioctl(fileno(stdout), TIOCGSIZE, &ttysize) != -1 )
936       Columns = ttysize.ts_cols;
937 #else
938     char *env = getenv("COLUMNS");
939     Columns = env ? atoi(env) : 80;
940 #endif
941   }
942   if (Columns == 0) Columns = MAXLINE - 1;
943 
944   printf("%s", C->init);
945 
946   Columns += strlen(C->sg) + strlen(C->eg); /* Don't count hidden chars */
947 
948   if (Columns >= MAXLINE) Columns = MAXLINE - 1;
949 
950 #ifdef DEBUG
951   if (debug) fprintf(stderr, "Columns = %d\n", Columns);
952 #endif
953 
954   MakeTree();
955   MarkProcs();
956   DropProcs();
957 
958   if (argc == optind) { /* No pids */
959     PrintTree(get_pid_index(RootPid), "");
960   } else while (optind < argc) {
961     int idx;
962     pid = (long)atoi(argv[optind]);
963     idx = get_pid_index(pid);
964     if (idx > -1) PrintTree(idx, "");
965     optind++;
966   }
967   free(P);
968   return 0;
969 }
970 
971 #ifdef NEED_STRSTR
972 /* Contributed by Paul Kern <pkern AT utcc.utoronto.ca> */
strstr(s1,s2)973 static char * strstr(s1, s2)
974      register char *s1, *s2;
975 {
976   register int n1, n2;
977 
978   if (n2 = strlen(s2))
979     for (n1 = strlen(s1); n1 >= n2; s1++, n1--)
980       if (strncmp(s1, s2, n2) == 0)
981 	return s1;
982   return NULL;
983 }
984 #endif /* NEED_STRSTR */
985 
986 #ifdef NEED_SNPRINTF
snprintf(char * s,int namesiz,char * format,...)987 int snprintf (char *s, int namesiz, char *format, ...) {
988   /* Original portable version by Michael E. White.
989      This version of Stan Sieler (sieler AT allegro.com) */
990 
991   int  chars_needed;              /* not including trailing null */
992 
993   char bigbuf [1024] = {'\0'};    /* note: 1024 is a guess, and may not be large enough! */
994 
995   va_list ap;         /* some systems allow "va_list ap = NULL;", others *do not* (like MACH) */
996 
997   va_start (ap, format);
998   chars_needed = vsprintf (bigbuf, format, ap); /* note: chars_needed does not include trailing null */
999   va_end (ap);
1000 
1001   /* 0 is documented as "don't write anything" ... while not specifically spelled out
1002      (e.g., does it also mean "don't internally call vsprintf"?), one can imply that it simply means
1003      "don't write to the output buffer 's'.  (Otherwise, if we didn't call vsprintf, we wouldn't
1004      know what value of chars_needed to return!) */
1005 
1006    if (namesiz <= 0)
1007      ;     /* Don't touch 's' buffer at all! Note: on some systems, a negative namesiz
1008 	      will cause the process to abort. By checking for <= 0, not just 0, we differ
1009 	      in that area, but it's a reasonable difference. */
1010 
1011    else if (chars_needed >= namesiz)
1012      {     /* oh oh, output too large for 'name' buffer... */
1013        memcpy (s, bigbuf, namesiz - 1);
1014        s [namesiz - 1] = '\0';
1015      }
1016 
1017    else    /* size is ok */
1018      {
1019        memcpy (s, bigbuf, chars_needed); /* chars_needed < namesiz */
1020        s [chars_needed] = '\0';
1021        /* note: above two could be replaced by strcpy (s, bigbuf)
1022 	  since we know strlen (bigbuf) is acceptable.
1023 	  But, why copy byte at a time, comparing to null, when
1024 	  we *know* the length? */
1025      }
1026 
1027    return chars_needed;    /* May be larger than namesiz, but that's ok
1028 			      In fact, not just 'ok', it's *useful*! */
1029 }
1030 #endif  /* NEED_SNPRINTF */
1031 
1032 /*
1033  * $Log: pstree.c,v $
1034  * Revision 2.39  2015/05/13 12:24:47  fred
1035  * Summary: Don't use uninitialized structs when ioctl() fails, e.g. if run with stdout
1036  * redirected. Problem reported by Jan Stary.
1037  *
1038  * Revision 2.38  2015/04/20 14:50:42  fred
1039  * Summary: Added patch for AIX61 contributed by Michael Staats
1040  *
1041  * Revision 2.37  2015/04/20 10:15:29  fred
1042  * Summary: V2.36
1043  *
1044  * Revision 2.36  2013-04-12 11:47:03+02  fred
1045  * Some processes like apache under a recent Linux were listed with UID
1046  * root instead of the correct UID, as they use setuid(). We now read the
1047  * UID from the owner of /proc/PID instead of /proc/PID/stat, as this
1048  * seems to be updated correctly. Thanks to Tom Schmidt
1049  * <tschmidt AT micron.com> for pointing out this bug.
1050  *
1051  * Revision 2.35  2013-02-28 08:33:02+01  fred
1052  * Added Stan Sieler's fix to my adaption of snprintf fix by Stan Sieler :-)
1053  *
1054  * Revision 2.34  2013-02-27 16:57:25+01  fred
1055  * Added snprintf fix by Stan Sieler
1056  *
1057  * Revision 2.33  2009-11-10 22:12:39+01  fred
1058  * Added UTF8, enlarged MAXLINE
1059  *
1060  * Revision 2.32  2007-10-26 21:39:50+02  fred
1061  * Added option -l provided by Michael E. White <mewhite AT us.ibm.com>
1062  *
1063  * Revision 2.31  2007-06-08 17:45:23+02  fred
1064  * Fixed problem with users with long login name (Reported by Oleg A. Mamontov)
1065  *
1066  * Revision 2.30  2007-05-10 23:13:04+02  fred
1067  * *** empty log message ***
1068  *
1069  * Revision 2.29  2007-05-10 22:37:13+02  fred
1070  * Added fix for Solaris Zone and bug fix from Philippe Torche
1071  *
1072  * Revision 2.28  2007-05-10 22:01:07+02  fred
1073  * Added new determination of window width
1074  *
1075  * Revision 2.27  2005-04-08 22:08:45+02  fred
1076  * Also accept PPID==1 if nothing else is found. Should fix problem with
1077  * FreeBSD and security.bsd.see_other_uids=0.
1078  *
1079  * Revision 2.26  2004-10-15 13:59:03+02  fred
1080  * Fixed small bug with char/int variable c
1081  * reported by Tomas Dvorak <tomas_dvorak AT mailcan.com>
1082  *
1083  * Revision 2.25  2004-05-14 16:41:39+02  fred
1084  * Added workaround for spurious blank lines in ps output under AIX 5.2
1085  * reported by Dean Rowswell <rowswell AT ca.ibm.com>
1086  *
1087  * Revision 2.24  2004-04-14 09:10:29+02  fred
1088  * *** empty log message ***
1089  *
1090  * Revision 2.23  2004-02-16 10:55:20+01  fred
1091  * Fix for zombies (pid == 0) under FreeBSD
1092  *
1093  * Revision 2.22  2003-12-12 10:58:46+01  fred
1094  * Added support for TRU64 v5.1b TruCluster
1095  *
1096  * Revision 2.21  2003-10-06 13:55:47+02  fred
1097  * Fixed SEGV under Linux when process table changes during run
1098  *
1099  * Revision 2.20  2003-07-09 20:07:29+02  fred
1100  * cosmetic
1101  *
1102  * Revision 2.19  2003/05/26 15:33:35  fred
1103  * Merged FreeBSD, (Open|Net)BSD; added Darwin (APPLE), fixed wide output
1104  * in FreeBSD
1105  *
1106  * Revision 2.18  2003/03/13 18:53:22  fred
1107  * Added getenv("COLUMNS"), cosmetic changes
1108  *
1109  * Revision 2.17  2001/12/17 12:18:02  fred
1110  * Changed ps call to something like ps -eo uid,pid,ppid,pgid,args under
1111  * AIX and Linux, workaround for AIX 5L.
1112  *
1113  * Revision 2.17  2001-12-13 08:27:00+08  chris
1114  * Added workaround for AIX Version >= 5
1115  *
1116  * Revision 2.16  2000-03-01 10:42:22+01  fred
1117  * Added support for thread count (thcount) in other OSs than AIX
1118  *
1119  * Revision 2.15  2000-03-01 10:18:56+01  fred
1120  * Added process group support for {Net|Open}BSD following a suggestion
1121  * by Ralf Meyer <ralf AT thp.Uni-Duisburg.de>
1122  *
1123  * Revision 2.14  1999-03-22 20:45:02+01  fred
1124  * Fixed bug when line longer than MAXLINE, set MAXLINE=512
1125  *
1126  * Revision 2.13  1998-12-17 19:31:53+01  fred
1127  * Fixed problem with option -f when input file is empty
1128  *
1129  * Revision 2.12  1998-12-07 17:08:59+01  fred
1130  * Added -f option and sun 68000 support by Paul Kern
1131  * <pkern AT utcc.utoronto.ca>
1132  *
1133  * Revision 2.11  1998-05-23 13:30:28+02  fred
1134  * Added vt100 sequences, NetBSD support
1135  *
1136  * Revision 2.10  1998-02-02 15:04:57+01  fred
1137  * Fixed bug in MakeTree()/get_pid_index() when parent doesn't
1138  * exist. Thanks to Igor Schein <igor AT andrew.air-boston.com> for the bug
1139  * report.
1140  *
1141  * Revision 2.9  1998-01-07 16:55:26+01  fred
1142  * Added support for getprocs()
1143  *
1144  * Revision 2.9  1998-01-06 17:13:19+01  fred
1145  * Added support for getprocs() under AIX
1146  *
1147  * Revision 2.8  1997-10-22 15:09:39+02  fred
1148  * Cosmetic
1149  *
1150  * Revision 2.7  1997-10-22 15:01:40+02  fred
1151  * Minor changes in getprocs for AIX
1152  *
1153  * Revision 2.6  1997/10/16 16:35:19  fred
1154  * Added uid2name() caching username lookup, added patch for Solaris 2.x
1155  *
1156  * Revision 2.5  1997-02-05 14:24:53+01  fred
1157  * return PrintTree when nothing to do.
1158  *
1159  * Revision 2.4  1997/02/05 09:54:08  fred
1160  * Fixed bug when P[i].cmd is empty
1161  *
1162  * Revision 2.3  1997-02-04 18:40:54+01  fred
1163  * Cosmetic
1164  *
1165  * Revision 2.2  1997-02-04 14:11:17+01  fred
1166  * *** empty log message ***
1167  *
1168  * Revision 2.1  1997-02-04 13:55:14+01  fred
1169  * Rewritten
1170  *
1171  * Revision 1.13  1997-02-04 09:01:59+01  fred
1172  * Start of rewrite
1173  *
1174  * Revision 1.12  1996-09-17 21:54:05+02  fred
1175  * *** empty log message ***
1176  *
1177  * Revision 1.11  1996-09-17 21:52:52+02  fred
1178  * revision added
1179  *
1180  * Revision 1.10  1996-09-17 21:45:35+02  fred
1181  * replace \n and \t with ? in output
1182  *
1183  * Revision 1.4  1996-09-17 21:43:14+02  fred
1184  * Moved under RCS, replace \n and \t with ?
1185  */
1186