1: /*
2: * w.c (2.11BSD) 2.0 1996/11/17
3: *
4: * w - print system status (who and what)
5: *
6: * Rewritten using sysctl, no nlist used - 1/19/94 - sms.
7: *
8: * This program is similar to the systat command on Tenex/Tops 10/20
9: * It needs read permission on /dev/mem and /dev/swap.
10: */
11: #include <sys/param.h>
12: #include <sys/sysctl.h>
13: #include <stdio.h>
14: #include <ctype.h>
15: #include <utmp.h>
16: #include <string.h>
17: #include <sys/stat.h>
18: #include <sys/user.h>
19: #include <sys/proc.h>
20: #include <sys/ioctl.h>
21: #include <sys/tty.h>
22:
23: #define NMAX sizeof(utmp.ut_name)
24: #define LMAX sizeof(utmp.ut_line)
25: #define ARGWIDTH 33 /* # chars left on 80 col crt for args */
26: #define ARGLIST 1024 /* amount of stack to examine for argument list */
27:
28: struct smproc {
29: long w_addr; /* address in file for args */
30: short w_pid; /* proc.p_pid */
31: int w_igintr; /* INTR+3*QUIT, 0=die, 1=ign, 2=catch */
32: time_t w_time; /* CPU time used by this process */
33: time_t w_ctime; /* CPU time used by children */
34: dev_t w_tty; /* tty device of process */
35: char w_comm[15]; /* user.u_comm, null terminated */
36: char w_args[ARGWIDTH+1]; /* args if interesting process */
37: } *pr;
38:
39: FILE *ut;
40: int swmem;
41: int swap; /* /dev/mem, mem, and swap */
42: int file;
43: dev_t tty;
44: char doing[520]; /* process attached to terminal */
45: time_t proctime; /* cpu time of process in doing */
46: double avenrun[3];
47: extern int errno, optind;
48:
49: #define DIV60(t) ((t+30)/60) /* x/60 rounded */
50: #define TTYEQ (tty == pr[i].w_tty)
51: #define IGINT (1+3*1) /* ignoring both SIGINT & SIGQUIT */
52:
53: long round();
54: char *getargs();
55: char *getptr();
56:
57: char *program;
58: int = 1; /* true if -h flag: don't print heading */
59: int lflag = 1; /* true if -l flag: long style output */
60: time_t idle; /* number of minutes user is idle */
61: int nusers; /* number of users logged in now */
62: char * sel_user; /* login of particular user selected */
63: int wcmd = 1; /* running as the w command */
64: time_t jobtime; /* total cpu time visible */
65: time_t now; /* the current time of day */
66: struct tm *nowt; /* current time as time struct */
67: struct timeval boottime; /* time since last reboot */
68: time_t uptime; /* elapsed time since */
69: int np; /* number of processes currently active */
70: struct utmp utmp;
71: struct user up;
72:
73: struct addrmap {
74: long b1, e1; long f1;
75: long b2, e2; long f2;
76: };
77: struct addrmap datmap;
78:
79: main(argc, argv)
80: char **argv;
81: {
82: int days, hrs, mins;
83: register int i;
84: char *cp;
85: register int curpid, empty;
86: size_t size;
87: int mib[2];
88:
89: program = argv[0];
90: if ((cp = rindex(program, '/')) || *(cp = program) == '-')
91: cp++;
92: if (*cp == 'u')
93: wcmd = 0;
94:
95: while ((i = getopt(argc, argv, "hlswu")) != EOF)
96: {
97: switch (i)
98: {
99: case 'h':
100: header = 0;
101: break;
102: case 'l':
103: lflag++;
104: break;
105: case 's':
106: lflag = 0;
107: break;
108: case 'u':
109: wcmd = 0;
110: break;
111: case 'w':
112: wcmd = 1;
113: break;
114: default:
115: fprintf(stderr, "Usage: %s [-hlswu] [user]\n",
116: program);
117: exit(1);
118: }
119: }
120: argc -= optind;
121: argv += optind;
122: if (*argv)
123: sel_user = *argv;
124:
125: if (wcmd)
126: readpr();
127:
128: ut = fopen(_PATH_UTMP, "r");
129: if (header) {
130: /* Print time of day */
131: time(&now);
132: nowt = localtime(&now);
133: prtat(nowt);
134:
135: mib[0] = CTL_KERN;
136: mib[1] = KERN_BOOTTIME;
137: size = sizeof (boottime);
138: if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 &&
139: boottime.tv_sec != 0) {
140: uptime = now - boottime.tv_sec;
141: days = uptime / (60L*60L*24L);
142: uptime %= (60L*60L*24L);
143: hrs = uptime / (60L*60L);
144: uptime %= (60L*60L);
145: mins = DIV60(uptime);
146:
147: printf(" up");
148: if (days > 0)
149: printf(" %d day%s,", days, days>1?"s":"");
150: if (hrs > 0 && mins > 0) {
151: printf(" %2d:%02d,", hrs, mins);
152: } else {
153: if (hrs > 0)
154: printf(" %d hr%s,", hrs, hrs>1?"s":"");
155: if (mins > 0)
156: printf(" %d min%s,", mins, mins>1?"s":"");
157: }
158: }
159:
160: /* Print number of users logged in to system */
161: while (fread(&utmp, sizeof(utmp), 1, ut)) {
162: if (utmp.ut_name[0] != '\0')
163: nusers++;
164: }
165: rewind(ut);
166: printf(" %d user%c", nusers, nusers > 1 ? 's' : '\0');
167:
168: if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1)
169: printf(", no load average information available\n");
170: else {
171: printf(", load averages:");
172: for (i = 0; i < (sizeof(avenrun)/sizeof(avenrun[0])); i++) {
173: if (i > 0)
174: printf(",");
175: printf(" %.2f", avenrun[i]);
176: }
177: }
178: printf("\n");
179: if (wcmd == 0)
180: exit(0);
181:
182: /* Headers for rest of output */
183: if (lflag)
184: printf("%-*.*s %-*.*s login@ idle JCPU PCPU what\n",
185: NMAX, NMAX, "User", LMAX, LMAX, "tty");
186: else
187: printf("%-*.*s tty idle what\n",
188: NMAX, NMAX, "User");
189: fflush(stdout);
190: }
191:
192:
193: for (;;) { /* for each entry in utmp */
194: if (fread(&utmp, sizeof(utmp), 1, ut) == NULL) {
195: fclose(ut);
196: exit(0);
197: }
198: if (utmp.ut_name[0] == '\0')
199: continue; /* that tty is free */
200: if (sel_user && strncmp(utmp.ut_name, sel_user, NMAX) != 0)
201: continue; /* we wanted only somebody else */
202:
203: gettty();
204: jobtime = 0;
205: proctime = 0;
206: strcpy(doing, "-"); /* default act: normally never prints */
207: empty = 1;
208: curpid = -1;
209: idle = findidle();
210: for (i=0; i<np; i++) { /* for each process on this tty */
211: if (!(TTYEQ))
212: continue;
213: jobtime += pr[i].w_time + pr[i].w_ctime;
214: proctime += pr[i].w_time;
215: if (empty && pr[i].w_igintr!=IGINT) {
216: empty = 0;
217: curpid = -1;
218: }
219: if(pr[i].w_pid>curpid && (pr[i].w_igintr!=IGINT || empty)){
220: curpid = pr[i].w_pid;
221: strcpy(doing, lflag ? pr[i].w_args : pr[i].w_comm);
222: if (doing[0]==0 || doing[0]=='-' && doing[1]<=' ' || doing[0] == '?') {
223: strcat(doing, " (");
224: strcat(doing, pr[i].w_comm);
225: strcat(doing, ")");
226: }
227: }
228: }
229: putline();
230: }
231: }
232:
233: /* figure out the major/minor device # pair for this tty */
234: gettty()
235: {
236: char ttybuf[20];
237: struct stat statbuf;
238:
239: ttybuf[0] = 0;
240: strcpy(ttybuf, "/dev/");
241: strcat(ttybuf, utmp.ut_line);
242: stat(ttybuf, &statbuf);
243: tty = statbuf.st_rdev;
244: }
245:
246: /*
247: * putline: print out the accumulated line of info about one user.
248: */
249: putline()
250: {
251:
252: /* print login name of the user */
253: printf("%-*.*s ", NMAX, NMAX, utmp.ut_name);
254:
255: /* print tty user is on */
256: if (lflag)
257: /* long form: all (up to) LMAX chars */
258: printf("%-*.*s", LMAX, LMAX, utmp.ut_line);
259: else {
260: /* short form: 2 chars, skipping 'tty' if there */
261: if (utmp.ut_line[0]=='t' && utmp.ut_line[1]=='t' && utmp.ut_line[2]=='y')
262: printf("%-2.2s", &utmp.ut_line[3]);
263: else
264: printf("%-2.2s", utmp.ut_line);
265: }
266:
267: if (lflag)
268: /* print when the user logged in */
269: prtat(localtime(&utmp.ut_time));
270:
271: /* print idle time */
272: prttime(idle," ");
273:
274: if (lflag) {
275: /* print CPU time for all processes & children */
276: prttime(DIV60(jobtime)," ");
277: /* print cpu time for interesting process */
278: prttime(DIV60(proctime)," ");
279: }
280:
281: /* what user is doing, either command tail or args */
282: printf(" %-.32s\n",doing);
283: fflush(stdout);
284: }
285:
286: /* find & return number of minutes current tty has been idle */
287: findidle()
288: {
289: struct stat stbuf;
290: long lastaction, diff;
291: char ttyname[20];
292:
293: strcpy(ttyname, "/dev/");
294: strncat(ttyname, utmp.ut_line, LMAX);
295: stat(ttyname, &stbuf);
296: time(&now);
297: lastaction = stbuf.st_atime;
298: diff = now - lastaction;
299: diff = DIV60(diff);
300: if (diff < 0) diff = 0;
301: return(diff);
302: }
303:
304: /*
305: * prttime prints a time in hours and minutes.
306: * The character string tail is printed at the end, obvious
307: * strings to pass are "", " ", or "am".
308: */
309: prttime(tim, tail)
310: time_t tim;
311: char *tail;
312: {
313: register int didhrs = 0;
314:
315: if (tim >= 60) {
316: printf("%3ld:", tim/60);
317: didhrs++;
318: } else {
319: printf(" ");
320: }
321: tim %= 60;
322: if (tim > 0 || didhrs) {
323: printf(didhrs&&tim<10 ? "%02ld" : "%2ld", tim);
324: } else {
325: printf(" ");
326: }
327: printf("%s", tail);
328: }
329:
330: /* prtat prints a 12 hour time given a pointer to a time of day */
331: prtat(p)
332: register struct tm *p;
333: {
334: register int pm;
335: time_t t;
336:
337: t = p -> tm_hour;
338: pm = (t > 11);
339: if (t > 11)
340: t -= 12;
341: if (t == 0)
342: t = 12;
343: prttime(t*60 + p->tm_min, pm ? "pm" : "am");
344: }
345:
346: /*
347: * readpr finds and reads in the array pr, containing the interesting
348: * parts of the proc and user tables for each live process.
349: */
350: readpr()
351: {
352: struct kinfo_proc *kp;
353: register struct proc *p;
354: register struct smproc *smp;
355: struct kinfo_proc *kpt;
356: int pn, nproc;
357: long addr, daddr, saddr;
358: long txtsiz, datsiz, stksiz;
359: int septxt;
360: int mib[4], st;
361: size_t size;
362:
363: if((swmem = open("/dev/mem", 0)) < 0) {
364: perror("/dev/mem");
365: exit(1);
366: }
367: if ((swap = open("/dev/swap", 0)) < 0) {
368: perror("/dev/swap");
369: exit(1);
370: }
371: mib[0] = CTL_KERN;
372: mib[1] = KERN_PROC;
373: mib[2] = KERN_PROC_ALL;
374: size = 0;
375: st = sysctl(mib, 4, NULL, &size, NULL, 0);
376: if (st == -1) {
377: fprintf(stderr, "sysctl: %s \n", strerror(errno));
378: exit(1);
379: }
380: if (size % sizeof (struct kinfo_proc) != 0) {
381: fprintf(stderr, "proc size mismatch (%d total, %d chunks)\n",
382: size, sizeof(struct kinfo_proc));
383: exit(1);
384: }
385: kpt = (struct kinfo_proc *)malloc(size);
386: if (kpt == (struct kinfo_proc *)NULL) {
387: fprintf(stderr, "Not %d bytes of memory for proc table\n",
388: size);
389: exit(1);
390: }
391: if (sysctl(mib, 4, kpt, &size, NULL, 0) == -1) {
392: fprintf(stderr, "sysctl fetch of proc table failed: %s\n",
393: strerror(errno));
394: exit(1);
395: }
396:
397: nproc = size / sizeof (struct kinfo_proc);
398: pr = (struct smproc *) malloc(nproc * sizeof(struct smproc));
399: if (pr == (struct smproc *)NULL) {
400: fprintf(stderr,"Not enough memory for proc table\n");
401: exit(1);
402: }
403: /*
404: * Now step thru the kinfo_proc structures and save interesting
405: * process's info in the 'smproc' structure.
406: */
407: smp = pr;
408: kp = kpt;
409: for (pn = 0; pn < nproc; kp++, pn++) {
410: p = &kp->kp_proc;
411: /* decide if it's an interesting process */
412: if (p->p_stat==0 || p->p_stat==SZOMB || p->p_pgrp==0)
413: continue;
414: /* find & read in the user structure */
415: if (p->p_flag & SLOAD) {
416: addr = ctob((long)p->p_addr);
417: daddr = ctob((long)p->p_daddr);
418: saddr = ctob((long)p->p_saddr);
419: file = swmem;
420: } else {
421: addr = (off_t)p->p_addr<<9;
422: daddr = (off_t)p->p_daddr<<9;
423: saddr = (off_t)p->p_saddr<<9;
424: file = swap;
425: }
426: lseek(file, addr, 0);
427: if (read(file, (char *)&up, sizeof(up)) != sizeof(up))
428: continue;
429: if (up.u_ttyp == NULL)
430: continue;
431:
432: /* set up address maps for user pcs */
433: txtsiz = ctob(up.u_tsize);
434: datsiz = ctob(up.u_dsize);
435: stksiz = ctob(up.u_ssize);
436: septxt = up.u_sep;
437: datmap.b1 = (septxt ? 0 : round(txtsiz,TXTRNDSIZ));
438: datmap.e1 = datmap.b1+datsiz;
439: datmap.f1 = daddr;
440: datmap.b2 = stackbas(stksiz);
441: datmap.e2 = stacktop(stksiz);
442: datmap.f2 = saddr;
443:
444: /* save the interesting parts */
445: smp->w_addr = saddr + ctob((long)p->p_ssize) - ARGLIST;
446: smp->w_pid = p->p_pid;
447: smp->w_igintr = (int)(((up.u_signal[2]==1) + 2*(up.u_signal[2]>1) + 3*(up.u_signal[3]==1)) + 6*(up.u_signal[3]>1));
448: smp->w_time = up.u_ru.ru_utime + up.u_ru.ru_stime;
449: smp->w_ctime = up.u_cru.ru_utime + up.u_cru.ru_stime;
450: smp->w_tty = up.u_ttyd;
451: up.u_comm[14] = 0; /* Bug: This bombs next field. */
452: strcpy(smp->w_comm, up.u_comm);
453: /*
454: * Get args if there's a chance we'll print it.
455: * Cant just save pointer: getargs returns static place.
456: * Cant use strncpy: that crock blank pads.
457: */
458: smp->w_args[0] = 0;
459: strncat(smp->w_args,getargs(smp),ARGWIDTH);
460: if (smp->w_args[0]==0 || smp->w_args[0]=='-' && smp->w_args[1]<=' ' || smp->w_args[0] == '?') {
461: strcat(smp->w_args, " (");
462: strcat(smp->w_args, smp->w_comm);
463: strcat(smp->w_args, ")");
464: }
465: smp++;
466: }
467: np = smp - pr;
468: free(kpt);
469: }
470:
471: /*
472: * getargs: given a pointer to a proc structure, this looks at the swap area
473: * and tries to reconstruct the arguments. This is straight out of ps.
474: */
475: char *
476: getargs(p)
477: struct smproc *p;
478: {
479: int c, nbad;
480: static char abuf[ARGLIST];
481: register int *ip;
482: register char *cp, *cp1;
483: char **ap;
484: long addr;
485:
486: addr = p->w_addr;
487:
488: /* look for sh special */
489: lseek(file, addr+ARGLIST-sizeof(char **), 0);
490: if (read(file, (char *)&ap, sizeof(char *)) != sizeof(char *))
491: return(NULL);
492: if (ap) {
493: char *b = (char *) abuf;
494: char *bp = b;
495: while((cp=getptr(ap++)) && cp && (bp<b+ARGWIDTH) ) {
496: nbad = 0;
497: while((c=getbyte(cp++)) && (bp<b+ARGWIDTH)) {
498: if (c<' ' || c>'~') {
499: if (nbad++>3)
500: break;
501: continue;
502: }
503: *bp++ = c;
504: }
505: *bp++ = ' ';
506: }
507: *bp++ = 0;
508: return(b);
509: }
510:
511: lseek(file, addr, 0);
512: if (read(file, abuf, sizeof(abuf)) != sizeof(abuf))
513: return((char *)1);
514: for (ip = (int *) &abuf[ARGLIST]-2; ip > (int *) abuf;) {
515: /* Look from top for -1 or 0 as terminator flag. */
516: if (*--ip == -1 || *ip == 0) {
517: cp = (char *)(ip+1);
518: if (*cp==0)
519: cp++;
520: nbad = 0; /* up to 5 funny chars as ?'s */
521: for (cp1 = cp; cp1 < (char *)&abuf[ARGLIST]; cp1++) {
522: c = *cp1&0177;
523: if (c==0) /* nulls between args => spaces */
524: *cp1 = ' ';
525: else if (c < ' ' || c > 0176) {
526: if (++nbad >= 5) {
527: *cp1++ = ' ';
528: break;
529: }
530: *cp1 = '?';
531: } else if (c=='=') { /* Oops - found an
532: * environment var, back
533: * over & erase it. */
534: *cp1 = 0;
535: while (cp1>cp && *--cp1!=' ')
536: *cp1 = 0;
537: break;
538: }
539: }
540: while (*--cp1==' ') /* strip trailing spaces */
541: *cp1 = 0;
542: return(cp);
543: }
544: }
545: return (p->w_comm);
546: }
547:
548: char *
549: getptr(adr)
550: char **adr;
551: {
552: char *ptr;
553: register char *p, *pa;
554: register i;
555:
556: ptr = 0;
557: pa = (char *)adr;
558: p = (char *)&ptr;
559: for (i=0; i<sizeof(ptr); i++)
560: *p++ = getbyte(pa++);
561: return(ptr);
562: }
563:
564: getbyte(adr)
565: char *adr;
566: {
567: register struct addrmap *amap = &datmap;
568: char b;
569: long saddr;
570:
571: if(!within(adr, amap->b1, amap->e1)) {
572: if(within(adr, amap->b2, amap->e2)) {
573: saddr = (unsigned)adr + amap->f2 - amap->b2;
574: } else
575: return(0);
576: } else
577: saddr = (unsigned)adr + amap->f1 - amap->b1;
578: if(lseek(file, saddr, 0)==-1
579: || read(file, &b, 1)<1) {
580: return(0);
581: }
582: return((unsigned)b);
583: }
584:
585:
586: within(adr,lbd,ubd)
587: char *adr;
588: long lbd, ubd;
589: {
590: return((unsigned)adr>=lbd && (unsigned)adr<ubd);
591: }
592:
593: long
594: round(a, b)
595: long a, b;
596: {
597: long w = ((a+b-1)/b)*b;
598:
599: return(w);
600: }