From decwrl!ucbvax!tut.cis.ohio-state.edu!cs.utexas.edu!uunet!allbery Sun Aug 6 16:31:24 PDT 1989 Article 1023 of comp.sources.misc: Path: decwrl!ucbvax!tut.cis.ohio-state.edu!cs.utexas.edu!uunet!allbery From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Newsgroups: comp.sources.misc Subject: v07i122: si -- display user process trees (Xenix 386) Message-ID: <62808@uunet.UU.NET> Date: 6 Aug 89 22:31:47 GMT Sender: allbery@uunet.UU.NET Reply-To: allbery@hal.CWRU.Edu@nc386.UUCP Lines: 727 Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Posting-number: Volume 7, Issue 122 Submitted-by: allbery@hal.CWRU.Edu@nc386.UUCP Archive-name: si As one of the folks in charge of NCoast.ORG, I often find it useful to keep an eye on what people are doing on the system. This program makes it easy by displaying genealogical process trees. ++Brandon #--------------------------------CUT HERE------------------------------------- #! /bin/sh # # This is a shell archive. Save this into a file, edit it # and delete all lines above this comment. Then give this # file to sh by executing the command "sh file". The files # will be extracted into the current directory owned by # you with default permissions. # # The files contained herein are: # # -rw-r--r-- 1 allbery 20 780 Aug 6 14:53 README # -rw-r--r-- 1 allbery 20 11187 Aug 6 14:06 si.c # -rw-r--r-- 1 allbery 20 2860 Aug 6 14:45 si.man # echo 'x - README' if test -f README; then echo 'shar: not overwriting README'; else sed 's/^X//' << '________This_Is_The_END________' > README XThis program displays process trees for processes other than the kernel Xand init. The name is historical; the first version did considerably Xmore than this one does, but subsequent versions were under System III Xand much of the information was unavailable or inaccessible. X XTo compile: cc -o si si.c -ltinfo -lx X chown sysinfo si X chmod 4711 si X(you need root permissions to chown and chgrp the program) X XTo run: si [ -m ] X XUse -m to get idle time calculations like SCO's version of w; the default is Xthe idle time calculation used by who and finger. (I wish more programs Xallowed the user to choose!) X XThe current version only works under SCO Xenix 386, but should be portable to XSystem III, System V R2/R3, and Xenix 3/5 systems with a little tweaking. X X++Brandon ________This_Is_The_END________ if test `wc -c < README` -ne 780; then echo 'shar: README was damaged during transit (should have been 780 bytes)' fi fi ; : end of overwriting check echo 'x - si.c' if test -f si.c; then echo 'shar: not overwriting si.c'; else sed 's/^X//' << '________This_Is_The_END________' > si.c X/* X * System process and user information X * X * Slurp the entire process table into memory (discarding system processes) X * and sort it by process tree and tty. Then present the information after X * the fashion of the old "si", using modified strings to represent NLI ttys X * and null ttys. X * X * This version is for SCO Xenix 386. The original ran under Plexus System III X * on a Plexus P/35 (68000 Unix). X */ X X#define M_TERMINFO /* d*mn SCO, anyway */ X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#define UTMP "/etc/utmp" X#define DEV "/dev/" X#define KERNEL "/xenix" X#define KMEM "/dev/kmem" X#define PMEM "/dev/mem" X#define SMEM "/dev/swap" X Xint kfd; Xint mfd; Xint sfd; Xint utmp; Xint dfd; Xint idlef; Xlong nproc; Xdaddr_t swplo; X Xstruct nlist kernel[] = X{ X {"_v"}, X {"_proc"}, X {"_swplo"}, X {NULL}, X}; X Xstruct tm *localtime(); Xstruct passwd *getpwuid(), *getpwnam(); Xstruct group *getgrgid(); Xchar *strrchr(), *malloc(); X X#define DAY (60L*60L*24) X#define HOUR (60L*60L) X#define MIN (60L) X X#define static X Xstatic void endw(), pcmd(); Xstatic int die(), paint(), grandparent(), pgsort(), rgetc(), showtty(); Xstatic struct proc *ppid(); X Xstatic Xvoid Xendw() X{ X move(LINES - 1, 0); X clrtoeol(); X refresh(); X endwin(); X} X Xint dying = 0; Xint repaint = 0; X Xstatic Xint Xdie(s) X{ X signal(s, die); X dying = 1; X} X Xstatic Xint Xpaint(s) X{ X signal(s, paint); X repaint = 1; X} X Xstruct proc *proctab; Xint numproc; X X/* X * find the ultimate parent of a tree of processes X */ X Xstatic Xstruct proc * Xppid(p) X register struct proc *p; X{ X register struct proc *pp; X X if (p->p_ppid == 1) X return p; X for (;;) { X for (pp = proctab; pp < &proctab[numproc]; pp++) X if (pp->p_pid == p->p_ppid) X break; X if (pp == &proctab[numproc]) X return 0; X if (pp->p_ppid == 1) X return pp; X p = pp; X } X} X X/* X * similar to ppid(), but stop when a parent process is the other specified X * process; return 1 if it's found, 0 if not X */ X Xstatic Xint Xgrandparent(p, gp) X register struct proc *p, *gp; X{ X register struct proc *pp; X X if (p->p_ppid < 2) X return 0; X for (;;) { X for (pp = proctab; pp < &proctab[numproc]; pp++) X if (pp->p_pid == p->p_ppid) X break; X if (pp == &proctab[numproc]) X return 0; X if (pp == gp) X return 1; X if (pp->p_ppid < 2) X return 0; X p = pp; X } X} X X/* X * sort function for qsort() of process table X * order by process group, then parent of "tree" of processes within group, X * then relative hierarchy within "tree". X * if tree parent's pid is same as pgrp, always sorts before other parents X */ X Xstatic Xint Xpgsort(p1, p2) X register struct proc *p1, *p2; X{ X register struct proc *p1p, *p2p; X X /* sort by process group first... */ X if (p1->p_pgrp != p2->p_pgrp) X return p1->p_pgrp - p2->p_pgrp; X /* sort by great^n-grandparent */ X if ((p1p = ppid(p1)) != (p2p = ppid(p2))) { X if (p1p->p_pgrp == p1p->p_pid) X return -1; X if (p2p->p_pgrp == p2p->p_pid) X return 1; X return p1p->p_pid - p2p->p_pid; X } X /* great^n-grandparent comes first, always */ X if (p1->p_ppid < 2) X return -1; X if (p2->p_ppid < 2) X return 1; X /* follow hierarchy up searching for common ancestry */ X if (grandparent(p1, p2)) X return 1; X if (grandparent(p2, p1)) X return -1; X /* this shouldn't happen... */ X return 0; X} X Xint Xmain(argc, argv) X char **argv; X{ X struct var vars; X register struct proc *scanp, *insp; X int depth, ptr, pgrp, ruid, rgid; X int stack[20]; X struct user *u; X struct passwd *pw; X struct group *gr; X X if (argc == 2) X idlef = (strcmp(argv[1], "-m") == 0); X signal(SIGHUP, die); X signal(SIGQUIT, die); X signal(SIGINT, paint); X signal(SIGTERM, die); X initscr(); X cbreak(); X noecho(); X nl(); X if (!cursor_address || !*cursor_address) X { X endw(); X fprintf(stderr, "I can't use your terminal.\n"); X exit(1); X } X if (nlist(KERNEL, kernel) == -1) X { X endw(); X perror(KERNEL); X exit(2); X } X if ((utmp = open(UTMP, 0)) == -1) X { X endw(); X perror(UTMP); X exit(1); X } X if ((kfd = open(KMEM, 0)) == -1) X { X endw(); X perror(KMEM); X exit(3); X } X lseek(kfd, kernel[0].n_value, 0); X read(kfd, &vars, sizeof vars); X nproc = vars.v_proc; X lseek(kfd, kernel[2].n_value, 0); X read(kfd, &swplo, sizeof swplo); X if ((mfd = open(PMEM, 0)) == -1) X { X endw(); X perror(PMEM); X exit(4); X } X if ((sfd = open(SMEM, 0)) == -1) X { X endw(); X perror(SMEM); X exit(5); X } X if ((dfd = open(DEV, 0)) == -1) X { X endw(); X perror(DEV); X exit(6); X } X if ((proctab = (struct proc *) malloc(nproc * sizeof *proctab)) == 0) X { X endw(); X fprintf(stderr, "Out of memory\n"); X exit(6); X } X for (;;) X { X /* handle pending signals */ X if (dying) X { X endw(); X exit(0); X } X if (repaint) X { X repaint = 0; X clear(); X } X /* prepare for output */ X erase(); X /* gobble proc */ X lseek(kfd, (long) kernel[1].n_value, 0); X read(kfd, proctab, nproc * sizeof *proctab); X numproc = nproc; X /* zero out swapper and init */ X memset(proctab, 0, 2 * sizeof *proctab); X insp = proctab; X /* prune proctab[] (and add user structures) */ X for (scanp = proctab; scanp < &proctab[nproc]; scanp++) X { X switch (scanp->p_stat) X { X case 0: X case SWAIT: X case SIDL: X case SZOMB: X scanp->p_stat = 0; X numproc--; X continue; X default: X while (insp->p_stat != 0 && insp != scanp) X insp++; X if (insp != scanp) X { X memcpy(insp, scanp, sizeof *scanp); X scanp->p_stat = 0; X } X if (!(insp->p_wchan = malloc(sizeof (struct user)))) X { X endw(); X fprintf(stderr, "Out of memory\n"); X exit(8); X } X if (insp->p_flag & SLOAD) X { X lseek(mfd, insp->p_addr[0].te_frameno * NBPC, 0); X read(mfd, insp->p_wchan, sizeof (struct user)); X } X else X { X lseek(sfd, insp->p_addr[0].te_frameno * NBPC, 0); X read(sfd, insp->p_wchan, sizeof (struct user)); X } X /* zap gettys, we could care less */ X if (isgetty(insp)) X { X insp->p_stat = 0; X free(insp->p_wchan); X scanp->p_stat = 0; X numproc--; X } X } X } X /* sort proctab[] by process group */ X qsort(proctab, numproc, sizeof *proctab, pgsort); X /* print proctab[] */ X pgrp = -1; X for (scanp = proctab; scanp < &proctab[numproc]; scanp++) X { X u = (struct user *) scanp->p_wchan; X ((struct user *) scanp->p_wchan)->u_procp = scanp; X if (scanp->p_pgrp != pgrp) X { X if (pgrp != -1) X addch('\n'); X pgrp = scanp->p_pgrp; X if (!u->u_ttyp) X { X addstr("(no tty)"); X move(stdscr->_cury, 40); X addstr("Process group detached from terminal\n"); X ruid = u->u_ruid; X rgid = u->u_rgid; X } X else if (!showtty(scanp, u, &ruid, &rgid)) X { X ruid = u->u_ruid; X rgid = u->u_rgid; X move(stdscr->_cury, 40); X addstr("Process group orphaned\n"); X } X depth = -1; X } X for (ptr = depth; ptr >= 0; ptr--) X if (scanp->p_ppid == stack[ptr]) X break; X stack[depth = ++ptr] = scanp->p_pid; X printw(" %5d ", scanp->p_pid); X if (u->u_uid == ruid) X addstr(" "); X else X { X setpwent(); X if (pw = getpwuid(u->u_uid)) X printw("%-8.8s ", pw->pw_name); X else X printw("%-8d ", u->u_uid); X } X if (u->u_gid == rgid) X addstr(" "); X else X { X setgrent(); X if (gr = getgrgid(u->u_gid)) X printw("%-8.8s ", gr->gr_name); X else X printw("%-8d ", u->u_gid); X } X printw("%*s ", depth * 2, ""); X pcmd(u); X addch('\n'); X free(u); X } X refresh(); X sleep(5); X } X} X X/* X * print the command name and arguments (if possible) of a process X * for xenix, this prints u.u_psargs instead of groveling in core X */ X Xstatic Xvoid Xpcmd(uinfo) X struct user *uinfo; X{ X int c, d; X X for (c = PSARGSZ; c-- && uinfo->u_psargs[c] != '\0'; ) X ; X if (!c) X printw("(%.*s)", DIRSIZ, uinfo->u_comm); X else X { X if (c > COLS - stdscr->_curx - 1) X c = COLS - stdscr->_curx - 1; X printw("%.*s", c, uinfo->u_psargs); X for (d = 0; d < c && uinfo->u_psargs[d] != ' '; d++) X ; X uinfo->u_psargs[d] = '\0'; X for (; d >= 0 && uinfo->u_psargs[d] != '/'; d--) X ; X if (strncmp(uinfo->u_psargs + d + 1, uinfo->u_comm, DIRSIZ) != 0) X printw(" (%.*s)", DIRSIZ, uinfo->u_comm); X } X} X X/* X * print tty information for process; if process owner (pgrp) isn't logged in, X * return FALSE, else set *ruidp and *rgidp and return TRUE X */ X Xstatic Xint Xshowtty(p, u, ruidp, rgidp) X struct proc *p; X struct user *u; X int *ruidp, *rgidp; X{ X int isut; X struct tty t; X struct direct dev; X char *cp; X char ttyp[32], login[16]; X struct stat sb; X struct utmp ut; X struct tm *tp; X struct passwd *pw; X long now; X X lseek(kfd, (long) u->u_ttyp, 0); X read(kfd, &t, sizeof t); X sb.st_rdev = -1; X lseek(dfd, 0L, 0); X while (read(dfd, &dev, sizeof dev) == sizeof dev) X { X strcpy(ttyp, DEV); X strncat(ttyp, dev.d_name, DIRSIZ); X ttyp[DIRSIZ + sizeof DEV] = '\0'; X if (stat(ttyp, &sb) == -1 || (sb.st_mode & S_IFMT) != S_IFCHR) X continue; X if (sb.st_rdev == u->u_ttyd) X break; X } X if (sb.st_rdev != u->u_ttyd) X { X printw("(cdev %d/%d not found)", major(u->u_ttyd), minor(u->u_ttyd)); X return 0; X } X if (t.t_pgrp != p->p_pgrp) X { X printw(" %-8.8s old", dev.d_name); X return 0; X } X lseek(utmp, 0L, 0); X while (isut = (read(utmp, &ut, sizeof ut) == sizeof ut)) X if (ut.ut_type != USER_PROCESS) X continue; X else if (strncmp(ut.ut_line, dev.d_name, sizeof ut.ut_line) == 0) X break; X if (!isut || ut.ut_name[0] == '\0') X { X printw(" %-8.8s old", dev.d_name); X return 0; X } X printw("%-8.8s %-8.8s ", ut.ut_name, dev.d_name); X tp = localtime(&ut.ut_time); X time(&now); X if (now - ut.ut_time >= 86400) X printw(" %02d/%02d", tp->tm_mon + 1, tp->tm_mday); X else X printw(" %2d:%02d", tp->tm_hour, tp->tm_min); X if ((now -= (idlef? sb.st_mtime: sb.st_atime)) >= 86400) X printw(" %3dd", now / 86400); X else if (now >= 3600) X printw(" %2d:%02d", now / 3600, (now % 3600) / 60); X else if (now >= 60) X printw(" %3d", now / 60); X strncpy(login, ut.ut_name, sizeof ut.ut_name); X login[sizeof ut.ut_name] = '\0'; X move(stdscr->_cury, 40); X setpwent(); X if (!(pw = getpwnam(login))) X { X *ruidp = u->u_ruid; X *rgidp = u->u_rgid; X addstr("(user not in passwd file)\n"); X } X else X { X *ruidp = pw->pw_uid; X *rgidp = pw->pw_gid; X for (cp = pw->pw_gecos; *cp && *cp != ','; cp++) X ; X *cp = '\0'; X addstr(pw->pw_gecos); X addch('\n'); X } X return 1; X} X X/* X * determine "getty-ness" of a process X */ X Xisgetty(u) X struct proc *u; X{ X struct utmp ut; X X lseek(utmp, 0L, 0); X while (read(utmp, &ut, sizeof ut) == sizeof ut) X if (ut.ut_pid == u->p_pid) X return ut.ut_type == LOGIN_PROCESS; X return 0; X} ________This_Is_The_END________ if test `wc -c < si.c` -ne 11187; then echo 'shar: si.c was damaged during transit (should have been 11187 bytes)' fi fi ; : end of overwriting check echo 'x - si.man' if test -f si.man; then echo 'shar: not overwriting si.man'; else sed 's/^X//' << '________This_Is_The_END________' > si.man X.TH SI 1 "6 August 1989" X.SH NAME Xsi \- show system and user process trees X.SH SYNOPSIS X.B si X[ X.B -m X] X.SH DESCRIPTION X.I Si Xdisplays indented process trees for processes other than the kernel, X.IR init , Xand X.IR getty . XThe information displayed is the terminal name or X.BR "(no tty)" , Xthe logged-in user, Xlogin time, Xidle time and user name, Xand for each process the process ID, Xeffective user and group IDs if different from the logged-in user's IDs, Xand the process name and arguments (if available). XIf the process name is not available or differs from the exec name, Xthe exec name is displayed in parentheses. XProcesses are displayed in order by process group, Xand within each process group by parent/child relationship. XThe only option is X.BR "-m" , Xwhich causes X.I si Xto compute the idle time based on the tty's modification time Xrather than access time. XThis allows the user to select between the idle time displayed by X.I who Xor by X.IR w . X.PP XThe program is full-screen, Xusing X.I curses X(with X.IR M_TERMINFO ) Xto control screen updates. XIt has two interactive commands: Xpress X.B quit X(usually X.BR Control-\\ ) Xto exit, Xand X.B intr X(usually X.B DEL Xor X.BR Control-C ) Xto force the screen to be redrawn after line noise or an Xunexpected screen message. X.SH FILES X.nf X.ta 2i X/dev/kmem system tables X/dev/mem processes in core X/dev/swap processes on swap X.fi X.SH SEE ALSO Xwho(1), w(1), finger(1) X.SH AUTHOR X.nf XBrandon S. Allbery Xtdi2!brandon (first version) Xncoast!allbery (second version) Xallbery@NCoast.ORG (third version) Xnc386!allbery (fourth version) X.fi X.SH HISTORY XFirst written at Tridelta Industries (tdi2), for System V Release 2.2. XThis version displayed quite a bit more information than the current one; Xit may be resurrected in the future. X.PP XA vastly simplified version was implemented on ncoast (nc68k); Xmuch of the information in the original was not available under XSystem III, Xso the program was restricted to process trees of logged-in users. X.PP XA third version was implemented much later, Xalso under System III. XIt displayed process trees for detached processes as well, Xand did a much better job of sorting processes. X.PP XThe fourth version was written for Xenix V/386, Xand amounted to a port of the third version to that environment. X.SH BUGS XThe program truncates lines that will not fit across the screen; Xon an 80-column screen under Xenix 386, this rarely causes problems. XIt also omits lines which will not fit, Xwhich is somewhat more serious on busy systems. X.PP XOccasionally, Xa race condition will cause X.I si Xto hang instead of exiting or redrawing the screen. XThis is a side effect of the old V7 signal mechanism; Xif SCO UNIX 386 implements SVR3 (more) reliable signals, Xthis will go away. XEnter the command again to retain control of the program. X(This may cause X.I si Xto exit without restoring the terminal state.) ________This_Is_The_END________ if test `wc -c < si.man` -ne 2860; then echo 'shar: si.man was damaged during transit (should have been 2860 bytes)' fi fi ; : end of overwriting check exit 0 -- Brandon S. Allbery, moderator of comp.sources.misc allbery@uunet.uu.net Please send comp.sources.misc submissions to comp-sources-misc@, related mail to comp-sources-misc-request@, and personal mail ONLY to allbery@NCoast.ORG. You have only yourself to blame if you don't.