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