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