1: /*
   2:  *	w.c	(2.11BSD)	2.0	1996/11/17
   3:  *
   4:  * w - print system status (who and what)
   5:  *
   6:  * Rewritten using sysctl, no nlist used  - 1/19/94 - sms.
   7:  *
   8:  * This program is similar to the systat command on Tenex/Tops 10/20
   9:  * It needs read permission on /dev/mem and /dev/swap.
  10:  */
  11: #include <sys/param.h>
  12: #include <sys/sysctl.h>
  13: #include <stdio.h>
  14: #include <ctype.h>
  15: #include <utmp.h>
  16: #include <string.h>
  17: #include <sys/stat.h>
  18: #include <sys/user.h>
  19: #include <sys/proc.h>
  20: #include <sys/ioctl.h>
  21: #include <sys/tty.h>
  22: 
  23: #define NMAX    sizeof(utmp.ut_name)
  24: #define LMAX    sizeof(utmp.ut_line)
  25: #define ARGWIDTH    33  /* # chars left on 80 col crt for args */
  26: #define ARGLIST 1024    /* amount of stack to examine for argument list */
  27: 
  28: struct smproc {
  29:     long    w_addr;         /* address in file for args */
  30:     short   w_pid;          /* proc.p_pid */
  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: 
  39: FILE    *ut;
  40: int swmem;
  41: int swap;           /* /dev/mem, mem, and swap */
  42: int file;
  43: dev_t   tty;
  44: char    doing[520];     /* process attached to terminal */
  45: time_t  proctime;       /* cpu time of process in doing */
  46: double  avenrun[3];
  47: extern  int errno, optind;
  48: 
  49: #define DIV60(t)    ((t+30)/60)    /* x/60 rounded */
  50: #define TTYEQ       (tty == pr[i].w_tty)
  51: #define IGINT       (1+3*1)     /* ignoring both SIGINT & SIGQUIT */
  52: 
  53: long    round();
  54: char    *getargs();
  55: char    *getptr();
  56: 
  57: char    *program;
  58: int header = 1;     /* true if -h flag: don't print heading */
  59: int lflag = 1;      /* true if -l flag: long style output */
  60: time_t  idle;           /* number of minutes user is idle */
  61: int nusers;         /* number of users logged in now */
  62: char *  sel_user;       /* login of particular user selected */
  63: int     wcmd = 1;       /* running as the w command */
  64: time_t  jobtime;        /* total cpu time visible */
  65: time_t  now;            /* the current time of day */
  66: struct  tm *nowt;       /* current time as time struct */
  67: struct  timeval boottime;   /* time since last reboot */
  68: time_t  uptime;         /* elapsed time since */
  69: int np;         /* number of processes currently active */
  70: struct  utmp utmp;
  71: struct  user up;
  72: 
  73: struct addrmap {
  74:     long    b1, e1; long f1;
  75:     long    b2, e2; long f2;
  76: };
  77: struct addrmap datmap;
  78: 
  79: main(argc, argv)
  80:     char **argv;
  81: {
  82:     int days, hrs, mins;
  83:     register int i;
  84:     char *cp;
  85:     register int curpid, empty;
  86:     size_t  size;
  87:     int mib[2];
  88: 
  89:     program = argv[0];
  90:     if ((cp = rindex(program, '/')) || *(cp = program) == '-')
  91:         cp++;
  92:     if (*cp == 'u')
  93:         wcmd = 0;
  94: 
  95:     while   ((i = getopt(argc, argv, "hlswu")) != EOF)
  96:         {
  97:         switch  (i)
  98:             {
  99:             case 'h':
 100:                 header = 0;
 101:                 break;
 102:             case 'l':
 103:                 lflag++;
 104:                 break;
 105:             case 's':
 106:                 lflag = 0;
 107:                 break;
 108:             case 'u':
 109:                 wcmd = 0;
 110:                 break;
 111:             case 'w':
 112:                 wcmd = 1;
 113:                 break;
 114:             default:
 115:                 fprintf(stderr, "Usage: %s [-hlswu] [user]\n",
 116:                     program);
 117:                 exit(1);
 118:             }
 119:         }
 120:     argc -= optind;
 121:     argv += optind;
 122:     if  (*argv)
 123:         sel_user = *argv;
 124: 
 125:     if (wcmd)
 126:         readpr();
 127: 
 128:     ut = fopen(_PATH_UTMP, "r");
 129:     if (header) {
 130:         /* Print time of day */
 131:         time(&now);
 132:         nowt = localtime(&now);
 133:         prtat(nowt);
 134: 
 135:         mib[0] = CTL_KERN;
 136:         mib[1] = KERN_BOOTTIME;
 137:         size = sizeof (boottime);
 138:         if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 &&
 139:             boottime.tv_sec != 0) {
 140:             uptime = now - boottime.tv_sec;
 141:             days = uptime / (60L*60L*24L);
 142:             uptime %= (60L*60L*24L);
 143:             hrs = uptime / (60L*60L);
 144:             uptime %= (60L*60L);
 145:             mins = DIV60(uptime);
 146: 
 147:             printf("  up");
 148:             if (days > 0)
 149:                 printf(" %d day%s,", days, days>1?"s":"");
 150:             if (hrs > 0 && mins > 0) {
 151:                 printf(" %2d:%02d,", hrs, mins);
 152:             } else {
 153:                 if (hrs > 0)
 154:                     printf(" %d hr%s,", hrs, hrs>1?"s":"");
 155:                 if (mins > 0)
 156:                     printf(" %d min%s,", mins, mins>1?"s":"");
 157:             }
 158:         }
 159: 
 160:         /* Print number of users logged in to system */
 161:         while (fread(&utmp, sizeof(utmp), 1, ut)) {
 162:             if (utmp.ut_name[0] != '\0')
 163:                 nusers++;
 164:         }
 165:         rewind(ut);
 166:         printf("  %d user%c", nusers, nusers > 1 ?  's' : '\0');
 167: 
 168:         if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1)
 169:             printf(", no load average information available\n");
 170:         else {
 171:             printf(",  load averages:");
 172:             for (i = 0; i < (sizeof(avenrun)/sizeof(avenrun[0])); i++) {
 173:                 if (i > 0)
 174:                     printf(",");
 175:                 printf(" %.2f", avenrun[i]);
 176:             }
 177:         }
 178:         printf("\n");
 179:         if (wcmd == 0)
 180:             exit(0);
 181: 
 182:         /* Headers for rest of output */
 183:         if (lflag)
 184:             printf("%-*.*s %-*.*s  login@  idle   JCPU   PCPU  what\n",
 185:                 NMAX, NMAX, "User", LMAX, LMAX, "tty");
 186:         else
 187:             printf("%-*.*s tty idle  what\n",
 188:                 NMAX, NMAX, "User");
 189:         fflush(stdout);
 190:     }
 191: 
 192: 
 193:     for (;;) {  /* for each entry in utmp */
 194:         if (fread(&utmp, sizeof(utmp), 1, ut) == NULL) {
 195:             fclose(ut);
 196:             exit(0);
 197:         }
 198:         if (utmp.ut_name[0] == '\0')
 199:             continue;   /* that tty is free */
 200:         if (sel_user && strncmp(utmp.ut_name, sel_user, NMAX) != 0)
 201:             continue;   /* we wanted only somebody else */
 202: 
 203:         gettty();
 204:         jobtime = 0;
 205:         proctime = 0;
 206:         strcpy(doing, "-"); /* default act: normally never prints */
 207:         empty = 1;
 208:         curpid = -1;
 209:         idle = findidle();
 210:         for (i=0; i<np; i++) {  /* for each process on this tty */
 211:             if (!(TTYEQ))
 212:                 continue;
 213:             jobtime += pr[i].w_time + pr[i].w_ctime;
 214:             proctime += pr[i].w_time;
 215:             if (empty && pr[i].w_igintr!=IGINT) {
 216:                 empty = 0;
 217:                 curpid = -1;
 218:             }
 219:             if(pr[i].w_pid>curpid && (pr[i].w_igintr!=IGINT || empty)){
 220:                 curpid = pr[i].w_pid;
 221:                 strcpy(doing, lflag ? pr[i].w_args : pr[i].w_comm);
 222:                 if (doing[0]==0 || doing[0]=='-' && doing[1]<=' ' || doing[0] == '?') {
 223:                     strcat(doing, " (");
 224:                     strcat(doing, pr[i].w_comm);
 225:                     strcat(doing, ")");
 226:                 }
 227:             }
 228:         }
 229:         putline();
 230:     }
 231: }
 232: 
 233: /* figure out the major/minor device # pair for this tty */
 234: gettty()
 235: {
 236:     char ttybuf[20];
 237:     struct stat statbuf;
 238: 
 239:     ttybuf[0] = 0;
 240:     strcpy(ttybuf, "/dev/");
 241:     strcat(ttybuf, utmp.ut_line);
 242:     stat(ttybuf, &statbuf);
 243:     tty = statbuf.st_rdev;
 244: }
 245: 
 246: /*
 247:  * putline: print out the accumulated line of info about one user.
 248:  */
 249: putline()
 250: {
 251: 
 252:     /* print login name of the user */
 253:     printf("%-*.*s ", NMAX, NMAX, utmp.ut_name);
 254: 
 255:     /* print tty user is on */
 256:     if (lflag)
 257:         /* long form: all (up to) LMAX chars */
 258:         printf("%-*.*s", LMAX, LMAX, utmp.ut_line);
 259:     else {
 260:         /* short form: 2 chars, skipping 'tty' if there */
 261:         if (utmp.ut_line[0]=='t' && utmp.ut_line[1]=='t' && utmp.ut_line[2]=='y')
 262:             printf("%-2.2s", &utmp.ut_line[3]);
 263:         else
 264:             printf("%-2.2s", utmp.ut_line);
 265:     }
 266: 
 267:     if (lflag)
 268:         /* print when the user logged in */
 269:         prtat(localtime(&utmp.ut_time));
 270: 
 271:     /* print idle time */
 272:     prttime(idle," ");
 273: 
 274:     if (lflag) {
 275:         /* print CPU time for all processes & children */
 276:         prttime(DIV60(jobtime)," ");
 277:         /* print cpu time for interesting process */
 278:         prttime(DIV60(proctime)," ");
 279:     }
 280: 
 281:     /* what user is doing, either command tail or args */
 282:     printf(" %-.32s\n",doing);
 283:     fflush(stdout);
 284: }
 285: 
 286: /* find & return number of minutes current tty has been idle */
 287: findidle()
 288: {
 289:     struct stat stbuf;
 290:     long lastaction, diff;
 291:     char ttyname[20];
 292: 
 293:     strcpy(ttyname, "/dev/");
 294:     strncat(ttyname, utmp.ut_line, LMAX);
 295:     stat(ttyname, &stbuf);
 296:     time(&now);
 297:     lastaction = stbuf.st_atime;
 298:     diff = now - lastaction;
 299:     diff = DIV60(diff);
 300:     if (diff < 0) diff = 0;
 301:     return(diff);
 302: }
 303: 
 304: /*
 305:  * prttime prints a time in hours and minutes.
 306:  * The character string tail is printed at the end, obvious
 307:  * strings to pass are "", " ", or "am".
 308:  */
 309: prttime(tim, tail)
 310:     time_t tim;
 311:     char *tail;
 312: {
 313:     register int didhrs = 0;
 314: 
 315:     if (tim >= 60) {
 316:         printf("%3ld:", tim/60);
 317:         didhrs++;
 318:     } else {
 319:         printf("    ");
 320:     }
 321:     tim %= 60;
 322:     if (tim > 0 || didhrs) {
 323:         printf(didhrs&&tim<10 ? "%02ld" : "%2ld", tim);
 324:     } else {
 325:         printf("  ");
 326:     }
 327:     printf("%s", tail);
 328: }
 329: 
 330: /* prtat prints a 12 hour time given a pointer to a time of day */
 331: prtat(p)
 332:     register struct tm *p;
 333: {
 334:     register int pm;
 335:     time_t t;
 336: 
 337:     t = p -> tm_hour;
 338:     pm = (t > 11);
 339:     if (t > 11)
 340:         t -= 12;
 341:     if (t == 0)
 342:         t = 12;
 343:     prttime(t*60 + p->tm_min, pm ? "pm" : "am");
 344: }
 345: 
 346: /*
 347:  * readpr finds and reads in the array pr, containing the interesting
 348:  * parts of the proc and user tables for each live process.
 349:  */
 350: readpr()
 351: {
 352:     struct  kinfo_proc *kp;
 353: register struct proc    *p;
 354: register struct smproc *smp;
 355:     struct  kinfo_proc *kpt;
 356:     int pn, nproc;
 357:     long addr, daddr, saddr;
 358:     long txtsiz, datsiz, stksiz;
 359:     int septxt;
 360:     int mib[4], st;
 361:     size_t  size;
 362: 
 363:     if((swmem = open("/dev/mem", 0)) < 0) {
 364:         perror("/dev/mem");
 365:         exit(1);
 366:     }
 367:     if ((swap = open("/dev/swap", 0)) < 0) {
 368:         perror("/dev/swap");
 369:         exit(1);
 370:     }
 371:     mib[0] = CTL_KERN;
 372:     mib[1] = KERN_PROC;
 373:     mib[2] = KERN_PROC_ALL;
 374:     size = 0;
 375:     st = sysctl(mib, 4, NULL, &size, NULL, 0);
 376:     if (st == -1) {
 377:         fprintf(stderr, "sysctl: %s \n", strerror(errno));
 378:         exit(1);
 379:     }
 380:     if (size % sizeof (struct kinfo_proc) != 0) {
 381:         fprintf(stderr, "proc size mismatch (%d total, %d chunks)\n",
 382:             size, sizeof(struct kinfo_proc));
 383:         exit(1);
 384:     }
 385:     kpt = (struct kinfo_proc *)malloc(size);
 386:     if (kpt == (struct kinfo_proc *)NULL) {
 387:         fprintf(stderr, "Not %d bytes of memory for proc table\n",
 388:             size);
 389:         exit(1);
 390:     }
 391:     if (sysctl(mib, 4, kpt, &size, NULL, 0) == -1) {
 392:         fprintf(stderr, "sysctl fetch of proc table failed: %s\n",
 393:             strerror(errno));
 394:         exit(1);
 395:     }
 396: 
 397:     nproc = size / sizeof (struct kinfo_proc);
 398:     pr = (struct smproc *) malloc(nproc * sizeof(struct smproc));
 399:     if (pr == (struct smproc *)NULL) {
 400:         fprintf(stderr,"Not enough memory for proc table\n");
 401:         exit(1);
 402:     }
 403:     /*
 404: 	 * Now step thru the kinfo_proc structures and save interesting
 405: 	 * process's info in the 'smproc' structure.
 406: 	 */
 407:     smp = pr;
 408:     kp = kpt;
 409:     for (pn = 0; pn < nproc; kp++, pn++) {
 410:         p = &kp->kp_proc;
 411:         /* decide if it's an interesting process */
 412:         if (p->p_stat==0 || p->p_stat==SZOMB || p->p_pgrp==0)
 413:             continue;
 414:         /* find & read in the user structure */
 415:         if (p->p_flag & SLOAD) {
 416:             addr = ctob((long)p->p_addr);
 417:             daddr = ctob((long)p->p_daddr);
 418:             saddr = ctob((long)p->p_saddr);
 419:             file = swmem;
 420:         } else {
 421:             addr = (off_t)p->p_addr<<9;
 422:             daddr = (off_t)p->p_daddr<<9;
 423:             saddr = (off_t)p->p_saddr<<9;
 424:             file = swap;
 425:         }
 426:         lseek(file, addr, 0);
 427:         if (read(file, (char *)&up, sizeof(up)) != sizeof(up))
 428:             continue;
 429:         if (up.u_ttyp == NULL)
 430:             continue;
 431: 
 432:         /* set up address maps for user pcs */
 433:         txtsiz = ctob(up.u_tsize);
 434:         datsiz = ctob(up.u_dsize);
 435:         stksiz = ctob(up.u_ssize);
 436:         septxt = up.u_sep;
 437:         datmap.b1 = (septxt ? 0 : round(txtsiz,TXTRNDSIZ));
 438:         datmap.e1 = datmap.b1+datsiz;
 439:         datmap.f1 = daddr;
 440:         datmap.b2 = stackbas(stksiz);
 441:         datmap.e2 = stacktop(stksiz);
 442:         datmap.f2 = saddr;
 443: 
 444:         /* save the interesting parts */
 445:         smp->w_addr = saddr + ctob((long)p->p_ssize) - ARGLIST;
 446:         smp->w_pid = p->p_pid;
 447:         smp->w_igintr = (int)(((up.u_signal[2]==1) + 2*(up.u_signal[2]>1) + 3*(up.u_signal[3]==1)) + 6*(up.u_signal[3]>1));
 448:         smp->w_time = up.u_ru.ru_utime + up.u_ru.ru_stime;
 449:         smp->w_ctime = up.u_cru.ru_utime + up.u_cru.ru_stime;
 450:         smp->w_tty = up.u_ttyd;
 451:         up.u_comm[14] = 0;  /* Bug: This bombs next field. */
 452:         strcpy(smp->w_comm, up.u_comm);
 453:         /*
 454: 		 * Get args if there's a chance we'll print it.
 455: 		 * Cant just save pointer: getargs returns static place.
 456: 		 * Cant use strncpy: that crock blank pads.
 457: 		 */
 458:         smp->w_args[0] = 0;
 459:         strncat(smp->w_args,getargs(smp),ARGWIDTH);
 460:         if (smp->w_args[0]==0 || smp->w_args[0]=='-' && smp->w_args[1]<=' ' || smp->w_args[0] == '?') {
 461:             strcat(smp->w_args, " (");
 462:             strcat(smp->w_args, smp->w_comm);
 463:             strcat(smp->w_args, ")");
 464:         }
 465:         smp++;
 466:     }
 467:     np = smp - pr;
 468:     free(kpt);
 469: }
 470: 
 471: /*
 472:  * getargs: given a pointer to a proc structure, this looks at the swap area
 473:  * and tries to reconstruct the arguments. This is straight out of ps.
 474:  */
 475: char *
 476: getargs(p)
 477:     struct smproc *p;
 478: {
 479:     int c, nbad;
 480:     static char abuf[ARGLIST];
 481:     register int *ip;
 482:     register char *cp, *cp1;
 483:     char **ap;
 484:     long addr;
 485: 
 486:     addr = p->w_addr;
 487: 
 488:     /* look for sh special */
 489:     lseek(file, addr+ARGLIST-sizeof(char **), 0);
 490:     if (read(file, (char *)&ap, sizeof(char *)) != sizeof(char *))
 491:         return(NULL);
 492:     if (ap) {
 493:         char *b = (char *) abuf;
 494:         char *bp = b;
 495:         while((cp=getptr(ap++)) && cp && (bp<b+ARGWIDTH) ) {
 496:             nbad = 0;
 497:             while((c=getbyte(cp++)) && (bp<b+ARGWIDTH)) {
 498:                 if (c<' ' || c>'~') {
 499:                     if (nbad++>3)
 500:                         break;
 501:                     continue;
 502:                 }
 503:                 *bp++ = c;
 504:             }
 505:             *bp++ = ' ';
 506:         }
 507:         *bp++ = 0;
 508:         return(b);
 509:     }
 510: 
 511:     lseek(file, addr, 0);
 512:     if (read(file, abuf, sizeof(abuf)) != sizeof(abuf))
 513:         return((char *)1);
 514:     for (ip = (int *) &abuf[ARGLIST]-2; ip > (int *) abuf;) {
 515:         /* Look from top for -1 or 0 as terminator flag. */
 516:         if (*--ip == -1 || *ip == 0) {
 517:             cp = (char *)(ip+1);
 518:             if (*cp==0)
 519:                 cp++;
 520:             nbad = 0;   /* up to 5 funny chars as ?'s */
 521:             for (cp1 = cp; cp1 < (char *)&abuf[ARGLIST]; cp1++) {
 522:                 c = *cp1&0177;
 523:                 if (c==0)  /* nulls between args => spaces */
 524:                     *cp1 = ' ';
 525:                 else if (c < ' ' || c > 0176) {
 526:                     if (++nbad >= 5) {
 527:                         *cp1++ = ' ';
 528:                         break;
 529:                     }
 530:                     *cp1 = '?';
 531:                 } else if (c=='=') {    /* Oops - found an
 532: 							 * environment var, back
 533: 							 * over & erase it. */
 534:                     *cp1 = 0;
 535:                     while (cp1>cp && *--cp1!=' ')
 536:                         *cp1 = 0;
 537:                     break;
 538:                 }
 539:             }
 540:             while (*--cp1==' ') /* strip trailing spaces */
 541:                 *cp1 = 0;
 542:             return(cp);
 543:         }
 544:     }
 545:     return (p->w_comm);
 546: }
 547: 
 548: char *
 549: getptr(adr)
 550: char **adr;
 551: {
 552:     char *ptr;
 553:     register char *p, *pa;
 554:     register i;
 555: 
 556:     ptr = 0;
 557:     pa = (char *)adr;
 558:     p = (char *)&ptr;
 559:     for (i=0; i<sizeof(ptr); i++)
 560:         *p++ = getbyte(pa++);
 561:     return(ptr);
 562: }
 563: 
 564: getbyte(adr)
 565: char *adr;
 566: {
 567:     register struct addrmap *amap = &datmap;
 568:     char b;
 569:     long saddr;
 570: 
 571:     if(!within(adr, amap->b1, amap->e1)) {
 572:         if(within(adr, amap->b2, amap->e2)) {
 573:             saddr = (unsigned)adr + amap->f2 - amap->b2;
 574:         } else
 575:             return(0);
 576:     } else
 577:         saddr = (unsigned)adr + amap->f1 - amap->b1;
 578:     if(lseek(file, saddr, 0)==-1
 579:            || read(file, &b, 1)<1) {
 580:         return(0);
 581:     }
 582:     return((unsigned)b);
 583: }
 584: 
 585: 
 586: within(adr,lbd,ubd)
 587: char *adr;
 588: long lbd, ubd;
 589: {
 590:     return((unsigned)adr>=lbd && (unsigned)adr<ubd);
 591: }
 592: 
 593: long
 594: round(a, b)
 595:     long        a, b;
 596: {
 597:     long        w = ((a+b-1)/b)*b;
 598: 
 599:     return(w);
 600: }

Defined functions

findidle defined in line 287; used 1 times
getargs defined in line 475; used 2 times
getbyte defined in line 564; used 2 times
getptr defined in line 548; used 2 times
gettty defined in line 234; used 1 times
main defined in line 79; never used
prtat defined in line 331; used 2 times
prttime defined in line 309; used 4 times
putline defined in line 249; used 1 times
readpr defined in line 350; used 1 times
round defined in line 593; used 2 times
within defined in line 586; used 2 times

Defined variables

avenrun defined in line 46; used 6 times
boottime defined in line 67; used 4 times
datmap defined in line 77; used 8 times
doing defined in line 44; used 10 times
file defined in line 42; used 10 times
header defined in line 58; used 2 times
idle defined in line 60; used 2 times
jobtime defined in line 64; used 3 times
lflag defined in line 59; used 7 times
now defined in line 65; used 5 times
nowt defined in line 66; used 2 times
np defined in line 69; used 2 times
nusers defined in line 61; used 3 times
pr defined in line 37; used 15 times
proctime defined in line 45; used 3 times
program defined in line 57; used 4 times
sel_user defined in line 62; used 3 times
swap defined in line 41; used 2 times
swmem defined in line 40; used 2 times
tty defined in line 43; used 2 times
up defined in line 71; used 19 times
uptime defined in line 68; used 6 times
utmp defined in line 70; used 19 times
wcmd defined in line 63; used 5 times

Defined struct's

addrmap defined in line 73; used 4 times
smproc defined in line 28; used 10 times

Defined macros

ARGLIST defined in line 26; used 5 times
ARGWIDTH defined in line 25; used 4 times
DIV60 defined in line 49; used 4 times
IGINT defined in line 51; used 2 times
LMAX defined in line 24; used 5 times
NMAX defined in line 23; used 7 times
TTYEQ defined in line 50; used 1 times
Last modified: 1996-11-18
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4620
Valid CSS Valid XHTML 1.0 Strict