home *** CD-ROM | disk | FTP | other *** search
- From decwrl!sun-barr!cs.utexas.edu!uunet!allbery Thu Aug 3 08:51:32 PDT 1989
- Article 998 of comp.sources.misc:
- Path: decwrl!sun-barr!cs.utexas.edu!uunet!allbery
- From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
- Newsgroups: comp.sources.misc
- Subject: v07i106: pt - show process family tree
- Message-ID: <61721@uunet.UU.NET>
- Date: 28 Jul 89 01:22:32 GMT
- Sender: allbery@uunet.UU.NET
- Reply-To: bauer@loligo.cc.fsu.edu (Jeff Bauer)
- Organization: Florida State University
- Lines: 777
- Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
-
- Posting-number: Volume 7, Issue 106
- Submitted-by: bauer@loligo.cc.fsu.edu (Jeff Bauer)
- Archive-name: pt
-
- Here's a simple utility that displays the hierarchy of the process
- structure on a UNIX system. I must admit, however, that "ps" output
- on System V is cleaner in terms of white space between columns
- than bsd systems and "pt" does a better job there.
-
- Just delete everything above and include the "cut here" line and
- run it through "sh".
-
- -- Jeff Bauer
-
- -- cut here -- cut here -- cut here -- cut here -- cut here --
- #!/bin/sh
- # shar: Shell Archiver
- # Run the following text with /bin/sh to create:
- # Makefile
- # pt.1
- # pt.1.cat
- # pt.c
- sed 's/^X//' << 'SHAR_EOF' > Makefile
- X#
- X# Comment out the next line if you are building for System V
- X#
- XDEFINES=-DBSD
- XCFLAGS=-O
- X
- Xpt: pt.c
- X $(CC) $@.c -o $@ $(CFLAGS) $(DEFINES)
- SHAR_EOF
- sed 's/^X//' << 'SHAR_EOF' > pt.1
- X.TH PT 1
- X.SH NAME
- X\fBpt\fR \- display process family tree
- X.SH USAGE
- X.B pt [-a] [user]
- X.SH DESCRIPTION
- XThe
- X.I pt
- Xcommand is a convenient alternative to using ps(1) to display a list
- Xof user processes. Instead of showing a list of processes in the order
- Xthat they appear in the system process table,
- X.I pt
- Xdisplays processes in order of their family hierarchy. The PID of
- Xchildren processes are indented to show the relationship between a
- Xparent and child process.
- X.sp
- XWith no parameters specified,
- X.I pt
- Xwill display only those processes not owned by "root". If
- X.I \-a
- Xis specified,
- X.I pt
- Xwill display all processes. If
- X.I user
- Xis specified,
- X.I pt
- Xwill display only processes owned by the user name specified.
- X.sp
- XThe column headings are somewhat self-explanatory; for further
- Xdetails see ps(1).
- X.SH EXAMPLE
- X.sp
- X$ pt carr
- X.br
- XPID User Time Pages S Command
- X.br
- X 8618 carr 0:00 8 (S) -ksh
- X.br
- X 8724 carr 0:00 8 (S) -ksh
- X.br
- X 8756 carr 16:13 479 (R) vlad6
- X.SH CAVEATS
- XWhen a user name is specified on the command line sometimes
- X.I pt
- Xcannot determine the correct lineage of a process. Any of these
- Xorphan processes will appear under the heading of "Orphans" on
- Xthe output.
- X.sp
- XSince
- X.I pt
- Xreally is a filter from a forked-off ps(1) sometimes the parsing of
- Xthe output from ps(1) fails and the fields get garbled.
- X.SH SEE ALSO
- X.BR ps(1).
- SHAR_EOF
- sed 's/^X//' << 'SHAR_EOF' > pt.1.cat
- XPT(1) USER COMMANDS PT(1)
- X
- X
- X
- XNAME
- X pt - display process family tree
- X
- XUSAGE
- X pt [-a] [user]
- X
- XDESCRIPTION
- X The _p_t command is a convenient alternative to using ps(1) to
- X display a list of user processes. Instead of showing a list
- X of processes in the order that they appear in the system
- X process table, _p_t displays processes in order of their fam-
- X ily hierarchy. The PID of children processes are indented
- X to show the relationship between a parent and child process.
- X
- X With no parameters specified, _p_t will display only those
- X processes not owned by "root". If -_a is specified, _p_t will
- X display all processes. If _u_s_e_r is specified, _p_t will
- X display only processes owned by the user name specified.
- X
- X The column headings are somewhat self-explanatory; for
- X further details see ps(1).
- X
- XEXAMPLE
- X $ pt carr
- X PID User Time Pages S Command
- X 8618 carr 0:00 8 (S) -ksh
- X 8724 carr 0:00 8 (S) -ksh
- X 8756 carr 16:13 479 (R) vlad6
- X
- XCAVEATS
- X When a user name is specified on the command line sometimes
- X _p_t cannot determine the correct lineage of a process. Any
- X of these orphan processes will appear under the heading of
- X "Orphans" on the output.
- X
- X Since _p_t really is a filter from a forked-off ps(1) some-
- X times the parsing of the output from ps(1) fails and the
- X fields get garbled.
- X
- XSEE ALSO
- X ps(1).
- SHAR_EOF
- sed 's/^X//' << 'SHAR_EOF' > pt.c
- X/*
- X pt.c - show process family tree
- X
- X pt [-a] [user]
- X
- X Default compilation is for System V UNIX.
- X To compile for a bsd system try :
- X
- X cc pt.c -o pt -O -DBSD
- X */
- X
- X#include <stdio.h>
- X#include <fcntl.h>
- X#include <string.h>
- X#ifdef BSD
- X#include <pwd.h>
- X#include <ctype.h>
- X#endif BSD
- X
- X#define MAXLINE 256
- X#define MYBUFSZ 256
- X#define STDIN 0
- X#define STDOUT 1
- X#define STDERR 2
- X
- Xchar line[MAXLINE], token[MAXLINE];
- Xint firstr, cur_char, cur_row, cur_col;
- X
- X#define TRUE 1
- X#define FALSE 0
- X#define UNDEF 2
- X
- Xstruct psl {
- X char state[8]; /* S */
- X char user[9]; /* UID */
- X unsigned int pid; /* PID */
- X unsigned int ppid; /* PPID */
- X unsigned int pages; /* SZ */
- X char time[12]; /* TIME */
- X char verb[32]; /* COMD */
- X};
- X
- Xstruct plist {
- X struct psl cp;
- X struct plist *s; /* sibling */
- X struct plist *c; /* child */
- X};
- X
- Xstruct psl cp;
- Xstruct plist *proot;
- Xstruct plist *orphans;
- Xint orphcnt;
- Xint showroot = FALSE;
- X#ifdef BSD
- Xstruct passwd *pe;
- Xextern int errno;
- X#endif BSD
- X
- Xextern char *malloc();
- Xvoid strcpy2(), cp_plist(), pprint();
- Xint pinsert();
- Xstruct plist *pfind();
- X
- Xvoid main(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X int gls;
- X int pfds[2];
- X char who[9];
- X int slen, wsp;
- X char tbuf[32], tbuf2[32];
- X#ifdef BSD
- X char d1[32], d2[32], d3[32], d4[32], d5[32], d6[32], d7[32];
- X#endif
- X struct plist *child;
- X int fostered;
- X int offset;
- X int adopted;
- X int i, j;
- X
- X if (argc == 1) who[0] = '\0';
- X
- X while (argc--) {
- X if (argc != 0) {
- X if (strcmp(argv[argc], "-a") == 0)
- X showroot = TRUE;
- X else
- X strcpy2(who, argv[argc], 8);
- X }
- X }
- X if (argc > 3) {
- X fprintf(stderr,"Usage : %s [-a] [user]\n", argv[0]);
- X exit(1);
- X }
- X if (strcmp(who, "root") == 0)
- X showroot = TRUE;
- X
- X orphcnt = 0;
- X
- X if (pipe(pfds) < 0) {
- X perror("pipe");
- X exit(1);
- X }
- X
- X if (fork() == 0) { /* BEGIN child processing */
- X if (close(STDOUT) < 0) { /* release fd 1 */
- X perror("close1");
- X exit(1);
- X }
- X if (dup(pfds[1]) < 0) { /* re-use fd 1 */
- X perror("dup1");
- X exit(1);
- X }
- X if (close(pfds[1]) < 0) {
- X perror("close2");
- X exit(1);
- X }
- X if (close(pfds[0]) < 0) {
- X perror("close3");
- X exit(1);
- X }
- X if (who[0] == '\0')
- X#ifdef BSD
- X execl("/bin/ps","ps","-axlw",0);
- X#else
- X execl("/bin/ps","ps","-elf",0);
- X#endif BSD
- X else
- X#ifdef BSD
- X execl("/bin/ps","ps","-axlw",0); /* no single-user filter on bsd "ps" */
- X#else
- X execl("/bin/ps","ps","-u",who,"-lf",0);
- X#endif BSD
- X } /* END child processing */
- X
- X if (close(STDIN) < 0) { /* release fd 0 */
- X perror("close4");
- X exit(1);
- X }
- X if (dup(pfds[0]) < 0) { /* re-use fd 0 */
- X perror("dup2");
- X exit(1);
- X }
- X if (close(pfds[0]) < 0) {
- X perror("close5");
- X exit(1);
- X }
- X if (close(pfds[1]) < 0) {
- X perror("close6");
- X exit(1);
- X }
- X
- X firstr = TRUE;
- X cur_row = 0;
- X while ((gls = getline(STDIN, line, MAXLINE)) >= 0) {
- X if (gls == -2) break;
- X cur_char = 0;
- X cur_col = 0;
- X offset = FALSE;
- X#ifdef BSD
- X d7[0] = '\0';
- X#endif BSD
- X while (get_token(line, token, MAXLINE, &wsp) >= 0) {
- X if (cur_row > 0) { /* skip column headings */
- X switch (cur_col) {
- X#ifdef BSD
- X case 0 :
- X if (((wsp > 0) && (strlen(token) > 5)) ||
- X ((wsp == 0) && (strlen(token) > 10))) {
- X j = 0;
- X for (i = (7 - wsp); i < strlen(token); i++)
- X tbuf[j++] = token[i];
- X tbuf[j+1] = '\0';
- X strcpy(token, tbuf);
- X cur_col += 1;
- X sscanf(token, "%d", &i);
- X if ((pe = getpwuid(i)) != NULL)
- X strcpy2(cp.user, pe->pw_name, 8);
- X else {
- X strcpy2(cp.user, "(bogus)", 8);
- X fprintf(stderr,"1: Checking UID %d : ",i);
- X perror("getpwuid");
- X }
- X }
- X else if ((wsp == 6) && (strlen(token) > 1)) {
- X cur_col += 1;
- X sscanf(token, "%d", &i);
- X if ((pe = getpwuid(i)) != NULL)
- X strcpy2(cp.user, pe->pw_name, 8);
- X else {
- X strcpy2(cp.user, "(bogus)", 8);
- X fprintf(stderr,"2: Checking UID %d : ",i);
- X perror("getpwuid");
- X }
- X }
- X break;
- X case 1 :
- X sscanf(token, "%d", &i);
- X if ((pe = getpwuid(i)) != NULL)
- X strcpy2(cp.user, pe->pw_name, 8);
- X else {
- X strcpy2(cp.user, "(bogus)", 8);
- X fprintf(stderr,"3: Checking UID %d : ",i);
- X perror("getpwuid");
- X }
- X break;
- X case 2 :
- X sscanf(token, "%d", &cp.pid);
- X break;
- X case 3 :
- X sscanf(token, "%d", &cp.ppid);
- X break;
- X case 7 :
- X if (strlen(token) > 4)
- X offset = TRUE;
- X break;
- X case 8 :
- X if (offset == TRUE) {
- X strcpy2(d1, token, 32);
- X }
- X break;
- X case 9 :
- X if (offset == TRUE) {
- X strcpy2(d2, token, 32);
- X }
- X else {
- X strcpy2(d1, token, 32);
- X }
- X break;
- X case 10 :
- X if (offset == TRUE) {
- X strcpy2(d3,token, 32);
- X }
- X else {
- X strcpy2(d2, token, 32);
- X }
- X break;
- X case 11 :
- X if (offset == TRUE) {
- X strcpy(d4, token, 32);
- X }
- X else {
- X strcpy2(d3, token, 32);
- X }
- X break;
- X case 12 :
- X if (offset == TRUE) {
- X strcpy2(d5, token, 32);
- X }
- X else {
- X strcpy2(d4, token, 32);
- X }
- X break;
- X case 13 :
- X if (offset == TRUE) {
- X strcpy2(d6, token, 32);
- X }
- X else {
- X strcpy2(d5, token, 32);
- X }
- X break;
- X case 14 :
- X if (offset == TRUE) {
- X strcpy2(d7, token, 32);
- X }
- X else {
- X strcpy2(d6, token, 32);
- X }
- X break;
- X#else
- X case 1 :
- X cp.state[0] = token[0];
- X cp.state[1] = '\0';
- X if (strlen(token) > 1) {
- X strcpy2(cp.user, (token+1), 8);
- X cur_col += 1;
- X }
- X if ((cp.state[0] == 'R') || (cp.state[0] == 'O'))
- X offset = TRUE; /* "WCHAN" is empty */
- X break;
- X case 2 :
- X strcpy2(cp.user, token, 8);
- X break;
- X case 3 :
- X sscanf(token, "%d", &cp.pid);
- X break;
- X case 4 :
- X sscanf(token, "%d", &cp.ppid);
- X break;
- X case 9 :
- X if (cp.state[0] == 'Z') {
- X cp.pages = 0;
- X break;
- X }
- X strcpy(tbuf,token);
- X if ((slen = strlen(token)) > 9 ) {
- X strcpy2(tbuf, token, (slen-9));
- X cur_col += 1;
- X }
- X sscanf(tbuf, "%d", &cp.pages);
- X break;
- X case 12 :
- X if (offset == TRUE) {
- X strcpy2(cp.time, token, 12);
- X }
- X break;
- X case 13 :
- X if (offset == TRUE) {
- X strcpy2(cp.verb, token, 32);
- X }
- X else {
- X strcpy2(cp.time, token, 12);
- X }
- X strcpy2(tbuf2, token, 12);
- X break;
- X case 14 :
- X if (offset == FALSE) {
- X strcpy2(cp.verb, token, 32);
- X }
- X break;
- X#endif BSD
- X }
- X cur_col += 1;
- X }
- X }
- X#ifdef BSD
- X
- X /* Sigh... */
- X
- X if (isupper(d2[0]) || (d2[0] == 'p')) {
- X strcpy(cp.state, d2);
- X strcpy(cp.time, d4);
- X strcpy(cp.verb, d5);
- X }
- X else {
- X strcpy(cp.state, d3);
- X strcpy(cp.time, d5);
- X strcpy(cp.verb, d6);
- X }
- X
- X#endif BSD
- X if (cur_row > 0) {
- X#ifdef BSD
- X if (who[0] == '\0')
- X pinsert(&cp, TRUE);
- X else
- X if (strcmp(cp.user, who) == 0)
- X pinsert(&cp, TRUE);
- X#else
- X pinsert(&cp, TRUE);
- X#endif BSD
- X }
- X cur_row += 1;
- X }
- X wait(0);
- X
- X /* Place orphans into their foster homes */
- X
- X fostered = 0;
- X
- X while (orphcnt != fostered) {
- X
- X adopted = FALSE;
- X
- X for (child = orphans; child != NULL; child = child->s) {
- X if (strcmp(child->cp.state, "HOME") != 0) {
- X if (pinsert(&child->cp, FALSE) == TRUE) {
- X strcpy(child->cp.state, "HOME");
- X fostered += 1;
- X adopted = TRUE;
- X }
- X }
- X }
- X if (adopted == FALSE) {
- X break;
- X }
- X }
- X printf("PID\t\t\t User Time Pages S Command\n");
- X pprint(proot, 0, FALSE);
- X if (orphcnt != fostered) {
- X printf("Orphans :\n");
- X printf("PID (PPID)\t\t User Time Pages S Command\n");
- X pprint(orphans, 0, TRUE);
- X }
- X}
- X
- Xint pinsert(cp, adopt) /* TRUE = info inserted; FALSE = info orphaned */
- Xstruct psl *cp;
- Xint adopt; /* TRUE = add orphans to orphan list */
- X{
- X struct plist *parent, *sibling;
- X int iflag;
- X
- X iflag = TRUE;
- X if (proot == NULL) { /* call this one root for now */
- X if ((proot = (struct plist *) malloc(sizeof(struct plist))) == NULL) {
- X fprintf(stderr,"malloc failed\n");
- X exit(1);
- X }
- X cp_plist(proot, cp);
- X }
- X else { /* find parent; traverse sibling list & insert */
- X if ((parent = pfind(proot, cp->ppid)) == NULL) { /* no parent yet */
- X iflag = FALSE;
- X if (adopt == TRUE) {
- X orphcnt += 1;
- X if (orphans == NULL) {
- X if ((orphans = (struct plist *) malloc(sizeof(struct plist))) == NULL) {
- X fprintf(stderr,"malloc failed\n");
- X exit(1);
- X }
- X cp_plist(orphans, cp);
- X }
- X else { /* chain orphans together (heartless!) */
- X for (sibling = orphans; sibling->s != NULL; sibling = sibling->s);
- X if ((sibling->s = (struct plist *) malloc(sizeof(struct plist))) == NULL) {
- X fprintf(stderr,"malloc failed\n");
- X exit(1);
- X }
- X cp_plist(sibling->s, cp);
- X }
- X }
- X }
- X else { /* parent exists */
- X if (parent->c == NULL) { /* first child */
- X if ((parent->c = (struct plist *) malloc(sizeof(struct plist))) == NULL) {
- X fprintf(stderr,"malloc failed\n");
- X exit(1);
- X }
- X cp_plist(parent->c, cp);
- X }
- X else { /* siblings exist */
- X sibling = parent->c;
- X while (sibling->s != NULL) sibling = sibling->s;
- X if ((sibling->s = (struct plist *) malloc(sizeof(struct plist))) == NULL) {
- X fprintf(stderr,"malloc failed\n");
- X exit(1);
- X }
- X cp_plist(sibling->s, cp);
- X }
- X }
- X }
- X return(iflag);
- X}
- X
- Xstruct plist *pfind(cp, ppid)
- Xstruct plist *cp;
- Xint ppid;
- X{
- X struct plist *p;
- X
- X if (cp == NULL) return(NULL);
- X else if (cp->cp.pid == ppid) return(cp);
- X else {
- X if ((p = pfind(cp->c, ppid)) == NULL)
- X return(pfind(cp->s, ppid));
- X else
- X return(p);
- X }
- X}
- X
- Xvoid pprint(cp, cnt, orphflg)
- Xstruct plist *cp;
- Xint cnt, orphflg;
- X{
- X int i, j;
- X char buf[16];
- X
- X if (cp == NULL) return;
- X
- X if (strcmp(cp->cp.state, "HOME") != 0) {
- X if (!((showroot == FALSE) && (strcmp(cp->cp.user, "root") == 0))) {
- X if (orphflg == TRUE) {
- X#ifdef BSD
- X sprintf(buf, "%d (%d)",cp->cp.pid, cp->cp.ppid);
- X j = strlen(buf);
- X#else
- X j = sprintf(buf, "%d (%d)",cp->cp.pid, cp->cp.ppid);
- X#endif BSD
- X }
- X else {
- X#ifdef BSD
- X sprintf(buf, "%d", cp->cp.pid);
- X j = strlen(buf);
- X#else
- X j = sprintf(buf, "%d", cp->cp.pid);
- X#endif BSD
- X }
- X for (i = 0; i < cnt; i++) printf(" ");
- X printf(" %s", buf);
- X for (i = 0; i < (3-(((cnt*1)+j+1)/8)); i++) printf("\t");
- X printf("%8s %8s %8d (%s) %s\n", cp->cp.user, cp->cp.time,
- X cp->cp.pages, cp->cp.state, cp->cp.verb);
- X }
- X }
- X
- X pprint(cp->c, cnt+1, orphflg);
- X pprint(cp->s, cnt, orphflg);
- X}
- X
- Xvoid cp_plist(pto, pfrom)
- Xstruct plist *pto;
- Xstruct psl *pfrom;
- X{
- X strcpy(pto->cp.state, pfrom->state);
- X strcpy(pto->cp.user, pfrom->user);
- X pto->cp.pid = pfrom->pid;
- X pto->cp.ppid = pfrom->ppid;
- X pto->cp.pages = pfrom->pages;
- X strcpy(pto->cp.time, pfrom->time);
- X strcpy(pto->cp.verb, pfrom->verb);
- X pto->c = pto->s = NULL;
- X}
- X
- Xint getline(fd,buf,max)
- Xint fd;
- Xchar *buf;
- Xint max;
- X
- X/*
- X getline(fd, buffer, max)
- X
- X Get a line from file *fd* up to *max* bytes into *buffer*.
- X Return 0 if OK, -1 if hit EOF, -2 if first read is NULL,
- X -3 if read failed.
- X */
- X
- X{
- X static char mybuf[MYBUFSZ]; /* internal buffer */
- X static int myend = 0; /* # bytes in mybuf */
- X static int mycnt = 0; /* # bytes already scanned */
- X static char *curline = NULL; /* beginning of current line to get */
- X char *p, lastc;
- X int nbytes;
- X
- X if (firstr == TRUE) curline = NULL;
- X
- X if (curline == NULL) { /* empty buffer */
- X if ((myend = read(fd, mybuf, MYBUFSZ)) < 0) {
- X perror("read");
- X return(-3);
- X }
- X curline = mybuf; /* new buffer filled */
- X mycnt = 0;
- X }
- X
- X if ((myend == 0) && firstr) { /* first read hit EOF (empty file) */
- X *buf = '\0';
- X return(-2);
- X }
- X
- X if (myend == 0) { /* later read hit EOF */
- X *buf = '\0';
- X return(-1);
- X }
- X
- X firstr = FALSE;
- X
- X p = curline;
- X nbytes = 0;
- X
- X read_loop:
- X
- X while ((*p != '\n') && (mycnt < myend) && (nbytes <= max)) {
- X *buf++ = *p++;
- X mycnt += 1;
- X nbytes += 1;
- X }
- X lastc = *p;
- X p += 1;
- X mycnt += 1;
- X
- X if ((mycnt >= myend) && (lastc != '\n')) {
- X if ((myend = read(fd, mybuf, MYBUFSZ)) < 0) {
- X perror("read");
- X return(-3);
- X }
- X p = curline = mybuf;
- X lastc = *p;
- X mycnt = 0;
- X }
- X if ((mycnt != myend) && (lastc != '\n')) goto read_loop;
- X
- X *buf += 1;
- X *buf = '\0';
- X curline = p; /* set for next *getline* call */
- X
- X if (mycnt >= myend)
- X curline = NULL; /* reached end of buffer */
- X
- X return(0);
- X}
- X
- Xint get_token(line, token, max, wsp) /* return 0 = token, -1 = no tokens left on line */
- Xchar *line, *token;
- Xint *wsp; /* # of white spaces preceding token */
- Xint max;
- X{
- X int i, j;
- X
- X i = cur_char;
- X j = 0;
- X *wsp = 0;
- X
- X if ((i >= (max-1)) || (line[i] == '\0')) {
- X *token = '\0';
- X return(-1);
- X }
- X
- X while((line[i] == ' ') || (line[i] == '\t')) {
- X i++;
- X *(wsp)++;
- X }
- X
- X while ((line[i] != ' ') && (line[i] != '\t') && (line[i] != '\n')
- X && (line[i] != '\r') && (line[i] != '\0'))
- X token[j++] = line[i++];
- X
- X token[j] = '\0';
- X
- X if (line[i] != '\0') cur_char = i + 1;
- X else cur_char = i;
- X return(0);
- X}
- X
- Xvoid strcpy2(dst, src, max)
- Xchar *dst, *src;
- Xint max;
- X{
- X int i;
- X
- X i = 0;
- X while ((src[i] != ' ') && (i < max) && (src[i] != '\0')) {
- X dst[i] = src[i];
- X i += 1;
- X }
- X dst[i] = '\0';
- X}
- SHAR_EOF
- exit
-
-
-