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