1: /*
2: * w - print system status (who and what)
3: *
4: * This program is similar to the systat command on Tenex/Tops 10/20
5: * It needs read permission on /dev/mem and /dev/swap.
6: *
7: * PDP-11 V7 version that does not run off ps -r.
8: */
9: #include <whoami.h>
10: #include <a.out.h>
11: #include <core.h>
12: #include <stdio.h>
13: #include <ctype.h>
14: #include <utmp.h>
15: #include <time.h>
16: #include <sys/param.h>
17: #include <sys/stat.h>
18: #include <sys/proc.h>
19: #include <sys/dir.h>
20: #include <sys/user.h>
21: #include <sys/tty.h>
22:
23: #define ARGWIDTH 33 /* # chars left on 80 col crt for args */
24: #define ARGLIST 1024 /* amount of stack to examine for argument list */
25:
26: struct smproc {
27: long w_addr; /* address in file for args */
28: short w_pid; /* proc.p_pid */
29: int w_igintr; /* INTR+3*QUIT, 0=die, 1=ign, 2=catch */
30: time_t w_time; /* CPU time used by this process */
31: time_t w_ctime; /* CPU time used by children */
32: dev_t w_tty; /* tty device of process */
33: char w_comm[15]; /* user.u_comm, null terminated */
34: char w_args[ARGWIDTH+1]; /* args if interesting process */
35: } *pr;
36:
37: struct nlist nl[] = {
38: { "_proc" },
39: #define X_PROC 0
40: { "_swapdev" },
41: #define X_SWAPDEV 1
42: { "_swplo" },
43: #define X_SWPLO 2
44: { "_avenrun" },
45: #define X_AVENRUN 3
46: { "_bootime" },
47: #define X_BOOTIME 4
48: { "_nproc" },
49: #define X_NPROC 5
50: { 0 },
51: };
52:
53: FILE *ps;
54: FILE *ut;
55: FILE *bootfd;
56: int swmem;
57: int mem;
58: int swap; /* /dev/mem, mem, and swap */
59: int nswap;
60: int file;
61: dev_t tty;
62: char doing[520]; /* process attached to terminal */
63: time_t proctime; /* cpu time of process in doing */
64: short avenrun[3];
65: double load[3];
66:
67: #define DIV60(t) ((t+30)/60) /* x/60 rounded */
68: #define TTYEQ (tty == pr[i].w_tty)
69: #define IGINT (1+3*1) /* ignoring both SIGINT & SIGQUIT */
70:
71: long round();
72: char *getargs();
73: char *fread();
74: char *ctime();
75: char *rindex();
76: char *getptr();
77: FILE *popen();
78: struct tm *localtime();
79:
80: int debug; /* true if -d flag: debugging output */
81: int = 1; /* true if -h flag: don't print heading */
82: int lflag = 1; /* true if -l flag: long style output */
83: int login; /* true if invoked as login shell */
84: time_t idle; /* number of minutes user is idle */
85: int nusers; /* number of users logged in now */
86: char * sel_user; /* login of particular user selected */
87: char firstchar; /* first char of name of prog invoked as */
88: time_t jobtime; /* total cpu time visible */
89: time_t now; /* the current time of day */
90: struct tm *nowt; /* current time as time struct */
91: time_t bootime, uptime; /* time of last reboot & elapsed time since */
92: int np; /* number of processes currently active */
93: struct utmp utmp;
94: struct proc mproc;
95: struct user up;
96: char fill[512];
97:
98: struct map {
99: long b1, e1; long f1;
100: long b2, e2; long f2;
101: };
102: struct map datmap;
103:
104: main(argc, argv)
105: char **argv;
106: {
107: int days, hrs, mins;
108: register int i, j;
109: char *cp;
110: register int curpid, empty;
111: extern char _sobuf[];
112:
113: setbuf(stdout, _sobuf);
114: login = (argv[0][0] == '-');
115: cp = rindex(argv[0], '/');
116: firstchar = login ? argv[0][1] : (cp==0) ? argv[0][0] : cp[1];
117: cp = argv[0]; /* for Usage */
118:
119: while (argc > 1) {
120: if (argv[1][0] == '-') {
121: for (i=1; argv[1][i]; i++) {
122: switch(argv[1][i]) {
123:
124: case 'd':
125: debug++;
126: break;
127:
128: case 'h':
129: header = 0;
130: break;
131:
132: case 'l':
133: lflag++;
134: break;
135:
136: case 's':
137: lflag = 0;
138: break;
139:
140: case 'u':
141: case 'w':
142: firstchar = argv[1][1];
143: break;
144:
145: default:
146: printf("Bad flag %s\n", argv[1]);
147: exit(1);
148: }
149: }
150: } else {
151: if (!isalnum(argv[1][0]) || argc > 2) {
152: printf("Usage: %s [ -hlsuw ] [ user ]\n", cp);
153: exit(1);
154: } else
155: sel_user = argv[1];
156: }
157: argc--; argv++;
158: }
159:
160: if ((mem = open("/dev/kmem", 0)) < 0) {
161: fprintf(stderr, "No mem\n");
162: exit(1);
163: }
164: nlist("/unix", nl);
165: if (nl[0].n_type==0) {
166: fprintf(stderr, "No namelist\n");
167: exit(1);
168: }
169:
170: if (firstchar != 'u')
171: readpr();
172:
173: ut = fopen("/etc/utmp","r");
174: if (header) {
175: /* Print time of day */
176: time(&now);
177: nowt = localtime(&now);
178: prtat(nowt);
179:
180: if (nl[X_BOOTIME].n_type > 0) {
181: /*
182: * Print how long system has been up.
183: * (Found by looking for "bootime" in kernel)
184: */
185: lseek(mem, (long)nl[X_BOOTIME].n_value, 0);
186: read(mem, &bootime, sizeof (bootime));
187:
188: uptime = now - bootime;
189: days = uptime / (60L*60L*24L);
190: uptime %= (60L*60L*24L);
191: hrs = uptime / (60L*60L);
192: uptime %= (60L*60L);
193: mins = DIV60(uptime);
194:
195: printf(" up");
196: if (days > 0)
197: printf(" %d day%s,", days, days>1?"s":"");
198: if (hrs > 0 && mins > 0) {
199: printf(" %2d:%02d,", hrs, mins);
200: } else {
201: if (hrs > 0)
202: printf(" %d hr%s,", hrs, hrs>1?"s":"");
203: if (mins > 0)
204: printf(" %d min%s,", mins, mins>1?"s":"");
205: }
206: }
207:
208: /* Print number of users logged in to system */
209: while (fread(&utmp, sizeof(utmp), 1, ut)) {
210: if (utmp.ut_name[0] != '\0')
211: nusers++;
212: }
213: rewind(ut);
214: printf(" %d user%c", nusers, nusers > 1 ? 's' : '\0');
215:
216: if (nl[X_AVENRUN].n_type > 0) {
217: /*
218: * Print 1, 5, and 15 minute load averages.
219: * (Found by looking in kernel for avenrun).
220: */
221: printf(", load average:");
222: lseek(mem, (long)nl[X_AVENRUN].n_value, 0);
223: read(mem, avenrun, sizeof(avenrun));
224: for (i = 0; i < (sizeof(avenrun)/sizeof(avenrun[0])); i++) {
225: load[i] = avenrun[i] / 256.0;
226: if (i > 0)
227: printf(",");
228: printf(" %.2f", load[i]);
229: }
230: }
231: printf("\n");
232: if (firstchar == 'u')
233: exit(0);
234:
235: /* Headers for rest of output */
236: if (lflag)
237: printf("User tty login@ idle JCPU PCPU what\n");
238: else
239: printf("User tty idle what\n");
240: fflush(stdout);
241: }
242:
243:
244: for (;;) { /* for each entry in utmp */
245: if (fread(&utmp, sizeof(utmp), 1, ut) == NULL) {
246: fclose(ut);
247: exit(0);
248: }
249: if (utmp.ut_name[0] == '\0')
250: continue; /* that tty is free */
251: if (sel_user && strncmp(utmp.ut_name, sel_user, 8) != 0)
252: continue; /* we wanted only somebody else */
253:
254: gettty();
255: jobtime = 0;
256: proctime = 0;
257: strcpy(doing, "-"); /* default act: normally never prints */
258: empty = 1;
259: curpid = -1;
260: idle = findidle();
261: for (i=0; i<np; i++) { /* for each process on this tty */
262: if (!(TTYEQ))
263: continue;
264: jobtime += pr[i].w_time + pr[i].w_ctime;
265: proctime += pr[i].w_time;
266: if (debug) {
267: printf("\t\t%d\t%s", pr[i].w_pid, pr[i].w_args);
268: if ((j=pr[i].w_igintr) > 0)
269: if (j==IGINT)
270: printf(" &");
271: else
272: printf(" & %d %d", j%3, j/3);
273: printf("\n");
274: }
275: if (empty && pr[i].w_igintr!=IGINT) {
276: empty = 0;
277: curpid = -1;
278: }
279: if(pr[i].w_pid>curpid && (pr[i].w_igintr!=IGINT || empty)){
280: curpid = pr[i].w_pid;
281: strcpy(doing, lflag ? pr[i].w_args : pr[i].w_comm);
282: if (doing[0]==0 || doing[0]=='-' && doing[1]<=' ' || doing[0] == '?') {
283: strcat(doing, " (");
284: strcat(doing, pr[i].w_comm);
285: strcat(doing, ")");
286: }
287: }
288: }
289: putline();
290: }
291: }
292:
293: /* figure out the major/minor device # pair for this tty */
294: gettty()
295: {
296: char ttybuf[20];
297: struct stat statbuf;
298:
299: ttybuf[0] = 0;
300: strcpy(ttybuf, "/dev/");
301: strcat(ttybuf, utmp.ut_line);
302: stat(ttybuf, &statbuf);
303: tty = statbuf.st_rdev;
304: }
305:
306: /*
307: * putline: print out the accumulated line of info about one user.
308: */
309: putline()
310: {
311: register int tm;
312:
313: /* print login name of the user */
314: printf("%-8.8s ", utmp.ut_name);
315:
316: /* print tty user is on */
317: if (lflag)
318: /* long form: all (up to) 8 chars */
319: printf("%-8.8s", utmp.ut_line);
320: else {
321: /* short form: 4 chars, skipping 'tty' if there */
322: if (utmp.ut_line[0]=='t' && utmp.ut_line[1]=='t' && utmp.ut_line[2]=='y')
323: printf("%-4.4s", &utmp.ut_line[3]);
324: else
325: printf("%-4.4s", utmp.ut_line);
326: }
327:
328: if (lflag)
329: /* print when the user logged in */
330: prtat(localtime(&utmp.ut_time));
331:
332: /* print idle time */
333: prttime(idle," ");
334:
335: if (lflag) {
336: /* print CPU time for all processes & children */
337: prttime(DIV60(jobtime)," ");
338: /* print cpu time for interesting process */
339: prttime(DIV60(proctime)," ");
340: }
341:
342: /* what user is doing, either command tail or args */
343: printf(" %-.32s\n",doing);
344: fflush(stdout);
345: }
346:
347: /* find & return number of minutes current tty has been idle */
348: findidle()
349: {
350: struct stat stbuf;
351: long lastaction, diff;
352: char ttyname[20];
353:
354: strcpy(ttyname, "/dev/");
355: strncat(ttyname, utmp.ut_line, 8);
356: stat(ttyname, &stbuf);
357: time(&now);
358: lastaction = stbuf.st_atime;
359: diff = now - lastaction;
360: diff = DIV60(diff);
361: if (diff < 0) diff = 0;
362: return(diff);
363: }
364:
365: /*
366: * prttime prints a time in hours and minutes.
367: * The character string tail is printed at the end, obvious
368: * strings to pass are "", " ", or "am".
369: */
370: prttime(tim, tail)
371: time_t tim;
372: char *tail;
373: {
374: register int didhrs = 0;
375:
376: if (tim >= 60) {
377: printf("%3ld:", tim/60);
378: didhrs++;
379: } else {
380: printf(" ");
381: }
382: tim %= 60;
383: if (tim > 0 || didhrs) {
384: printf(didhrs&&tim<10 ? "%02ld" : "%2ld", tim);
385: } else {
386: printf(" ");
387: }
388: printf("%s", tail);
389: }
390:
391: /* prtat prints a 12 hour time given a pointer to a time of day */
392: prtat(p)
393: struct tm *p;
394: {
395: register int pm;
396: register time_t t;
397:
398: t = p -> tm_hour;
399: pm = (t > 11);
400: if (t > 11)
401: t -= 12;
402: if (t == 0)
403: t = 12;
404: prttime(t*60 + p->tm_min, pm ? "pm" : "am");
405: }
406:
407: /*
408: * readpr finds and reads in the array pr, containing the interesting
409: * parts of the proc and user tables for each live process.
410: */
411: readpr()
412: {
413: int pn, mf, c, nproc;
414: int szpt, pfnum, i;
415: long addr;
416: #ifdef VIRUS_VFORK
417: long daddr, saddr;
418: #endif
419: daddr_t swplo;
420: long txtsiz, datsiz, stksiz;
421: int septxt;
422:
423: if((swmem = open("/dev/mem", 0)) < 0) {
424: perror("/dev/mem");
425: exit(1);
426: }
427: if ((swap = open("/dev/swap", 0)) < 0) {
428: perror("/dev/swap");
429: exit(1);
430: }
431: /*
432: * read mem to find swap dev.
433: */
434: lseek(mem, (long)nl[X_SWAPDEV].n_value, 0);
435: read(mem, &nl[X_SWAPDEV].n_value, sizeof(nl[X_SWAPDEV].n_value));
436: /*
437: * Find base of swap
438: */
439: lseek(mem, (long)nl[X_SWPLO].n_value, 0);
440: read(mem, &swplo, sizeof(swplo));
441: if (nl[X_NPROC].n_value == 0) {
442: fprintf(stderr, "nproc not in namelist\n");
443: exit(1);
444: }
445: lseek (mem, (off_t) nl[X_NPROC].n_value, 0);
446: read(mem, (char *)&nproc, sizeof(nproc));
447: pr = (struct smproc *) malloc(nproc * sizeof(struct smproc));
448: if (pr == (struct smproc *)NULL) {
449: fprintf("Not enough memory for proc table\n");
450: exit(1);
451: }
452: /*
453: * Locate proc table
454: */
455: np = 0;
456: for (pn=0; pn<nproc; pn++) {
457: lseek(mem, (long)(nl[X_PROC].n_value + pn*(sizeof mproc)), 0);
458: pread(mem, &mproc, sizeof mproc, (long)(nl[X_PROC].n_value + pn*(sizeof mproc)));
459: /* decide if it's an interesting process */
460: if (mproc.p_stat==0 || mproc.p_pgrp==0)
461: continue;
462:
463: #ifdef notdef
464: /*
465: * The following improves speed on systems with lots of ttys
466: * by skipping gettys and inits, but loses when root logs in.
467: */
468: if (mproc.p_ppid == 1 && mproc.p_uid == 0)
469: continue;
470: #endif
471: /* find & read in the user structure */
472: if (mproc.p_flag&SLOAD) {
473: addr = ctob((long)mproc.p_addr);
474: #ifdef VIRUS_VFORK
475: daddr = ctob((long)mproc.p_daddr);
476: saddr = ctob((long)mproc.p_saddr);
477: #endif
478: file = swmem;
479: } else {
480: addr = (mproc.p_addr+swplo)<<9;
481: #ifdef VIRUS_VFORK
482: daddr = (mproc.p_daddr+swplo)<<9;
483: saddr = (mproc.p_saddr+swplo)<<9;
484: #endif
485: file = swap;
486: }
487: lseek(file, addr, 0);
488: if (pread(file, (char *)&up, sizeof(up), addr) != sizeof(up))
489: continue;
490: if (up.u_ttyp == NULL)
491: continue;
492:
493: /* set up address maps for user pcs */
494: txtsiz = ctob(up.u_tsize);
495: datsiz = ctob(up.u_dsize);
496: stksiz = ctob(up.u_ssize);
497: septxt = up.u_sep;
498: datmap.b1 = (septxt ? 0 : round(txtsiz,TXTRNDSIZ));
499: datmap.e1 = datmap.b1+datsiz;
500: #ifdef VIRUS_VFORK
501: datmap.f1 = daddr;
502: #else
503: datmap.f1 = ctob(USIZE)+addr;
504: #endif
505: datmap.b2 = stackbas(stksiz);
506: datmap.e2 = stacktop(stksiz);
507: #ifdef VIRUS_VFORK
508: datmap.f2 = saddr;
509: #else
510: datmap.f2 = ctob(USIZE)+(datmap.e1-datmap.b1)+addr;
511: #endif
512:
513: /* save the interesting parts */
514: #ifdef VIRUS_VFORK
515: pr[np].w_addr = saddr + ctob((long)mproc.p_ssize) - ARGLIST;
516: #else
517: pr[np].w_addr = addr + ctob((long)mproc.p_size) - ARGLIST;
518: #endif
519: pr[np].w_pid = mproc.p_pid;
520: pr[np].w_igintr = ((up.u_signal[2]==1) + 2*(up.u_signal[2]>1) + 3*(up.u_signal[3]==1)) + 6*(up.u_signal[3]>1);
521: pr[np].w_time = up.u_utime + up.u_stime;
522: pr[np].w_ctime = up.u_cutime + up.u_cstime;
523: pr[np].w_tty = up.u_ttyd;
524: up.u_comm[14] = 0; /* Bug: This bombs next field. */
525: strcpy(pr[np].w_comm, up.u_comm);
526: /*
527: * Get args if there's a chance we'll print it.
528: * Cant just save pointer: getargs returns static place.
529: * Cant use strncpy: that crock blank pads.
530: */
531: pr[np].w_args[0] = 0;
532: strncat(pr[np].w_args,getargs(&pr[np]),ARGWIDTH);
533: if (pr[np].w_args[0]==0 || pr[np].w_args[0]=='-' && pr[np].w_args[1]<=' ' || pr[np].w_args[0] == '?') {
534: strcat(pr[np].w_args, " (");
535: strcat(pr[np].w_args, pr[np].w_comm);
536: strcat(pr[np].w_args, ")");
537: }
538: np++;
539: }
540: }
541:
542: /*
543: * getargs: given a pointer to a proc structure, this looks at the swap area
544: * and tries to reconstruct the arguments. This is straight out of ps.
545: */
546: char *
547: getargs(p)
548: struct smproc *p;
549: {
550: int c, nbad;
551: static char abuf[ARGLIST];
552: register int *ip;
553: register char *cp, *cp1;
554: char **ap;
555: long addr;
556:
557: addr = p->w_addr;
558:
559: /* look for sh special */
560: lseek(file, addr+ARGLIST-sizeof(char **), 0);
561: if (read(file, (char *)&ap, sizeof(char *)) != sizeof(char *))
562: return(NULL);
563: if (ap) {
564: char *b = (char *) abuf;
565: char *bp = b;
566: while((cp=getptr(ap++)) && cp && (bp<b+ARGWIDTH) ) {
567: nbad = 0;
568: while((c=getbyte(cp++)) && (bp<b+ARGWIDTH)) {
569: if (c<' ' || c>'~') {
570: if (nbad++>3)
571: break;
572: continue;
573: }
574: *bp++ = c;
575: }
576: *bp++ = ' ';
577: }
578: *bp++ = 0;
579: return(b);
580: }
581:
582: lseek(file, addr, 0);
583: if (pread(file, abuf, sizeof(abuf), addr) != sizeof(abuf))
584: return(1);
585: for (ip = (int *) &abuf[ARGLIST]-2; ip > (int *) abuf;) {
586: /* Look from top for -1 or 0 as terminator flag. */
587: if (*--ip == -1 || *ip == 0) {
588: cp = (char *)(ip+1);
589: if (*cp==0)
590: cp++;
591: nbad = 0; /* up to 5 funny chars as ?'s */
592: for (cp1 = cp; cp1 < (char *)&abuf[ARGLIST]; cp1++) {
593: c = *cp1&0177;
594: if (c==0) /* nulls between args => spaces */
595: *cp1 = ' ';
596: else if (c < ' ' || c > 0176) {
597: if (++nbad >= 5) {
598: *cp1++ = ' ';
599: break;
600: }
601: *cp1 = '?';
602: } else if (c=='=') { /* Oops - found an
603: * environment var, back
604: * over & erase it. */
605: *cp1 = 0;
606: while (cp1>cp && *--cp1!=' ')
607: *cp1 = 0;
608: break;
609: }
610: }
611: while (*--cp1==' ') /* strip trailing spaces */
612: *cp1 = 0;
613: return(cp);
614: }
615: }
616: return (p->w_comm);
617: }
618:
619: min(a, b)
620: {
621:
622: return (a < b ? a : b);
623: }
624:
625: char *
626: getptr(adr)
627: char **adr;
628: {
629: char *ptr;
630: register char *p, *pa;
631: register i;
632:
633: ptr = 0;
634: pa = (char *)adr;
635: p = (char *)&ptr;
636: for (i=0; i<sizeof(ptr); i++)
637: *p++ = getbyte(pa++);
638: return(ptr);
639: }
640:
641: getbyte(adr)
642: char *adr;
643: {
644: register struct map *amap = &datmap;
645: char b;
646: long saddr;
647:
648: if(!within(adr, amap->b1, amap->e1)) {
649: if(within(adr, amap->b2, amap->e2)) {
650: saddr = (unsigned)adr + amap->f2 - amap->b2;
651: } else
652: return(0);
653: } else
654: saddr = (unsigned)adr + amap->f1 - amap->b1;
655: if(lseek(file, saddr, 0)==-1
656: || read(file, &b, 1)<1) {
657: return(0);
658: }
659: return((unsigned)b);
660: }
661:
662:
663: within(adr,lbd,ubd)
664: char *adr;
665: long lbd, ubd;
666: {
667: return((unsigned)adr>=lbd && (unsigned)adr<ubd);
668: }
669:
670: long
671: round(a, b)
672: long a, b;
673: {
674: long w = ((a+b-1)/b)*b;
675:
676: return(w);
677: }
678:
679: /*
680: * pread is like read, but if it's /dev/mem we use the phys
681: * system call for speed. (On systems without phys we have
682: * to use regular read.)
683: */
684: pread(fd, ptr, nbytes, loc)
685: char *ptr;
686: long loc;
687: {
688: int rc;
689: extern int errno;
690:
691: if (fd == swmem) {
692: rc=phys(6, nbytes/64+1, (short)(loc/64));
693: if (rc>=0) {
694: memcpy(ptr, 0140000, nbytes);
695: return nbytes;
696: } else {
697: return read(fd, ptr, nbytes);
698: }
699: } else {
700: return read(fd, ptr, nbytes);
701: }
702: }
703:
704: memcpy(dest, src, nbytes)
705: register char *dest, *src;
706: register int nbytes;
707: {
708: while (nbytes--)
709: *dest++ = *src++;
710: }