users only when nusers > 1
[unix-history] / usr / src / usr.bin / w / w.c
CommitLineData
68d13ee3 1#ifndef lint
4473e7e3 2static char *sccsid = "@(#)w.c 4.15 (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 */
af9a91c9 37 int w_uid; /* uid of process */
76238d88
BJ
38 char w_comm[15]; /* user.u_comm, null terminated */
39 char w_args[ARGWIDTH+1]; /* args if interesting process */
bdb1f95f
BJ
40} *pr;
41int nproc;
76238d88
BJ
42
43struct nlist nl[] = {
44 { "_proc" },
45#define X_PROC 0
46 { "_swapdev" },
47#define X_SWAPDEV 1
48 { "_Usrptmap" },
49#define X_USRPTMA 2
50 { "_usrpt" },
51#define X_USRPT 3
52 { "_nswap" },
53#define X_NSWAP 4
54 { "_avenrun" },
55#define X_AVENRUN 5
a43be079
SL
56 { "_boottime" },
57#define X_BOOTTIME 6
bdb1f95f
BJ
58 { "_nproc" },
59#define X_NPROC 7
68d13ee3
SL
60 { "_dmmin" },
61#define X_DMMIN 8
62 { "_dmmax" },
63#define X_DMMAX 9
20932249 64 { "" },
76238d88
BJ
65};
66
67FILE *ps;
68FILE *ut;
69FILE *bootfd;
70int kmem;
71int mem;
72int swap; /* /dev/kmem, mem, and swap */
73int nswap;
68d13ee3 74int dmmin, dmmax;
76238d88 75dev_t tty;
af9a91c9 76int uid;
76238d88
BJ
77char doing[520]; /* process attached to terminal */
78time_t proctime; /* cpu time of process in doing */
79double avenrun[3];
bdb1f95f 80struct proc *aproc;
76238d88 81
68d13ee3 82#define DIV60(t) ((t+30)/60) /* x/60 rounded */
af9a91c9 83#define TTYEQ (tty == pr[i].w_tty && uid == pr[i].w_uid)
76238d88
BJ
84#define IGINT (1+3*1) /* ignoring both SIGINT & SIGQUIT */
85
86char *getargs();
87char *fread();
88char *ctime();
89char *rindex();
90FILE *popen();
91struct tm *localtime();
92
93int debug; /* true if -d flag: debugging output */
94int header = 1; /* true if -h flag: don't print heading */
95int lflag = 1; /* true if -l flag: long style output */
96int login; /* true if invoked as login shell */
97int idle; /* number of minutes user is idle */
98int nusers; /* number of users logged in now */
99char * sel_user; /* login of particular user selected */
100char firstchar; /* first char of name of prog invoked as */
101time_t jobtime; /* total cpu time visible */
102time_t now; /* the current time of day */
103struct tm *nowt; /* current time as time struct */
a43be079
SL
104struct timeval boottime;
105time_t uptime; /* time of last reboot & elapsed time since */
76238d88
BJ
106int np; /* number of processes currently active */
107struct utmp utmp;
108struct proc mproc;
20932249
SL
109union {
110 struct user U_up;
111 char pad[NBPG][UPAGES];
112} Up;
113#define up Up.U_up
76238d88
BJ
114
115main(argc, argv)
116 char **argv;
117{
118 int days, hrs, mins;
119 register int i, j;
120 char *cp;
121 register int curpid, empty;
76238d88 122
76238d88
BJ
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;
af9a91c9 311 uid = statbuf.st_uid;
76238d88
BJ
312}
313
314/*
315 * putline: print out the accumulated line of info about one user.
316 */
317putline()
318{
319 register int tm;
320
321 /* print login name of the user */
322 printf("%-*.*s ", NMAX, NMAX, utmp.ut_name);
323
324 /* print tty user is on */
325 if (lflag)
326 /* long form: all (up to) LMAX chars */
327 printf("%-*.*s", LMAX, LMAX, utmp.ut_line);
328 else {
329 /* short form: 2 chars, skipping 'tty' if there */
330 if (utmp.ut_line[0]=='t' && utmp.ut_line[1]=='t' && utmp.ut_line[2]=='y')
331 printf("%-2.2s", &utmp.ut_line[3]);
332 else
333 printf("%-2.2s", utmp.ut_line);
334 }
335
336 if (lflag)
337 /* print when the user logged in */
338 prtat(localtime(&utmp.ut_time));
339
340 /* print idle time */
341 prttime(idle," ");
342
343 if (lflag) {
344 /* print CPU time for all processes & children */
a591da4b 345 prttime(jobtime," ");
76238d88 346 /* print cpu time for interesting process */
a591da4b 347 prttime(proctime," ");
76238d88
BJ
348 }
349
350 /* what user is doing, either command tail or args */
351 printf(" %-.32s\n",doing);
352 fflush(stdout);
353}
354
355/* find & return number of minutes current tty has been idle */
356findidle()
357{
358 struct stat stbuf;
359 long lastaction, diff;
360 char ttyname[20];
361
362 strcpy(ttyname, "/dev/");
363 strcatn(ttyname, utmp.ut_line, LMAX);
364 stat(ttyname, &stbuf);
365 time(&now);
366 lastaction = stbuf.st_atime;
367 diff = now - lastaction;
368 diff = DIV60(diff);
369 if (diff < 0) diff = 0;
370 return(diff);
371}
372
373/*
374 * prttime prints a time in hours and minutes.
375 * The character string tail is printed at the end, obvious
376 * strings to pass are "", " ", or "am".
377 */
378prttime(tim, tail)
379 time_t tim;
380 char *tail;
381{
382 register int didhrs = 0;
383
384 if (tim >= 60) {
385 printf("%3d:", tim/60);
386 didhrs++;
387 } else {
388 printf(" ");
389 }
390 tim %= 60;
391 if (tim > 0 || didhrs) {
392 printf(didhrs&&tim<10 ? "%02d" : "%2d", tim);
393 } else {
394 printf(" ");
395 }
396 printf("%s", tail);
397}
398
399/* prtat prints a 12 hour time given a pointer to a time of day */
400prtat(p)
401 struct tm *p;
402{
403 register int t, pm;
404
405 t = p -> tm_hour;
406 pm = (t > 11);
407 if (t > 11)
408 t -= 12;
409 if (t == 0)
410 t = 12;
411 prttime(t*60 + p->tm_min, pm ? "pm" : "am");
412}
413
414/*
415 * readpr finds and reads in the array pr, containing the interesting
416 * parts of the proc and user tables for each live process.
417 */
418readpr()
419{
420 int pn, mf, addr, c;
421 int szpt, pfnum, i;
422 struct pte *Usrptma, *usrpt, *pte, apte;
423 struct dblock db;
424
425 Usrptma = (struct pte *) nl[X_USRPTMA].n_value;
426 usrpt = (struct pte *) nl[X_USRPT].n_value;
427 if((mem = open("/dev/mem", 0)) < 0) {
428 fprintf(stderr, "No mem\n");
429 exit(1);
430 }
431 if ((swap = open("/dev/drum", 0)) < 0) {
432 fprintf(stderr, "No drum\n");
433 exit(1);
434 }
435 /*
436 * read mem to find swap dev.
437 */
438 lseek(kmem, (long)nl[X_SWAPDEV].n_value, 0);
439 read(kmem, &nl[X_SWAPDEV].n_value, sizeof(nl[X_SWAPDEV].n_value));
440 /*
68d13ee3 441 * Find base of and parameters of swap
76238d88
BJ
442 */
443 lseek(kmem, (long)nl[X_NSWAP].n_value, 0);
444 read(kmem, &nswap, sizeof(nswap));
68d13ee3
SL
445 lseek(kmem, (long)nl[X_DMMIN].n_value, 0);
446 read(kmem, &dmmin, sizeof(dmmin));
447 lseek(kmem, (long)nl[X_DMMAX].n_value, 0);
448 read(kmem, &dmmax, sizeof(dmmax));
76238d88
BJ
449 /*
450 * Locate proc table
451 */
bdb1f95f
BJ
452 lseek(kmem, (long)nl[X_NPROC].n_value, 0);
453 read(kmem, &nproc, sizeof(nproc));
454 pr = (struct pr *)calloc(nproc, sizeof (struct pr));
76238d88 455 np = 0;
bdb1f95f
BJ
456 lseek(kmem, (long)nl[X_PROC].n_value, 0);
457 read(kmem, &aproc, sizeof(aproc));
458 for (pn=0; pn<nproc; pn++) {
459 lseek(kmem, (int)(aproc + pn), 0);
76238d88
BJ
460 read(kmem, &mproc, sizeof mproc);
461 /* decide if it's an interesting process */
462 if (mproc.p_stat==0 || mproc.p_pgrp==0)
463 continue;
76238d88
BJ
464 /* find & read in the user structure */
465 if ((mproc.p_flag & SLOAD) == 0) {
466 /* not in memory - get from swap device */
20932249 467 addr = dtob(mproc.p_swaddr);
76238d88
BJ
468 lseek(swap, (long)addr, 0);
469 if (read(swap, &up, sizeof(up)) != sizeof(up)) {
470 continue;
471 }
472 } else {
473 int p0br, cc;
474#define INTPPG (NBPG / sizeof (int))
475 struct pte pagetbl[NBPG / sizeof (struct pte)];
476 /* loaded, get each page from memory separately */
477 szpt = mproc.p_szpt;
478 p0br = (int)mproc.p_p0br;
479 pte = &Usrptma[btokmx(mproc.p_p0br) + szpt-1];
480 lseek(kmem, (long)pte, 0);
481 if (read(kmem, &apte, sizeof(apte)) != sizeof(apte))
482 continue;
483 lseek(mem, ctob(apte.pg_pfnum), 0);
484 if (read(mem,pagetbl,sizeof(pagetbl)) != sizeof(pagetbl))
485cont:
486 continue;
487 for(cc=0; cc<UPAGES; cc++) { /* get u area */
488 int upage = pagetbl[NPTEPG-UPAGES+cc].pg_pfnum;
489 lseek(mem,ctob(upage),0);
490 if (read(mem,((int *)&up)+INTPPG*cc,NBPG) != NBPG)
491 goto cont;
492 }
493 szpt = up.u_pcb.pcb_szpt;
494 pr[np].w_seekaddr = ctob(apte.pg_pfnum);
495 }
e941b445 496 vstodb(0, CLSIZE, &up.u_smap, &db, 1);
20932249 497 pr[np].w_lastpg = dtob(db.db_base);
76238d88
BJ
498 if (up.u_ttyp == NULL)
499 continue;
500
501 /* save the interesting parts */
502 pr[np].w_pid = mproc.p_pid;
503 pr[np].w_flag = mproc.p_flag;
504 pr[np].w_size = mproc.p_dsize + mproc.p_ssize;
20932249
SL
505 pr[np].w_igintr = (((int)up.u_signal[2]==1) +
506 2*((int)up.u_signal[2]>1) + 3*((int)up.u_signal[3]==1)) +
507 6*((int)up.u_signal[3]>1);
a43be079
SL
508 pr[np].w_time =
509 up.u_ru.ru_utime.tv_sec + up.u_ru.ru_stime.tv_sec;
510 pr[np].w_ctime =
511 up.u_cru.ru_utime.tv_sec + up.u_cru.ru_stime.tv_sec;
76238d88 512 pr[np].w_tty = up.u_ttyd;
af9a91c9 513 pr[np].w_uid = mproc.p_uid;
76238d88
BJ
514 up.u_comm[14] = 0; /* Bug: This bombs next field. */
515 strcpy(pr[np].w_comm, up.u_comm);
516 /*
517 * Get args if there's a chance we'll print it.
518 * Cant just save pointer: getargs returns static place.
519 * Cant use strcpyn: that crock blank pads.
520 */
521 pr[np].w_args[0] = 0;
522 strcatn(pr[np].w_args,getargs(&pr[np]),ARGWIDTH);
523 if (pr[np].w_args[0]==0 || pr[np].w_args[0]=='-' && pr[np].w_args[1]<=' ' || pr[np].w_args[0] == '?') {
524 strcat(pr[np].w_args, " (");
525 strcat(pr[np].w_args, pr[np].w_comm);
526 strcat(pr[np].w_args, ")");
527 }
528 np++;
529 }
530}
531
532/*
533 * getargs: given a pointer to a proc structure, this looks at the swap area
534 * and tries to reconstruct the arguments. This is straight out of ps.
535 */
536char *
537getargs(p)
bdb1f95f 538 struct pr *p;
76238d88
BJ
539{
540 int c, addr, nbad;
e941b445 541 static int abuf[CLSIZE*NBPG/sizeof(int)];
76238d88
BJ
542 struct pte pagetbl[NPTEPG];
543 register int *ip;
544 register char *cp, *cp1;
545
546 if ((p->w_flag & SLOAD) == 0) {
547 lseek(swap, p->w_lastpg, 0);
548 if (read(swap, abuf, sizeof(abuf)) != sizeof(abuf))
549 return(p->w_comm);
550 } else {
551 c = p->w_seekaddr;
552 lseek(mem,c,0);
553 if (read(mem,pagetbl,NBPG) != NBPG)
554 return(p->w_comm);
e941b445
BJ
555 if (pagetbl[NPTEPG-CLSIZE-UPAGES].pg_fod==0 && pagetbl[NPTEPG-CLSIZE-UPAGES].pg_pfnum) {
556 lseek(mem,ctob(pagetbl[NPTEPG-CLSIZE-UPAGES].pg_pfnum),0);
76238d88
BJ
557 if (read(mem,abuf,sizeof(abuf)) != sizeof(abuf))
558 return(p->w_comm);
559 } else {
560 lseek(swap, p->w_lastpg, 0);
561 if (read(swap, abuf, sizeof(abuf)) != sizeof(abuf))
562 return(p->w_comm);
563 }
564 }
e941b445
BJ
565 abuf[sizeof(abuf)/sizeof(abuf[0])-1] = 0;
566 for (ip = &abuf[sizeof(abuf)/sizeof(abuf[0])-2]; ip > abuf;) {
76238d88
BJ
567 /* Look from top for -1 or 0 as terminator flag. */
568 if (*--ip == -1 || *ip == 0) {
569 cp = (char *)(ip+1);
570 if (*cp==0)
571 cp++;
572 nbad = 0; /* up to 5 funny chars as ?'s */
e941b445 573 for (cp1 = cp; cp1 < (char *)&abuf[sizeof(abuf)/sizeof(abuf[0])]; cp1++) {
76238d88
BJ
574 c = *cp1&0177;
575 if (c==0) /* nulls between args => spaces */
576 *cp1 = ' ';
577 else if (c < ' ' || c > 0176) {
578 if (++nbad >= 5) {
579 *cp1++ = ' ';
580 break;
581 }
582 *cp1 = '?';
583 } else if (c=='=') { /* Oops - found an
584 * environment var, back
585 * over & erase it. */
586 *cp1 = 0;
587 while (cp1>cp && *--cp1!=' ')
588 *cp1 = 0;
589 break;
590 }
591 }
592 while (*--cp1==' ') /* strip trailing spaces */
593 *cp1 = 0;
594 return(cp);
595 }
596 }
597 return (p->w_comm);
598}
599
600/*
601 * Given a base/size pair in virtual swap area,
602 * return a physical base/size pair which is the
603 * (largest) initial, physically contiguous block.
604 */
605vstodb(vsbase, vssize, dmp, dbp, rev)
606 register int vsbase;
607 int vssize;
608 struct dmap *dmp;
609 register struct dblock *dbp;
610{
68d13ee3 611 register int blk = dmmin;
76238d88
BJ
612 register swblk_t *ip = dmp->dm_map;
613
20932249
SL
614 vsbase = ctod(vsbase);
615 vssize = ctod(vssize);
76238d88
BJ
616 if (vsbase < 0 || vsbase + vssize > dmp->dm_size)
617 panic("vstodb");
618 while (vsbase >= blk) {
619 vsbase -= blk;
68d13ee3 620 if (blk < dmmax)
76238d88
BJ
621 blk *= 2;
622 ip++;
623 }
624 if (*ip <= 0 || *ip + blk > nswap)
625 panic("vstodb *ip");
626 dbp->db_size = min(vssize, blk - vsbase);
627 dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
628}
629
630panic(cp)
631 char *cp;
632{
633
634 /* printf("%s\n", cp); */
635}
636
637min(a, b)
638{
639
640 return (a < b ? a : b);
641}