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