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