char *sccsid = "%W%"; /* * ps - process status * This is the augmented UCB ps for 2.9BSD PDP-11 Unix (11/82). * It is not very portable, using the phys sys call and * knowing the format of an a.out symbol table. * Examine and print certain things about processes * Usage: ps [ acgklnrtuwxU# ] [ corefile [ swapfile [ system ] ] ] */ #include "whoami.h" #include #include #include #include #include #include #include #include #include #include #include #include #define equal !strcmp #define exists(x) (stat ((x), &stbuf) == 0) #define within(x,y,z) (((unsigned)(x) >= (y)) && ((unsigned)(x) < (z))) #define round(x,y) ((long) ((((long) (x) + (long) (y) - 1L) / (long) (y)) * (long) (y))) struct nlist nl[] = { { "_proc" , 0, 0}, #define X_PROC 0 { "_swplo" , 0, 0}, #define X_SWPLO 1 { "_nproc", 0, 0}, #define X_NPROC 2 { "_hz", 0, 0}, #define X_HZ 3 0 }; #define NNAMESIZ (sizeof nl[0].n_name) struct proc *mproc, proc [8]; struct user u; struct stat stbuf; int hz; int chkpid = 0; int aflg; /* -a: all processes, not just mine */ int cflg; /* -c: not complete listing of args, just comm. */ int gflg; /* -g: complete listing including group headers, etc */ int kflg; /* -k: read from core file instead of real memory */ int lflg; /* -l: long listing form */ int nflg; /* -n: numeric wchans */ int rflg; /* -r: raw output in style */ int uflg; /* -u: user name */ int wflg; /* -w[w]: wide terminal */ int xflg; /* -x: ALL processes, even those without ttys */ int Uflg; /* -U: update the private list */ char *tptr, *mytty; char *nlistf, *uname; int file; off_t swplo; int nproc; off_t tell; int nchans; int ndev; int nsyms; int ismem; #ifndef PSFILE char *psdb = "/etc/psdatabase"; #else char *psdb = PSFILE; #endif /* * Structure for the unix wchan table */ struct wchan { char cname [NNAMESIZ]; unsigned caddr; }; struct wchan *wchanhd; char *calloc (), *malloc (), *realloc (); char *gettty (), *getptr (), *getchan (); char *ttyname (); int pscomp (); int wchancomp (); off_t lseek (); #ifndef NUMDEV #define NUMDEV 256 #endif struct devl { char dname[DIRSIZ]; dev_t dev; } devl[NUMDEV]; struct map { off_t b1, e1; off_t f1; off_t b2, e2; off_t f2; }; struct map datmap; struct psout *outargs; /* info for first npr processes */ int npr; /* number of processes found so far */ int twidth; /* terminal width */ int cmdstart; /* starting position for command field */ char *memf; /* name of kernel memory file to use */ char *kmemf; /* name of physical memory file to use */ char *swapf; /* name of swap file to use */ char *nlistf; /* name of symbol table file to use */ int kmem, mem, swap; extern char _sobuf[]; #ifdef TERMCAP char *getenv (); #endif main (argc, argv) char **argv; { int uid, euid, puid, nread; register i, j; char *ap; register struct proc *procp; #ifdef TERMCAP char *termp, capbuf [1024]; #endif #ifdef TERMCAP if ((termp = getenv ("TERM")) != (char *) NULL) if (tgetent (capbuf, termp) == 1) twidth = tgetnum ("co"); else ; else #endif twidth = 80; setbuf (stdout, _sobuf); argc--, argv++; if (argc > 0) { ap = argv [0]; while (*ap) switch (*ap++) { case '-': break; case 'a': aflg++; break; case 'c': cflg++; break; case 'g': gflg++; break; case 'k': kflg++; break; case 'l': lflg = 1; break; case 'n': nflg++; lflg = 1; break; case 'r': rflg++; break; case 't': if (*ap) tptr = ap; aflg++; gflg++; if (*tptr == '?') xflg++; while (*ap) ap++; break; case 'u': uflg = 1; break; case 'U': Uflg++; break; case 'w': if (twidth == 80) twidth = 132; else twidth = BUFSIZ; wflg++; break; case 'x': xflg++; break; default: if (!isdigit (ap[-1])) break; chkpid = atoi (--ap); *ap = '\0'; aflg++; xflg++; break; } } openfiles (argc, argv); getkvars (argc, argv); if (kflg) swplo = (off_t) 0; uid = getuid (); euid = geteuid (); mytty = ttyname (0); printhdr (); for (i = 0; i < nproc; i += 8) { j = nproc - i; if (j > 8) j = 8; j *= sizeof (struct proc); if ((nread = read (kmem, (char *) proc, j)) != j) { cantread ("proc table", kmemf); if (nread == -1) break; } for (j = nread / sizeof (struct proc) - 1; j >= 0; j--) { mproc = &proc[j]; procp = mproc; /* skip processes that don't exist */ if (procp->p_stat == 0) continue; /* skip those without a tty unless -x */ if (procp->p_pgrp == 0 && xflg == 0) continue; /* skip group leaders on a tty unless -g, -x, or -t.. */ #ifdef MENLO_JCL if (!tptr && !gflg && !xflg && procp->p_ppid == 1 && (procp->p_flag & SDETACH) == 0) #else if (!tptr && !gflg && !xflg && procp->p_pid == procp->p_pgrp) #endif continue; /* -g also skips those where **argv is "-" - see savcom */ puid = procp->p_uid; /* skip other peoples processes unless -a or a specific pid */ if ((uid != puid && euid != puid && aflg == 0) || (chkpid != 0 && chkpid != procp->p_pid)) continue; if (savcom (puid)) npr++; } } fixup (npr); for (i = 0; i < npr; i++) { #ifdef TERMCAP register cmdwidth = twidth - cmdstart - 2; #endif register struct psout *a = &outargs[i]; if (rflg) { if (write (1, (char *) a, sizeof (struct psout)) != sizeof (struct psout)) perror ("write"); continue; } else if (lflg) lpr (a); else if (uflg) upr (a); else spr (a); #ifdef TERMCAP if (cmdwidth < 0) cmdwidth = 80 - cmdstart - 2; #endif if (a->o_stat == SZOMB) #ifdef TERMCAP printf ("%.*s", cmdwidth, " "); #else printf (" "); #endif else if (a->o_pid == 0) #ifdef TERMCAP printf ("%.*s", cmdwidth, " swapper"); #else printf (" swapper"); #endif else printf (" %.*s", twidth - cmdstart - 2, cflg ? a->o_comm : a->o_args); putchar ('\n'); } exit (!npr); } /* * Getdev() changes directories so it should be * called only after other necessary files have * been opened. */ getdev () { register FILE *df; struct direct dbuf; register struct direct *dirp = &dbuf; if ((chdir ("/dev") < 0) || ((df = fopen (".", "r")) == NULL)) perrexit ("/dev"); while (fread ((char *) &dbuf, sizeof (dbuf), 1, df) == 1) { if (dirp->d_ino == 0) continue; if (!exists (dirp->d_name)) continue; if ((stbuf.st_mode&S_IFMT) != S_IFCHR) continue; strncpy (devl[ndev].dname, dirp->d_name, sizeof devl[ndev].dname); devl[ndev].dev = stbuf.st_rdev; ndev++; } fclose (df); } savcom (puid) { char *tp; off_t addr; #ifdef VIRUS_VFORK off_t daddr, saddr; #endif register struct psout *a; register struct proc *procp = mproc; register struct user *up = &u; long txtsiz, datsiz, stksiz; int septxt; if (procp->p_flag & SLOAD) { addr = ctob ((off_t) procp->p_addr); #ifdef VIRUS_VFORK daddr = ctob ((off_t) procp->p_daddr); saddr = ctob ((off_t) procp->p_saddr); #endif file = mem; } else { addr = (procp->p_addr + swplo) << 9; #ifdef VIRUS_VFORK daddr = (procp->p_daddr + swplo) << 9; saddr = (procp->p_saddr + swplo) << 9; #endif file = swap; } if (pread (file, (char *) up, sizeof (u), addr) != sizeof (u)) return (0); /* set up address maps for user pcs */ txtsiz = ctob (up->u_tsize); datsiz = ctob (up->u_dsize); stksiz = ctob (up->u_ssize); septxt = up->u_sep; datmap.b1 = (septxt ? 0 : round (txtsiz, TXTRNDSIZ)); datmap.e1 = datmap.b1 + datsiz; #ifdef VIRUS_VFORK datmap.f1 = daddr; #else datmap.f1 = ctob (USIZE) + addr; #endif datmap.b2 = stackbas (stksiz); datmap.e2 = stacktop (stksiz); #ifdef VIRUS_VFORK datmap.f2 = saddr; #else datmap.f2 = ctob (USIZE) + (datmap.e1 - datmap.b1) + addr; #endif tp = gettty (); if ((tptr && strcmp (tptr, tp)) || (strcmp (mytty, tp) && !aflg)) return (0); a = &outargs[npr]; /* saving com starts here */ a->o_uid = puid; a->o_pid = procp->p_pid; a->o_flag = procp->p_flag; a->o_ppid = procp->p_ppid; a->o_cpu = procp->p_cpu; a->o_pri = procp->p_pri; a->o_nice = procp->p_nice; a->o_addr0 = procp->p_addr; #ifdef VIRUS_VFORK a->o_size = ctod(procp->p_dsize + procp->p_ssize + USIZE); #else a->o_size = (procp->p_size + 7) >> 3; #endif a->o_wchan = procp->p_wchan; a->o_pgrp = procp->p_pgrp; strncpy (a->o_tty, tp, 8); a->o_ttyd = tp[0] == '?' ? -1 : up->u_ttyd; a->o_stat = procp->p_stat; a->o_flag = procp->p_flag; if (a->o_stat == SZOMB) return (1); a->o_utime = up->u_utime; a->o_stime = up->u_stime; a->o_cutime = up->u_cutime; a->o_cstime = up->u_cstime; a->o_sigs = (int)up->u_signal[SIGINT] + (int)up->u_signal[SIGQUIT]; a->o_uname[0] = 0; strncpy (a->o_comm, up->u_comm, 14); if (cflg) return (1); #ifdef VIRUS_VFORK else return (getcmd (a, saddr)); #else else return (getcmd (a, addr)); #endif } char * gettty () { register i; register char *p; if (u.u_ttyp == 0) return ("?"); for (i = 0; i < ndev; i++) { if (devl[i].dev == u.u_ttyd) { p = devl[i].dname; if (p[0] == 't' && p[1] == 't' && p[2] == 'y') p += 3; return (p); } } return ("?"); } char * ttyname (fd) { register i; register char *p; struct stat statb; if (fstat (fd, &statb)) return ("?"); for (i = 0; i < ndev; i++) { if (devl[i].dev == statb.st_rdev) { p = devl[i].dname; if (p[0] == 't' && p[1] == 't' && p[2] == 'y') p += 3; return (p); } } return ("?"); } /* * fixup figures out everybodys name and sorts into a nice order. */ fixup (np) register np; { register i; register struct passwd *pw; struct passwd *getpwent (); if (uflg) { /* * If we want names, traverse the password file. For each * passwd entry, look for it in the processes. * In case of multiple entries in /etc/passwd, we believe * the first one (same thing ls does). */ while ((pw = getpwent ()) != (struct passwd *) NULL) { for (i = 0; i < np; i++) if (outargs[i].o_uid == pw->pw_uid) { if (outargs[i].o_uname[0] == 0) strcpy (outargs[i].o_uname, pw->pw_name); } } } qsort (outargs, np, sizeof (outargs[0]), pscomp); } pscomp (x1, x2) register struct psout *x1, *x2; { register c; c = (x1)->o_ttyd - (x2)->o_ttyd; if (c == 0) c = (x1)->o_pid - (x2)->o_pid; return (c); } wchancomp (x1, x2) register struct wchan *x1, *x2; { if (x1->caddr > x2->caddr) return (1); else if (x1->caddr == x2->caddr) return (0); else return (-1); } char * getptr (adr) char **adr; { char *ptr; register char *p, *pa; register i; ptr = 0; pa = (char *)adr; p = (char *)&ptr; for (i = 0; i < sizeof (ptr); i++) *p++ = getbyte (pa++); return (ptr); } getbyte (adr) register char *adr; { register struct map *amap = &datmap; char b; off_t saddr; if (!within (adr, amap->b1, amap->e1)) if (within (adr, amap->b2, amap->e2)) saddr = (unsigned) adr + amap->f2 - amap->b2; else return (0); else saddr = (unsigned) adr + amap->f1 - amap->b1; if (lseek (file, saddr, 0) == (off_t) -1 || read (file, &b, 1) < 1) return (0); return ((unsigned) b); } /* * pread is like read, but if it's /dev/mem we use the phys * system call for speed. On systems without phys we have * to use regular read. */ pread (fd, ptr, nbytes, loc) char *ptr; off_t loc; { register rc; if (fd == mem && ismem) { rc = phys (6, nbytes / 64 + 1, (short) (loc / 64)); if (rc >= 0) { bmove (0140000, ptr, nbytes); return (nbytes); } } lseek (fd, loc, 0); return (read (fd, ptr, nbytes)); } addchan (name, caddr) char *name; unsigned caddr; { register nc = nchans; register struct wchan *wp = wchanhd; if (nc == 0) wp = (struct wchan *) malloc (sizeof (struct wchan)); else wp = (struct wchan *) realloc (wp, sizeof (struct wchan) * (nc + 1)); if (wp == (struct wchan *) NULL) err ("Too many symbols"); strncpy (wp[nc].cname, name, NNAMESIZ - 1); wp[nc].cname[NNAMESIZ-1] = '\0'; wp[nc].caddr = caddr; wchanhd = wp; nchans++; } char * getchan (chan) register unsigned chan; { register i; register char *prevsym; prevsym = ""; if (chan) for (i = 0; i < nchans; i++) { if (wchanhd[i].caddr > chan) return (prevsym); prevsym = wchanhd[i].cname; } return (prevsym); } nlist (aout, tgt) char *aout; struct nlist *tgt; { register i; register FILE *aaa; register struct nlist *nnn; int iii, nllen; struct ovlhdr ovlbuf; static struct nlist nbuf; static struct exec hbuf; off_t sa = (off_t) 0; nllen = sizeof nl / sizeof (struct nlist); if ((aaa = fopen (aout, "r")) == 0 ) perrexit (aout); if (fread (&hbuf, sizeof hbuf, 1, aaa) != 1 ) err ("Invalid symbol table"); if (N_BADMAG (hbuf)) { fprintf (stderr, "%s: not in object file format\n", aout); exit (1); } if (hbuf.a_magic == A_MAGIC5 || hbuf.a_magic == A_MAGIC6) { fread ((char *) &ovlbuf, 1, sizeof ovlbuf, aaa); for (i = 0; i < NOVL; i++) sa += (off_t) (ovlbuf.ov_siz)[i]; fseek (aaa, sa, 1); } sa = (off_t) hbuf.a_text + hbuf.a_data; if ((hbuf.a_flag & 01) == 0) sa *= (off_t) 2; fseek (aaa, sa, 1); nsyms = hbuf.a_syms / sizeof nbuf; if (nsyms == 0) { fprintf (stderr, "%s: no symbol table\n", aout); exit (1); } while (--nsyms >= 0 && fread (&nbuf, sizeof (nbuf), 1, aaa) == 1 ) { if (nbuf.n_name[0] != '_' ) continue; iii = nbuf.n_type&(N_TYPE|N_EXT); if ((nbuf.n_type&N_TYPE) != N_ABS && iii != (N_EXT|N_DATA) && iii != (N_EXT|N_BSS) ) continue; if (!nflg) addchan (nbuf.n_name+1, (unsigned) (nbuf.n_value)); if (nllen) for (nnn = tgt; nnn->n_name[0] != '\0'; nnn++ ) if (!strncmp (nnn->n_name, nbuf.n_name, NNAMESIZ)) { nnn->n_value = nbuf.n_value; nnn->n_type = nbuf.n_type; nllen--; break; } } fclose (aaa); if (!nflg) qsort (wchanhd, nchans, sizeof (struct wchan), wchancomp); return (nllen == 0 ? 0 : -1); } err (msg) char *msg; { fprintf (stderr, "%s\n", msg); exit (1); } perrexit (msg) char *msg; { perror (msg); exit (1); } writepsdb (unixname) char *unixname; { int nllen; register FILE *fp; setuid (getuid ()); if ((fp = fopen (psdb, "w")) == NULL) perrexit (psdb); else chmod (psdb, 0644); nllen = sizeof nl / sizeof (struct nlist); fwrite (unixname, strlen (unixname) + 1, 1, fp); fwrite ((char *) &nllen, sizeof nllen, 1, fp); fwrite ((char *) &ndev, sizeof ndev, 1, fp); fwrite ((char *) &nchans, sizeof nchans, 1, fp); fwrite ((char *) nl, sizeof (struct nlist), nllen, fp); fwrite ((char *) devl, sizeof (struct devl), ndev, fp); fwrite ((char *) wchanhd, sizeof (struct wchan), nchans, fp); fclose (fp); } char * readpsdb () { int nllen; register i; register FILE *fp; static char unamebuf [BUFSIZ]; char *p = unamebuf; register struct wchan *ccc; if ((fp = fopen (psdb, "r")) == NULL) perrexit (psdb); while ((*p= getc (fp)) != '\0') p++; fread (&nllen, sizeof nllen, 1, fp); fread (&ndev, sizeof ndev, 1, fp); fread (&nchans, sizeof nchans, 1, fp); fread (nl, sizeof (struct nlist), nllen, fp); fread (devl, sizeof (struct devl), ndev, fp); if (!nflg) if ((wchanhd = calloc (nchans, sizeof (struct wchan))) == (struct wchan *) NULL) err ("Too many symbols"); else for (i = 0, ccc = wchanhd; i < nchans; i++) { fread ((char *) ccc, sizeof (struct wchan), 1, fp); ccc++; } return (unamebuf); } openfiles (argc, argv) char **argv; { kmemf = "/dev/kmem"; if (kflg) kmemf = argc > 1 ? argv[1] : "/usr/sys/core"; kmem = open (kmemf, 0); if (kmem < 0) perrexit (kmemf); if (!kflg) { memf = "/dev/mem"; ismem++; } else memf = kmemf; mem = open (memf, 0); if (mem < 0) perrexit (memf); swapf = argc > 2 ? argv[2] : "/dev/swap"; swap = open (swapf, 0); if (swap < 0) perrexit (swapf); } getkvars (argc, argv) char **argv; { nlistf = argc > 3 ? argv[3] : "/unix"; if (Uflg) { nlist (nlistf, nl); getdev (); writepsdb (nlistf); exit (0); } else if (exists (psdb)) { uname = readpsdb (); if (!equal (uname, nlistf)) { /* * Let addchan() do the work. */ nchans = 0; free ((char *) wchanhd); nlist (nlistf, nl); } } else nlist (nlistf, nl), getdev (); /* * Find base of swap */ lseek (kmem, (off_t) nl[X_SWPLO].n_value, 0); if (read (kmem, (char *) &swplo, sizeof (swplo)) != sizeof (swplo)) cantread ("swplo", kmemf); /* * Find number of procs */ if (nl[X_NPROC].n_value) { lseek (kmem, (off_t) nl[X_NPROC].n_value, 0); if (read(kmem, (char *)&nproc, sizeof(nproc)) != sizeof(nproc)){ perror(kmemf); exit(1); } } else { fprintf(stderr, "nproc not in namelist\n"); exit(1); } outargs = (struct psout *)calloc(nproc, sizeof(struct psout)); if (outargs == (struct psout *)NULL) { fprintf(stderr, "ps: not enough memory for saving info\n"); exit(1); } /* * Find value of hz */ lseek (kmem, (off_t) nl[X_HZ].n_value, 0); read (kmem, (char *) &hz, sizeof hz); /* * Locate proc table */ lseek (kmem, (off_t) nl[X_PROC].n_value, 0); tell = (off_t) nl[X_PROC].n_value; } char *uhdr = "USER PID NICE SZ TTY TIME"; upr (a) register struct psout *a; { printf ("%-8.8s", a->o_uname); printf ("%6u", a->o_pid); printf ("%4d ", a->o_nice); printf ("%4d", a->o_size); ptty (a->o_tty); ptime (a); } char *shdr = " PID TTY TIME"; spr (a) register struct psout *a; { printf ("%6u", a->o_pid); ptty (a->o_tty); ptime (a); } char *lhdr = " F S UID PID PPID CPU PRI NICE ADDR SZ WCHAN TTY TIME"; lpr (a) register struct psout *a; { printf ("%3o %c", 0377 & a->o_flag, "0SWRIZT"[a->o_stat]); printf ("%6u", a->o_uid); printf ("%6u", a->o_pid); printf ("%6u%4d%4d%4d%7o", a->o_ppid, a->o_cpu&0377, a->o_pri, a->o_nice, a->o_addr0); printf ("%4d", a->o_size); if (nflg) if (a->o_wchan) printf ("%7o", a->o_wchan); else printf (" "); else printf (" %-6.6s", getchan (a->o_wchan)); ptty (a->o_tty); ptime (a); } ptty (tp) char *tp; { printf (" %-8.8s", tp); } ptime (a) register struct psout *a; { time_t tm; tm = (a->o_utime + a->o_stime + 30) / hz; printf ("%3ld:", tm / 60); tm %= 60; printf (tm < 10 ? "0%ld" : "%ld", tm); } cantread (what, fromwhat) char *what, *fromwhat; { fprintf (stderr, "ps: error reading %s from %s\n", what, fromwhat); } getcmd (a, addr) off_t addr; register struct psout *a; { /* amount of top of stack to examine for args */ #define ARGLIST (1024/sizeof(int)) register *ip; register char *cp, *cp1; char c; char **ap; int cc, nbad, abuf [ARGLIST]; a->o_args[0] = 0; /* in case of early return */ #ifdef VIRUS_VFORK addr += ctob ((off_t) mproc->p_ssize) - ARGLIST*sizeof(int); #else addr += ctob ((off_t) mproc->p_size) - ARGLIST*sizeof(int); #endif /* look for sh special */ lseek (file, addr + ARGLIST*sizeof(int) - sizeof (char **), 0); if (read (file, (char *) &ap, sizeof (char *)) != sizeof (char *)) return (1); if (ap) { char b[82]; char *bp = b; while ((cp = getptr (ap++)) && cp && (bp < b+sizeof (a->o_args)) ) { nbad = 0; while ((c = getbyte (cp++)) && (bp < b+sizeof (a->o_args))) { if (c<' ' || c > '~') { if (nbad++ > 3) break; continue; } *bp++ = c; } *bp++ = ' '; } *bp++ = 0; strcpy (a->o_args, b); return (1); } if (pread (file, (char *) abuf, sizeof (abuf), addr) != sizeof (abuf)) return (1); abuf[ARGLIST-1] = 0; for (ip = &abuf[ARGLIST-2]; ip > abuf;) { if (*--ip == -1 || *ip == 0) { cp = (char *) (ip + 1); if (*cp == '\0') cp++; nbad = 0; for (cp1 = cp; cp1 < (char *) &abuf[ARGLIST]; cp1++) { cc = *cp1 & 0177; if (cc == 0) *cp1 = ' '; else if (cc < ' ' || cc > 0176) { if (++nbad >= 5) { *cp1++ = ' '; break; } *cp1 = '?'; } else if (cc == '=') { *cp1 = '\0'; while (cp1 > cp && *--cp1 != ' ') *cp1 = '\0'; break; } } while (*--cp1 == ' ') *cp1 = 0; strcpy (a->o_args, cp); garbage: cp = a->o_args; if (cp[0] == '-' && cp[1] <= ' ' || cp[0] == '?' || cp[0] <= ' ') { strcat (cp, " ("); strcat (cp, u.u_comm); strcat (cp, ")"); } cp[63] = 0; /* max room in psout is 64 chars */ if (xflg || gflg || tptr || cp[0] != '-') return (1); return (0); } } goto garbage; } printhdr () { register char *hdr; #ifdef TERMCAP register char *cmdstr = " COMMAND"; #endif if (rflg) return; if (lflg && uflg) err ("ps: specify only one of l and u"); hdr = lflg ? lhdr : (uflg ? uhdr : shdr); cmdstart = strlen (hdr); #ifdef TERMCAP if (cmdstart + strlen (cmdstr) >= twidth) cmdstr = " CMD"; printf ("%s%s\n", hdr, cmdstr); #else printf ("%s COMMAND\n", hdr); #endif fflush (stdout); }