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