1 static char *sccsid = "@(#)w.c 4.5 (Berkeley) 02/26/82"; 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 /* find & read in the user structure */ 449 if ((mproc.p_flag & SLOAD) == 0) { 450 /* not in memory - get from swap device */ 451 addr = mproc.p_swaddr<<9; 452 lseek(swap, (long)addr, 0); 453 if (read(swap, &up, sizeof(up)) != sizeof(up)) { 454 continue; 455 } 456 } else { 457 int p0br, cc; 458 #define INTPPG (NBPG / sizeof (int)) 459 struct pte pagetbl[NBPG / sizeof (struct pte)]; 460 /* loaded, get each page from memory separately */ 461 szpt = mproc.p_szpt; 462 p0br = (int)mproc.p_p0br; 463 pte = &Usrptma[btokmx(mproc.p_p0br) + szpt-1]; 464 lseek(kmem, (long)pte, 0); 465 if (read(kmem, &apte, sizeof(apte)) != sizeof(apte)) 466 continue; 467 lseek(mem, ctob(apte.pg_pfnum), 0); 468 if (read(mem,pagetbl,sizeof(pagetbl)) != sizeof(pagetbl)) 469 cont: 470 continue; 471 for(cc=0; cc<UPAGES; cc++) { /* get u area */ 472 int upage = pagetbl[NPTEPG-UPAGES+cc].pg_pfnum; 473 lseek(mem,ctob(upage),0); 474 if (read(mem,((int *)&up)+INTPPG*cc,NBPG) != NBPG) 475 goto cont; 476 } 477 szpt = up.u_pcb.pcb_szpt; 478 pr[np].w_seekaddr = ctob(apte.pg_pfnum); 479 } 480 vstodb(0, CLSIZE, &up.u_smap, &db, 1); 481 pr[np].w_lastpg = ctob(db.db_base); 482 if (up.u_ttyp == NULL) 483 continue; 484 485 /* save the interesting parts */ 486 pr[np].w_pid = mproc.p_pid; 487 pr[np].w_flag = mproc.p_flag; 488 pr[np].w_size = mproc.p_dsize + mproc.p_ssize; 489 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); 490 pr[np].w_time = up.u_vm.vm_utime + up.u_vm.vm_stime; 491 pr[np].w_ctime = up.u_cvm.vm_utime + up.u_cvm.vm_stime; 492 pr[np].w_tty = up.u_ttyd; 493 up.u_comm[14] = 0; /* Bug: This bombs next field. */ 494 strcpy(pr[np].w_comm, up.u_comm); 495 /* 496 * Get args if there's a chance we'll print it. 497 * Cant just save pointer: getargs returns static place. 498 * Cant use strcpyn: that crock blank pads. 499 */ 500 pr[np].w_args[0] = 0; 501 strcatn(pr[np].w_args,getargs(&pr[np]),ARGWIDTH); 502 if (pr[np].w_args[0]==0 || pr[np].w_args[0]=='-' && pr[np].w_args[1]<=' ' || pr[np].w_args[0] == '?') { 503 strcat(pr[np].w_args, " ("); 504 strcat(pr[np].w_args, pr[np].w_comm); 505 strcat(pr[np].w_args, ")"); 506 } 507 np++; 508 } 509 } 510 511 /* 512 * getargs: given a pointer to a proc structure, this looks at the swap area 513 * and tries to reconstruct the arguments. This is straight out of ps. 514 */ 515 char * 516 getargs(p) 517 struct pr *p; 518 { 519 int c, addr, nbad; 520 static int abuf[CLSIZE*NBPG/sizeof(int)]; 521 struct pte pagetbl[NPTEPG]; 522 register int *ip; 523 register char *cp, *cp1; 524 525 if ((p->w_flag & SLOAD) == 0) { 526 lseek(swap, p->w_lastpg, 0); 527 if (read(swap, abuf, sizeof(abuf)) != sizeof(abuf)) 528 return(p->w_comm); 529 } else { 530 c = p->w_seekaddr; 531 lseek(mem,c,0); 532 if (read(mem,pagetbl,NBPG) != NBPG) 533 return(p->w_comm); 534 if (pagetbl[NPTEPG-CLSIZE-UPAGES].pg_fod==0 && pagetbl[NPTEPG-CLSIZE-UPAGES].pg_pfnum) { 535 lseek(mem,ctob(pagetbl[NPTEPG-CLSIZE-UPAGES].pg_pfnum),0); 536 if (read(mem,abuf,sizeof(abuf)) != sizeof(abuf)) 537 return(p->w_comm); 538 } else { 539 lseek(swap, p->w_lastpg, 0); 540 if (read(swap, abuf, sizeof(abuf)) != sizeof(abuf)) 541 return(p->w_comm); 542 } 543 } 544 abuf[sizeof(abuf)/sizeof(abuf[0])-1] = 0; 545 for (ip = &abuf[sizeof(abuf)/sizeof(abuf[0])-2]; ip > abuf;) { 546 /* Look from top for -1 or 0 as terminator flag. */ 547 if (*--ip == -1 || *ip == 0) { 548 cp = (char *)(ip+1); 549 if (*cp==0) 550 cp++; 551 nbad = 0; /* up to 5 funny chars as ?'s */ 552 for (cp1 = cp; cp1 < (char *)&abuf[sizeof(abuf)/sizeof(abuf[0])]; cp1++) { 553 c = *cp1&0177; 554 if (c==0) /* nulls between args => spaces */ 555 *cp1 = ' '; 556 else if (c < ' ' || c > 0176) { 557 if (++nbad >= 5) { 558 *cp1++ = ' '; 559 break; 560 } 561 *cp1 = '?'; 562 } else if (c=='=') { /* Oops - found an 563 * environment var, back 564 * over & erase it. */ 565 *cp1 = 0; 566 while (cp1>cp && *--cp1!=' ') 567 *cp1 = 0; 568 break; 569 } 570 } 571 while (*--cp1==' ') /* strip trailing spaces */ 572 *cp1 = 0; 573 return(cp); 574 } 575 } 576 return (p->w_comm); 577 } 578 579 /* 580 * Given a base/size pair in virtual swap area, 581 * return a physical base/size pair which is the 582 * (largest) initial, physically contiguous block. 583 */ 584 vstodb(vsbase, vssize, dmp, dbp, rev) 585 register int vsbase; 586 int vssize; 587 struct dmap *dmp; 588 register struct dblock *dbp; 589 { 590 register int blk = DMMIN; 591 register swblk_t *ip = dmp->dm_map; 592 593 if (vsbase < 0 || vsbase + vssize > dmp->dm_size) 594 panic("vstodb"); 595 while (vsbase >= blk) { 596 vsbase -= blk; 597 if (blk < DMMAX) 598 blk *= 2; 599 ip++; 600 } 601 if (*ip <= 0 || *ip + blk > nswap) 602 panic("vstodb *ip"); 603 dbp->db_size = min(vssize, blk - vsbase); 604 dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase); 605 } 606 607 panic(cp) 608 char *cp; 609 { 610 611 /* printf("%s\n", cp); */ 612 } 613 614 min(a, b) 615 { 616 617 return (a < b ? a : b); 618 } 619