date and time created 80/10/01 17:29:40 by bill
[unix-history] / usr / src / usr.bin / w / w.c
CommitLineData
76238d88
BJ
1static char *sccsid = "@(#)w.c 4.1 (Berkeley) %G%";
2/*
3 * w - print system status (who and what)
4 *
5 * This program is similar to the systat command on Tenex/Tops 10/20
6 * It needs read permission on /dev/mem, /dev/kmem, and /dev/drum.
7 */
8#include <sys/param.h>
9#include <nlist.h>
10#include <stdio.h>
11#include <ctype.h>
12#include <utmp.h>
13#include <time.h>
14#include <sys/stat.h>
15#include <sys/dir.h>
16#include <sys/user.h>
17#include <sys/proc.h>
18#include <sys/pte.h>
19#include <sys/vm.h>
20
21#define NMAX sizeof(utmp.ut_name)
22#define LMAX sizeof(utmp.ut_line)
23
24#define ARGWIDTH 33 /* # chars left on 80 col crt for args */
25
26struct smproc {
27 short w_pid; /* proc.p_pid */
28 char w_flag; /* proc.p_flag */
29 short w_size; /* proc.p_size */
30 long w_seekaddr; /* where to find args */
31 long w_lastpg; /* disk address of stack */
32 int w_igintr; /* INTR+3*QUIT, 0=die, 1=ign, 2=catch */
33 time_t w_time; /* CPU time used by this process */
34 time_t w_ctime; /* CPU time used by children */
35 dev_t w_tty; /* tty device of process */
36 char w_comm[15]; /* user.u_comm, null terminated */
37 char w_args[ARGWIDTH+1]; /* args if interesting process */
38} pr[NPROC];
39
40struct nlist nl[] = {
41 { "_proc" },
42#define X_PROC 0
43 { "_swapdev" },
44#define X_SWAPDEV 1
45 { "_Usrptmap" },
46#define X_USRPTMA 2
47 { "_usrpt" },
48#define X_USRPT 3
49 { "_nswap" },
50#define X_NSWAP 4
51 { "_avenrun" },
52#define X_AVENRUN 5
53 { "_bootime" },
54#define X_BOOTIME 6
55 { 0 },
56};
57
58FILE *ps;
59FILE *ut;
60FILE *bootfd;
61int kmem;
62int mem;
63int swap; /* /dev/kmem, mem, and swap */
64int nswap;
65dev_t tty;
66char doing[520]; /* process attached to terminal */
67time_t proctime; /* cpu time of process in doing */
68double avenrun[3];
69
70#define DIV60(t) ((t+30)/60) /* x/60 rounded */
71#define TTYEQ (tty == pr[i].w_tty)
72#define IGINT (1+3*1) /* ignoring both SIGINT & SIGQUIT */
73
74char *getargs();
75char *fread();
76char *ctime();
77char *rindex();
78FILE *popen();
79struct tm *localtime();
80
81int debug; /* true if -d flag: debugging output */
82int header = 1; /* true if -h flag: don't print heading */
83int lflag = 1; /* true if -l flag: long style output */
84int login; /* true if invoked as login shell */
85int idle; /* number of minutes user is idle */
86int nusers; /* number of users logged in now */
87char * sel_user; /* login of particular user selected */
88char firstchar; /* first char of name of prog invoked as */
89time_t jobtime; /* total cpu time visible */
90time_t now; /* the current time of day */
91struct tm *nowt; /* current time as time struct */
92time_t bootime, uptime; /* time of last reboot & elapsed time since */
93int np; /* number of processes currently active */
94struct utmp utmp;
95struct proc mproc;
96struct user up;
97char fill[512];
98
99main(argc, argv)
100 char **argv;
101{
102 int days, hrs, mins;
103 register int i, j;
104 char *cp;
105 register int curpid, empty;
106 char obuf[BUFSIZ];
107
108 setbuf(stdout, obuf);
109 login = (argv[0][0] == '-');
110 cp = rindex(argv[0], '/');
111 firstchar = login ? argv[0][1] : (cp==0) ? argv[0][0] : cp[1];
112 cp = argv[0]; /* for Usage */
113
114 while (argc > 1) {
115 if (argv[1][0] == '-') {
116 for (i=1; argv[1][i]; i++) {
117 switch(argv[1][i]) {
118
119 case 'd':
120 debug++;
121 break;
122
123 case 'h':
124 header = 0;
125 break;
126
127 case 'l':
128 lflag++;
129 break;
130
131 case 's':
132 lflag = 0;
133 break;
134
135 case 'u':
136 case 'w':
137 firstchar = argv[1][1];
138 break;
139
140 default:
141 printf("Bad flag %s\n", argv[1]);
142 exit(1);
143 }
144 }
145 } else {
146 if (!isalnum(argv[1][0]) || argc > 2) {
147 printf("Usage: %s [ -hlsuw ] [ user ]\n", cp);
148 exit(1);
149 } else
150 sel_user = argv[1];
151 }
152 argc--; argv++;
153 }
154
155 if ((kmem = open("/dev/kmem", 0)) < 0) {
156 fprintf(stderr, "No kmem\n");
157 exit(1);
158 }
159 nlist("/vmunix", nl);
160 if (nl[0].n_type==0) {
161 fprintf(stderr, "No namelist\n");
162 exit(1);
163 }
164
165 if (firstchar != 'u')
166 readpr();
167
168 ut = fopen("/etc/utmp","r");
169 if (header) {
170 /* Print time of day */
171 time(&now);
172 nowt = localtime(&now);
173 prtat(nowt);
174
175 /*
176 * Print how long system has been up.
177 * (Found by looking for "bootime" in kernel)
178 */
179 lseek(kmem, (long)nl[X_BOOTIME].n_value, 0);
180 read(kmem, &bootime, sizeof (bootime));
181
182 uptime = now - bootime;
183 days = uptime / (60*60*24);
184 uptime %= (60*60*24);
185 hrs = uptime / (60*60);
186 uptime %= (60*60);
187 mins = DIV60(uptime);
188
189 printf(" up");
190 if (days > 0)
191 printf(" %d day%s,", days, days>1?"s":"");
192 if (hrs > 0 && mins > 0) {
193 printf(" %2d:%02d,", hrs, mins);
194 } else {
195 if (hrs > 0)
196 printf(" %d hr%s,", hrs, hrs>1?"s":"");
197 if (mins > 0)
198 printf(" %d min%s,", mins, mins>1?"s":"");
199 }
200
201 /* Print number of users logged in to system */
202 while (fread(&utmp, sizeof(utmp), 1, ut)) {
203 if (utmp.ut_name[0] != '\0')
204 nusers++;
205 }
206 rewind(ut);
207 printf(" %d users", nusers);
208
209 /*
210 * Print 1, 5, and 15 minute load averages.
211 * (Found by looking in kernel for avenrun).
212 */
213 printf(", load average:");
214 lseek(kmem, (long)nl[X_AVENRUN].n_value, 0);
215 read(kmem, avenrun, sizeof(avenrun));
216 for (i = 0; i < (sizeof(avenrun)/sizeof(avenrun[0])); i++) {
217 if (i > 0)
218 printf(",");
219 printf(" %.2f", avenrun[i]);
220 }
221 printf("\n");
222 if (firstchar == 'u')
223 exit(0);
224
225 /* Headers for rest of output */
226 if (lflag)
227 printf("User tty login@ idle JCPU PCPU what\n");
228 else
229 printf("User tty idle what\n");
230 fflush(stdout);
231 }
232
233
234 for (;;) { /* for each entry in utmp */
235 if (fread(&utmp, sizeof(utmp), 1, ut) == NULL) {
236 fclose(ut);
237 exit(0);
238 }
239 if (utmp.ut_name[0] == '\0')
240 continue; /* that tty is free */
241 if (sel_user && strcmpn(utmp.ut_name, sel_user, NMAX) != 0)
242 continue; /* we wanted only somebody else */
243
244 gettty();
245 jobtime = 0;
246 proctime = 0;
247 strcpy(doing, "-"); /* default act: normally never prints */
248 empty = 1;
249 curpid = -1;
250 idle = findidle();
251 for (i=0; i<np; i++) { /* for each process on this tty */
252 if (!(TTYEQ))
253 continue;
254 jobtime += pr[i].w_time + pr[i].w_ctime;
255 proctime += pr[i].w_time;
256 if (debug) {
257 printf("\t\t%d\t%s", pr[i].w_pid, pr[i].w_args);
258 if ((j=pr[i].w_igintr) > 0)
259 if (j==IGINT)
260 printf(" &");
261 else
262 printf(" & %d %d", j%3, j/3);
263 printf("\n");
264 }
265 if (empty && pr[i].w_igintr!=IGINT) {
266 empty = 0;
267 curpid = -1;
268 }
269 if(pr[i].w_pid>curpid && (pr[i].w_igintr!=IGINT || empty)){
270 curpid = pr[i].w_pid;
271 strcpy(doing, lflag ? pr[i].w_args : pr[i].w_comm);
272#ifdef notdef
273 if (doing[0]==0 || doing[0]=='-' && doing[1]<=' ' || doing[0] == '?') {
274 strcat(doing, " (");
275 strcat(doing, pr[i].w_comm);
276 strcat(doing, ")");
277 }
278#endif
279 }
280 }
281 putline();
282 }
283}
284
285/* figure out the major/minor device # pair for this tty */
286gettty()
287{
288 char ttybuf[20];
289 struct stat statbuf;
290
291 ttybuf[0] = 0;
292 strcpy(ttybuf, "/dev/");
293 strcat(ttybuf, utmp.ut_line);
294 stat(ttybuf, &statbuf);
295 tty = statbuf.st_rdev;
296}
297
298/*
299 * putline: print out the accumulated line of info about one user.
300 */
301putline()
302{
303 register int tm;
304
305 /* print login name of the user */
306 printf("%-*.*s ", NMAX, NMAX, utmp.ut_name);
307
308 /* print tty user is on */
309 if (lflag)
310 /* long form: all (up to) LMAX chars */
311 printf("%-*.*s", LMAX, LMAX, utmp.ut_line);
312 else {
313 /* short form: 2 chars, skipping 'tty' if there */
314 if (utmp.ut_line[0]=='t' && utmp.ut_line[1]=='t' && utmp.ut_line[2]=='y')
315 printf("%-2.2s", &utmp.ut_line[3]);
316 else
317 printf("%-2.2s", utmp.ut_line);
318 }
319
320 if (lflag)
321 /* print when the user logged in */
322 prtat(localtime(&utmp.ut_time));
323
324 /* print idle time */
325 prttime(idle," ");
326
327 if (lflag) {
328 /* print CPU time for all processes & children */
329 prttime(DIV60(jobtime)," ");
330 /* print cpu time for interesting process */
331 prttime(DIV60(proctime)," ");
332 }
333
334 /* what user is doing, either command tail or args */
335 printf(" %-.32s\n",doing);
336 fflush(stdout);
337}
338
339/* find & return number of minutes current tty has been idle */
340findidle()
341{
342 struct stat stbuf;
343 long lastaction, diff;
344 char ttyname[20];
345
346 strcpy(ttyname, "/dev/");
347 strcatn(ttyname, utmp.ut_line, LMAX);
348 stat(ttyname, &stbuf);
349 time(&now);
350 lastaction = stbuf.st_atime;
351 diff = now - lastaction;
352 diff = DIV60(diff);
353 if (diff < 0) diff = 0;
354 return(diff);
355}
356
357/*
358 * prttime prints a time in hours and minutes.
359 * The character string tail is printed at the end, obvious
360 * strings to pass are "", " ", or "am".
361 */
362prttime(tim, tail)
363 time_t tim;
364 char *tail;
365{
366 register int didhrs = 0;
367
368 if (tim >= 60) {
369 printf("%3d:", tim/60);
370 didhrs++;
371 } else {
372 printf(" ");
373 }
374 tim %= 60;
375 if (tim > 0 || didhrs) {
376 printf(didhrs&&tim<10 ? "%02d" : "%2d", tim);
377 } else {
378 printf(" ");
379 }
380 printf("%s", tail);
381}
382
383/* prtat prints a 12 hour time given a pointer to a time of day */
384prtat(p)
385 struct tm *p;
386{
387 register int t, pm;
388
389 t = p -> tm_hour;
390 pm = (t > 11);
391 if (t > 11)
392 t -= 12;
393 if (t == 0)
394 t = 12;
395 prttime(t*60 + p->tm_min, pm ? "pm" : "am");
396}
397
398/*
399 * readpr finds and reads in the array pr, containing the interesting
400 * parts of the proc and user tables for each live process.
401 */
402readpr()
403{
404 int pn, mf, addr, c;
405 int szpt, pfnum, i;
406 struct pte *Usrptma, *usrpt, *pte, apte;
407 struct dblock db;
408
409 Usrptma = (struct pte *) nl[X_USRPTMA].n_value;
410 usrpt = (struct pte *) nl[X_USRPT].n_value;
411 if((mem = open("/dev/mem", 0)) < 0) {
412 fprintf(stderr, "No mem\n");
413 exit(1);
414 }
415 if ((swap = open("/dev/drum", 0)) < 0) {
416 fprintf(stderr, "No drum\n");
417 exit(1);
418 }
419 /*
420 * read mem to find swap dev.
421 */
422 lseek(kmem, (long)nl[X_SWAPDEV].n_value, 0);
423 read(kmem, &nl[X_SWAPDEV].n_value, sizeof(nl[X_SWAPDEV].n_value));
424 /*
425 * Find base of swap
426 */
427 lseek(kmem, (long)nl[X_NSWAP].n_value, 0);
428 read(kmem, &nswap, sizeof(nswap));
429 /*
430 * Locate proc table
431 */
432 np = 0;
433 for (pn=0; pn<NPROC; pn++) {
434 lseek(kmem, (long)(nl[X_PROC].n_value + pn*(sizeof mproc)), 0);
435 read(kmem, &mproc, sizeof mproc);
436 /* decide if it's an interesting process */
437 if (mproc.p_stat==0 || mproc.p_pgrp==0)
438 continue;
439 if (mproc.p_flag&SDETACH)
440 continue;
441
442#ifdef notdef
443 /*
444 * The following speeds up w on systems with lots of ttys
445 * by ignoring inits and gettys, but loses on root login shells.
446 * On Ernie it reduced user and system time by .3 seconds,
447 * an insignificant amount. It is commented out since it
448 * will lose when root logs in.
449 */
450 if (mproc.p_uid == 0 & mproc.p_ppid == 1)
451 continue;
452#endif
453
454 /* find & read in the user structure */
455 if ((mproc.p_flag & SLOAD) == 0) {
456 /* not in memory - get from swap device */
457 addr = mproc.p_swaddr<<9;
458 lseek(swap, (long)addr, 0);
459 if (read(swap, &up, sizeof(up)) != sizeof(up)) {
460 continue;
461 }
462 } else {
463 int p0br, cc;
464#define INTPPG (NBPG / sizeof (int))
465 struct pte pagetbl[NBPG / sizeof (struct pte)];
466 /* loaded, get each page from memory separately */
467 szpt = mproc.p_szpt;
468 p0br = (int)mproc.p_p0br;
469 pte = &Usrptma[btokmx(mproc.p_p0br) + szpt-1];
470 lseek(kmem, (long)pte, 0);
471 if (read(kmem, &apte, sizeof(apte)) != sizeof(apte))
472 continue;
473 lseek(mem, ctob(apte.pg_pfnum), 0);
474 if (read(mem,pagetbl,sizeof(pagetbl)) != sizeof(pagetbl))
475cont:
476 continue;
477 for(cc=0; cc<UPAGES; cc++) { /* get u area */
478 int upage = pagetbl[NPTEPG-UPAGES+cc].pg_pfnum;
479 lseek(mem,ctob(upage),0);
480 if (read(mem,((int *)&up)+INTPPG*cc,NBPG) != NBPG)
481 goto cont;
482 }
483 szpt = up.u_pcb.pcb_szpt;
484 pr[np].w_seekaddr = ctob(apte.pg_pfnum);
485 }
486 vstodb(0, 1, &up.u_smap, &db, 1);
487 pr[np].w_lastpg = ctob(db.db_base);
488 if (up.u_ttyp == NULL)
489 continue;
490
491 /* save the interesting parts */
492 pr[np].w_pid = mproc.p_pid;
493 pr[np].w_flag = mproc.p_flag;
494 pr[np].w_size = mproc.p_dsize + mproc.p_ssize;
495 pr[np].w_igintr = (((int)up.u_signal[2]==1) + 2*((int)up.u_signal[2]>1) + 3*((int)up.u_signal[3]==1)) + 6*((int)up.u_signal[3]>1);
496 pr[np].w_time = up.u_vm.vm_utime + up.u_vm.vm_stime;
497 pr[np].w_ctime = up.u_cvm.vm_utime + up.u_cvm.vm_stime;
498 pr[np].w_tty = up.u_ttyd;
499 up.u_comm[14] = 0; /* Bug: This bombs next field. */
500 strcpy(pr[np].w_comm, up.u_comm);
501 /*
502 * Get args if there's a chance we'll print it.
503 * Cant just save pointer: getargs returns static place.
504 * Cant use strcpyn: that crock blank pads.
505 */
506 pr[np].w_args[0] = 0;
507 strcatn(pr[np].w_args,getargs(&pr[np]),ARGWIDTH);
508 if (pr[np].w_args[0]==0 || pr[np].w_args[0]=='-' && pr[np].w_args[1]<=' ' || pr[np].w_args[0] == '?') {
509 strcat(pr[np].w_args, " (");
510 strcat(pr[np].w_args, pr[np].w_comm);
511 strcat(pr[np].w_args, ")");
512 }
513 np++;
514 }
515}
516
517/*
518 * getargs: given a pointer to a proc structure, this looks at the swap area
519 * and tries to reconstruct the arguments. This is straight out of ps.
520 */
521char *
522getargs(p)
523 struct smproc *p;
524{
525 int c, addr, nbad;
526 static int abuf[512/sizeof(int)];
527 struct pte pagetbl[NPTEPG];
528 register int *ip;
529 register char *cp, *cp1;
530
531 if ((p->w_flag & SLOAD) == 0) {
532 lseek(swap, p->w_lastpg, 0);
533 if (read(swap, abuf, sizeof(abuf)) != sizeof(abuf))
534 return(p->w_comm);
535 } else {
536 c = p->w_seekaddr;
537 lseek(mem,c,0);
538 if (read(mem,pagetbl,NBPG) != NBPG)
539 return(p->w_comm);
540 if (pagetbl[NPTEPG-1-UPAGES].pg_fod==0 && pagetbl[NPTEPG-1-UPAGES].pg_pfnum) {
541 lseek(mem,ctob(pagetbl[NPTEPG-1-UPAGES].pg_pfnum),0);
542 if (read(mem,abuf,sizeof(abuf)) != sizeof(abuf))
543 return(p->w_comm);
544 } else {
545 lseek(swap, p->w_lastpg, 0);
546 if (read(swap, abuf, sizeof(abuf)) != sizeof(abuf))
547 return(p->w_comm);
548 }
549 }
550 abuf[127] = 0;
551 for (ip = &abuf[126]; ip > abuf;) {
552 /* Look from top for -1 or 0 as terminator flag. */
553 if (*--ip == -1 || *ip == 0) {
554 cp = (char *)(ip+1);
555 if (*cp==0)
556 cp++;
557 nbad = 0; /* up to 5 funny chars as ?'s */
558 for (cp1 = cp; cp1 < (char *)&abuf[128]; cp1++) {
559 c = *cp1&0177;
560 if (c==0) /* nulls between args => spaces */
561 *cp1 = ' ';
562 else if (c < ' ' || c > 0176) {
563 if (++nbad >= 5) {
564 *cp1++ = ' ';
565 break;
566 }
567 *cp1 = '?';
568 } else if (c=='=') { /* Oops - found an
569 * environment var, back
570 * over & erase it. */
571 *cp1 = 0;
572 while (cp1>cp && *--cp1!=' ')
573 *cp1 = 0;
574 break;
575 }
576 }
577 while (*--cp1==' ') /* strip trailing spaces */
578 *cp1 = 0;
579 return(cp);
580 }
581 }
582 return (p->w_comm);
583}
584
585/*
586 * Given a base/size pair in virtual swap area,
587 * return a physical base/size pair which is the
588 * (largest) initial, physically contiguous block.
589 */
590vstodb(vsbase, vssize, dmp, dbp, rev)
591 register int vsbase;
592 int vssize;
593 struct dmap *dmp;
594 register struct dblock *dbp;
595{
596 register int blk = DMMIN;
597 register swblk_t *ip = dmp->dm_map;
598
599 if (vsbase < 0 || vsbase + vssize > dmp->dm_size)
600 panic("vstodb");
601 while (vsbase >= blk) {
602 vsbase -= blk;
603 if (blk < DMMAX)
604 blk *= 2;
605 ip++;
606 }
607 if (*ip <= 0 || *ip + blk > nswap)
608 panic("vstodb *ip");
609 dbp->db_size = min(vssize, blk - vsbase);
610 dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
611}
612
613panic(cp)
614 char *cp;
615{
616
617 /* printf("%s\n", cp); */
618}
619
620min(a, b)
621{
622
623 return (a < b ? a : b);
624}