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