1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: char copyright[] = 9: "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 10: All rights reserved.\n"; 11: #endif not lint 12: 13: #ifndef lint 14: static char sccsid[] = "@(#)sysline.c 5.6 (Berkeley) 1/9/86"; 15: #endif not lint 16: 17: /* 18: * sysline - system status display on 25th line of terminal 19: * j.k.foderaro 20: * 21: * Prints a variety of information on the special status line of terminals 22: * that have a status display capability. Cursor motions, status commands, 23: * etc. are gleamed from /etc/termcap. 24: * By default, all information is printed, and flags are given on the command 25: * line to disable the printing of information. The information and 26: * disabling flags are: 27: * 28: * flag what 29: * ----- ---- 30: * time of day 31: * load average and change in load average in the last 5 mins 32: * number of user logged on 33: * -p # of processes the users owns which are runnable and the 34: * number which are suspended. Processes whose parent is 1 35: * are not counted. 36: * -l users who've logged on and off. 37: * -m summarize new mail which has arrived 38: * 39: * <other flags> 40: * -r use non reverse video 41: * -c turn off 25th line for 5 seconds before redisplaying. 42: * -b beep once one the half hour, twice on the hour 43: * +N refresh display every N seconds. 44: * -i print pid first thing 45: * -e do simple print designed for an emacs buffer line 46: * -w do the right things for a window 47: * -h print hostname between time and load average 48: * -D print day/date before time of day 49: * -d debug mode - print status line data in human readable format 50: * -q quiet mode - don't output diagnostic messages 51: * -s print Short (left-justified) line if escapes not allowed 52: * -j Print left Justified line regardless 53: */ 54: 55: #define BSD4_2 /* for 4.2 BSD */ 56: #define WHO /* turn this on always */ 57: #define HOSTNAME /* 4.1a or greater, with hostname() */ 58: #define RWHO /* 4.1a or greater, with rwho */ 59: #define VMUNIX /* turn this on if you are running on vmunix */ 60: #define NEW_BOOTTIME /* 4.1c or greater */ 61: 62: #define NETPREFIX "ucb" 63: #define DEFDELAY 60 /* update status once per minute */ 64: #define MAILDIR "/usr/spool/mail" 65: /* 66: * if MAXLOAD is defined, then if the load average exceeded MAXLOAD 67: * then the process table will not be scanned and the log in/out data 68: * will not be checked. The purpose of this is to reduced the load 69: * on the system when it is loaded. 70: */ 71: #define MAXLOAD 6.0 72: 73: #include <stdio.h> 74: #include <sys/param.h> 75: #include <sys/signal.h> 76: #include <utmp.h> 77: #include <ctype.h> 78: #ifndef BSD4_2 79: #include <unctrl.h> 80: #endif 81: #include <sys/time.h> 82: #include <sys/stat.h> 83: #ifdef VMUNIX 84: #include <nlist.h> 85: #include <sys/vtimes.h> 86: #include <sys/proc.h> 87: #endif 88: #ifdef pdp11 89: #include <a.out.h> 90: #include <sys/proc.h> 91: #endif 92: #include <curses.h> 93: #undef nl 94: #ifdef TERMINFO 95: #include <term.h> 96: #endif TERMINFO 97: 98: #ifdef RWHO 99: #include <protocols/rwhod.h> 100: 101: #define DOWN_THRESHOLD (11 * 60) 102: #define RWHOLEADER "/usr/spool/rwho/whod." 103: 104: struct remotehost { 105: char *rh_host; 106: int rh_file; 107: } remotehost[10]; 108: int nremotes = 0; 109: #endif RWHO 110: 111: struct nlist nl[] = { 112: #ifdef NEW_BOOTTIME 113: { "_boottime" }, /* After 4.1a the label changed to "boottime" */ 114: #else 115: { "_bootime" }, /* Under 4.1a and earlier it is "bootime" */ 116: #endif 117: #define NL_BOOT 0 118: { "_proc" }, 119: #define NL_PROC 1 120: { "_avenrun" }, 121: #define NL_AVEN 2 122: #ifdef VMUNIX 123: { "_nproc" }, 124: #define NL_NPROC 3 125: #endif 126: 0 127: }; 128: 129: /* stuff for the kernel */ 130: int kmem; /* file descriptor for /dev/kmem */ 131: struct proc *proc, *procNPROC; 132: int nproc; 133: int procadr; 134: double avenrun[3]; /* used for storing load averages */ 135: 136: /* 137: * In order to determine how many people are logged on and who has 138: * logged in or out, we read in the /etc/utmp file. We also keep track of 139: * the previous utmp file. 140: */ 141: int ut = -1; /* the file descriptor */ 142: struct utmp *new, *old; 143: char *status; /* per tty status bits, see below */ 144: int nentries; /* number of utmp entries */ 145: /* string lengths for printing */ 146: #define LINESIZE (sizeof old->ut_line) 147: #define NAMESIZE (sizeof old->ut_name) 148: /* 149: * Status codes to say what has happened to a particular entry in utmp. 150: * NOCH means no change, ON means new person logged on, 151: * OFF means person logged off. 152: */ 153: #define NOCH 0 154: #define ON 0x1 155: #define OFF 0x2 156: 157: #ifdef WHO 158: char whofilename[100]; 159: char whofilename2[100]; 160: #endif 161: 162: #ifdef HOSTNAME 163: char hostname[MAXHOSTNAMELEN+1]; /* one more for null termination */ 164: #endif 165: 166: char lockfilename[100]; /* if exists, will prevent us from running */ 167: 168: /* flags which determine which info is printed */ 169: int mailcheck = 1; /* m - do biff like checking of mail */ 170: int proccheck = 1; /* p - give information on processes */ 171: int logcheck = 1; /* l - tell who logs in and out */ 172: int hostprint = 0; /* h - print out hostname */ 173: int dateprint = 0; /* h - print out day/date */ 174: int quiet = 0; /* q - hush diagnostic messages */ 175: 176: /* flags which determine how things are printed */ 177: int clr_bet_ref = 0; /* c - clear line between refeshes */ 178: int reverse = 1; /* r - use reverse video */ 179: int shortline = 0; /* s - short (left-justified) if escapes not allowed */ 180: int leftline = 0; /* j - left-justified even if escapes allowed */ 181: 182: /* flags which have terminal do random things */ 183: int beep = 0; /* b - beep every half hour and twice every hour */ 184: int printid = 0; /* i - print pid of this process at startup */ 185: int synch = 1; /* synchronize with clock */ 186: 187: /* select output device (status display or straight output) */ 188: int emacs = 0; /* e - assume status display */ 189: int window = 0; /* w - window mode */ 190: int dbug = 0; /* d - debug */ 191: 192: /* 193: * used to turn off reverse video every REVOFF times 194: * in an attempt to not wear out the phospher. 195: */ 196: #define REVOFF 5 197: int revtime = 1; 198: 199: /* used by mail checker */ 200: off_t mailsize = 0; 201: off_t linebeg = 0; /* place where we last left off reading */ 202: 203: /* things used by the string routines */ 204: int chars; /* number of printable characters */ 205: char *sp; 206: char strarr[512]; /* big enough now? */ 207: /* flags to stringdump() */ 208: char sawmail; /* remember mail was seen to print bells */ 209: char mustclear; /* status line messed up */ 210: 211: /* strings which control status line display */ 212: #ifdef TERMINFO 213: char *rev_out, *rev_end, *arrows; 214: char *tparm(); 215: #else 216: char to_status_line[64]; 217: char from_status_line[64]; 218: char dis_status_line[64]; 219: char clr_eol[64]; 220: char rev_out[20], rev_end[20]; 221: char *arrows, *bell = "\007"; 222: int eslok; /* escapes on status line okay (reverse, cursor addressing) */ 223: int columns; 224: #define tparm(cap, parm) tgoto((cap), 0, (parm)) 225: char *tgoto(); 226: #endif 227: 228: /* to deal with window size changes */ 229: #ifdef SIGWINCH 230: int sigwinch(); 231: char winchanged; /* window size has changed since last update */ 232: #endif 233: 234: /* random globals */ 235: char *username; 236: char *ourtty; /* keep track of what tty we're on */ 237: struct stat stbuf, mstbuf; /* mstbuf for mail check only */ 238: unsigned delay = DEFDELAY; 239: short uid; 240: double loadavg = 0.0; /* current load average */ 241: int users = 0; 242: 243: char *getenv(); 244: char *ttyname(); 245: char *strcpy1(); 246: char *sysrup(); 247: char *calloc(); 248: char *malloc(); 249: int outc(); 250: int erroutc(); 251: 252: main(argc,argv) 253: register char **argv; 254: { 255: int clearbotl(); 256: register char *cp; 257: char *home; 258: extern char _sobuf[]; 259: extern char *index(); 260: 261: setbuf(stdout, _sobuf); 262: 263: #ifdef HOSTNAME 264: gethostname(hostname, sizeof hostname - 1); 265: if ((cp = index(hostname, '.')) != NULL) 266: *cp = '\0'; 267: #endif 268: 269: for (argv++; *argv != 0; argv++) 270: switch (**argv) { 271: case '-': 272: for (cp = *argv + 1; *cp; cp++) { 273: switch(*cp) { 274: case 'r' : /* turn off reverse video */ 275: reverse = 0; 276: break; 277: case 'c': 278: clr_bet_ref = 1; 279: break; 280: case 'h': 281: hostprint = 1; 282: break; 283: case 'D': 284: dateprint = 1; 285: break; 286: #ifdef RWHO 287: case 'H': 288: if (argv[1] == 0) 289: break; 290: argv++; 291: if (strcmp(hostname, *argv) && 292: strcmp(&hostname[sizeof NETPREFIX - 1], *argv)) 293: remotehost[nremotes++].rh_host = *argv; 294: break; 295: #endif RWHO 296: case 'm': 297: mailcheck = 0; 298: break; 299: case 'p': 300: proccheck = 0; 301: break; 302: case 'l': 303: logcheck = 0; 304: break; 305: case 'b': 306: beep = 1; 307: break; 308: case 'i': 309: printid = 1; 310: break; 311: case 'w': 312: window = 1; 313: break; 314: case 'e': 315: emacs = 1; 316: break; 317: case 'd': 318: dbug = 1; 319: break; 320: case 'q': 321: quiet = 1; 322: break; 323: case 's': 324: shortline = 1; 325: break; 326: case 'j': 327: leftline = 1; 328: break; 329: default: 330: fprintf(stderr, 331: "sysline: bad flag: %c\n", *cp); 332: } 333: } 334: break; 335: case '+': 336: delay = atoi(*argv + 1); 337: if (delay < 10) 338: delay = 10; 339: else if (delay > 500) 340: delay = 500; 341: synch = 0; /* no more sync */ 342: break; 343: default: 344: fprintf(stderr, "sysline: illegal argument %s\n", 345: argv[0]); 346: } 347: if (emacs) { 348: reverse = 0; 349: columns = 79; 350: } else /* if not to emacs window, initialize terminal dependent info */ 351: initterm(); 352: #ifdef SIGWINCH 353: /* 354: * When the window size changes and we are the foreground 355: * process (true if -w), we get this signal. 356: */ 357: signal(SIGWINCH, sigwinch); 358: #endif 359: getwinsize(); /* get window size from ioctl */ 360: 361: /* immediately fork and let the parent die if not emacs mode */ 362: if (!emacs && !window && !dbug) { 363: if (fork()) 364: exit(0); 365: /* pgrp should take care of things, but ignore them anyway */ 366: signal(SIGINT, SIG_IGN); 367: signal(SIGQUIT, SIG_IGN); 368: #ifdef VMUNIX 369: signal(SIGTTOU, SIG_IGN); 370: #endif 371: } 372: /* 373: * When we logoff, init will do a "vhangup()" on this 374: * tty which turns off I/O access and sends a SIGHUP 375: * signal. We catch this and thereby clear the status 376: * display. Note that a bug in 4.1bsd caused the SIGHUP 377: * signal to be sent to the wrong process, so you had to 378: * `kill -HUP' yourself in your .logout file. 379: * Do the same thing for SIGTERM, which is the default kill 380: * signal. 381: */ 382: signal(SIGHUP, clearbotl); 383: signal(SIGTERM, clearbotl); 384: /* 385: * This is so kill -ALRM to force update won't screw us up.. 386: */ 387: signal(SIGALRM, SIG_IGN); 388: 389: uid = getuid(); 390: ourtty = ttyname(2); /* remember what tty we are on */ 391: if (printid) { 392: printf("%d\n", getpid()); 393: fflush(stdout); 394: } 395: dup2(2, 1); 396: 397: if ((home = getenv("HOME")) == 0) 398: home = ""; 399: strcpy1(strcpy1(whofilename, home), "/.who"); 400: strcpy1(strcpy1(whofilename2, home), "/.sysline"); 401: strcpy1(strcpy1(lockfilename, home), "/.syslinelock"); 402: 403: if ((kmem = open("/dev/kmem",0)) < 0) { 404: fprintf(stderr, "Can't open kmem.\n"); 405: exit(1); 406: } 407: readnamelist(); 408: if (proccheck) 409: initprocread(); 410: if (mailcheck) 411: if ((username = getenv("USER")) == 0) 412: mailcheck = 0; 413: else { 414: chdir(MAILDIR); 415: if (stat(username, &mstbuf) >= 0) 416: mailsize = mstbuf.st_size; 417: else 418: mailsize = 0; 419: } 420: 421: while (emacs || window || isloggedin()) 422: if (access(lockfilename, 0) >= 0) 423: sleep(60); 424: else { 425: prtinfo(); 426: sleep(delay); 427: if (clr_bet_ref) { 428: tputs(dis_status_line, 1, outc); 429: fflush(stdout); 430: sleep(5); 431: } 432: revtime = (1 + revtime) % REVOFF; 433: } 434: clearbotl(); 435: /*NOTREACHED*/ 436: } 437: 438: isloggedin() 439: { 440: /* 441: * you can tell if a person has logged out if the owner of 442: * the tty has changed 443: */ 444: struct stat statbuf; 445: 446: return fstat(2, &statbuf) == 0 && statbuf.st_uid == uid; 447: } 448: 449: readnamelist() 450: { 451: time_t bootime, clock, nintv, time(); 452: 453: #ifdef pdp11 454: nlist("/unix", nl); 455: #else 456: nlist("/vmunix", nl); 457: #endif 458: if (nl[0].n_value == 0) { 459: if (!quiet) 460: fprintf(stderr, "No namelist\n"); 461: return; 462: } 463: lseek(kmem, (long)nl[NL_BOOT].n_value, 0); 464: read(kmem, &bootime, sizeof(bootime)); 465: (void) time(&clock); 466: nintv = clock - bootime; 467: if (nintv <= 0L || nintv > 60L*60L*24L*365L) { 468: if (!quiet) 469: fprintf(stderr, 470: "Time makes no sense... namelist must be wrong\n"); 471: nl[NL_PROC].n_value = nl[NL_AVEN].n_value = 0; 472: } 473: } 474: 475: readutmp(nflag) 476: char nflag; 477: { 478: static time_t lastmod; /* initially zero */ 479: static off_t utmpsize; /* ditto */ 480: struct stat st; 481: 482: if (ut < 0 && (ut = open("/etc/utmp", 0)) < 0) { 483: fprintf(stderr, "sysline: Can't open utmp.\n"); 484: exit(1); 485: } 486: if (fstat(ut, &st) < 0 || st.st_mtime == lastmod) 487: return 0; 488: lastmod = st.st_mtime; 489: if (utmpsize != st.st_size) { 490: utmpsize = st.st_size; 491: nentries = utmpsize / sizeof (struct utmp); 492: if (old == 0) { 493: old = (struct utmp *)calloc(utmpsize, 1); 494: new = (struct utmp *)calloc(utmpsize, 1); 495: } else { 496: old = (struct utmp *)realloc((char *)old, utmpsize); 497: new = (struct utmp *)realloc((char *)new, utmpsize); 498: free(status); 499: } 500: status = malloc(nentries * sizeof *status); 501: if (old == 0 || new == 0 || status == 0) { 502: fprintf(stderr, "sysline: Out of memory.\n"); 503: exit(1); 504: } 505: } 506: lseek(ut, 0L, 0); 507: (void) read(ut, (char *) (nflag ? new : old), utmpsize); 508: return 1; 509: } 510: 511: /* 512: * read in the process table locations and sizes, and allocate space 513: * for storing the process table. This is done only once. 514: */ 515: initprocread() 516: { 517: 518: if (nl[NL_PROC].n_value == 0) 519: return; 520: #ifdef VMUNIX 521: lseek(kmem, (long)nl[NL_PROC].n_value, 0); 522: read(kmem, &procadr, sizeof procadr); 523: lseek(kmem, (long)nl[NL_NPROC].n_value, 0); 524: read(kmem, &nproc, sizeof nproc); 525: #endif 526: #ifdef pdp11 527: procadr = nl[NL_PROC].n_value; 528: nproc = NPROC; /* from param.h */ 529: #endif 530: if ((proc = (struct proc *) calloc(nproc, sizeof (struct proc))) == 0) { 531: fprintf(stderr, "Out of memory.\n"); 532: exit(1); 533: } 534: procNPROC = proc + nproc; 535: } 536: 537: /* 538: * read in the process table. This assumes that initprocread has alread been 539: * called to set up storage. 540: */ 541: readproctab() 542: { 543: 544: if (nl[NL_PROC].n_value == 0) 545: return (0); 546: lseek(kmem, (long)procadr, 0); 547: read(kmem, (char *)proc, nproc * sizeof (struct proc)); 548: return (1); 549: } 550: 551: prtinfo() 552: { 553: int on, off; 554: register i; 555: char fullprocess; 556: 557: stringinit(); 558: #ifdef SIGWINCH 559: if (winchanged) { 560: winchanged = 0; 561: getwinsize(); 562: mustclear = 1; 563: } 564: #endif 565: #ifdef WHO 566: /* check for file named .who in the home directory */ 567: whocheck(); 568: #endif 569: timeprint(); 570: /* 571: * if mail is seen, don't print rest of info, just the mail 572: * reverse new and old so that next time we run, we won't lose log 573: * in and out information 574: */ 575: if (mailcheck && (sawmail = mailseen())) 576: goto bottom; 577: #ifdef HOSTNAME 578: #ifdef RWHO 579: for (i = 0; i < nremotes; i++) { 580: char *tmp; 581: 582: stringspace(); 583: tmp = sysrup(remotehost + i); 584: stringcat(tmp, strlen(tmp)); 585: } 586: #endif 587: /* 588: * print hostname info if requested 589: */ 590: if (hostprint) { 591: stringspace(); 592: stringcat(hostname, -1); 593: } 594: #endif 595: /* 596: * print load average and difference between current load average 597: * and the load average 5 minutes ago 598: */ 599: if (nl[NL_AVEN].n_value != 0) { 600: double diff; 601: 602: stringspace(); 603: #ifdef VMUNIX 604: lseek(kmem, (long)nl[NL_AVEN].n_value, 0); 605: read(kmem, avenrun, sizeof avenrun); 606: #endif 607: #ifdef pdp11 608: loadav(avenrun); 609: #endif 610: if ((diff = avenrun[0] - avenrun[1]) < 0.0) 611: stringprt("%.1f %.1f", avenrun[0], diff); 612: else 613: stringprt("%.1f +%.1f", avenrun[0], diff); 614: loadavg = avenrun[0]; /* remember load average */ 615: } 616: /* 617: * print log on and off information 618: */ 619: stringspace(); 620: fullprocess = 1; 621: #ifdef MAXLOAD 622: if (loadavg > MAXLOAD) 623: fullprocess = 0; /* too loaded to run */ 624: #endif 625: /* 626: * Read utmp file (logged in data) only if we are doing a full 627: * process, or if this is the first time and we are calculating 628: * the number of users. 629: */ 630: on = off = 0; 631: if (users == 0) { /* first time */ 632: if (readutmp(0)) 633: for (i = 0; i < nentries; i++) 634: if (old[i].ut_name[0]) 635: users++; 636: } else if (fullprocess && readutmp(1)) { 637: struct utmp *tmp; 638: 639: users = 0; 640: for (i = 0; i < nentries; i++) { 641: if (strncmp(old[i].ut_name, 642: new[i].ut_name, NAMESIZE) == 0) 643: status[i] = NOCH; 644: else if (old[i].ut_name[0] == '\0') { 645: status[i] = ON; 646: on++; 647: } else if (new[i].ut_name[0] == '\0') { 648: status[i] = OFF; 649: off++; 650: } else { 651: status[i] = ON | OFF; 652: on++; 653: off++; 654: } 655: if (new[i].ut_name[0]) 656: users++; 657: } 658: tmp = new; 659: new = old; 660: old = tmp; 661: } 662: /* 663: * Print: 664: * 1. number of users 665: * 2. a * for unread mail 666: * 3. a - if load is too high 667: * 4. number of processes running and stopped 668: */ 669: stringprt("%du", users); 670: if (mailsize > 0 && mstbuf.st_mtime >= mstbuf.st_atime) 671: stringcat("*", -1); 672: if (!fullprocess && (proccheck || logcheck)) 673: stringcat("-", -1); 674: if (fullprocess && proccheck && readproctab()) { 675: register struct proc *p; 676: int procrun, procstop; 677: 678: /* 679: * We are only interested in processes which have the same 680: * uid as us, and whose parent process id is not 1. 681: */ 682: procrun = procstop = 0; 683: for (p = proc; p < procNPROC; p++) { 684: if (p->p_stat == 0 || p->p_pgrp == 0 || 685: p->p_uid != uid || p->p_ppid == 1) 686: continue; 687: switch (p->p_stat) { 688: case SSTOP: 689: procstop++; 690: break; 691: case SSLEEP: 692: /* 693: * Sleep can mean waiting for a signal or just 694: * in a disk or page wait queue ready to run. 695: * We can tell if it is the later by the pri 696: * being negative. 697: */ 698: if (p->p_pri < PZERO) 699: procrun++; 700: break; 701: case SWAIT: 702: case SRUN: 703: case SIDL: 704: procrun++; 705: } 706: } 707: if (procrun > 0 || procstop > 0) { 708: stringspace(); 709: if (procrun > 0 && procstop > 0) 710: stringprt("%dr %ds", procrun, procstop); 711: else if (procrun > 0) 712: stringprt("%dr", procrun); 713: else 714: stringprt("%ds", procstop); 715: } 716: } 717: /* 718: * If anyone has logged on or off, and we are interested in it, 719: * print it out. 720: */ 721: if (logcheck) { 722: /* old and new have already been swapped */ 723: if (on) { 724: stringspace(); 725: stringcat("on:", -1); 726: for (i = 0; i < nentries; i++) 727: if (status[i] & ON) { 728: stringprt(" %.8s", old[i].ut_name); 729: ttyprint(old[i].ut_line); 730: } 731: } 732: if (off) { 733: stringspace(); 734: stringcat("off:", -1); 735: for (i = 0; i < nentries; i++) 736: if (status[i] & OFF) { 737: stringprt(" %.8s", new[i].ut_name); 738: ttyprint(new[i].ut_line); 739: } 740: } 741: } 742: bottom: 743: /* dump out what we know */ 744: stringdump(); 745: } 746: 747: timeprint() 748: { 749: long curtime; 750: struct tm *tp, *localtime(); 751: static int beepable = 1; 752: 753: /* always print time */ 754: time(&curtime); 755: tp = localtime(&curtime); 756: if (dateprint) 757: stringprt("%.11s", ctime(&curtime)); 758: stringprt("%d:%02d", tp->tm_hour > 12 ? tp->tm_hour - 12 : 759: (tp->tm_hour == 0 ? 12 : tp->tm_hour), tp->tm_min); 760: if (synch) /* sync with clock */ 761: delay = 60 - tp->tm_sec; 762: /* 763: * Beepable is used to insure that we get at most one set of beeps 764: * every half hour. 765: */ 766: if (beep) 767: if (beepable) { 768: if (tp->tm_min == 30) { 769: tputs(bell, 1, outc); 770: fflush(stdout); 771: beepable = 0; 772: } else if (tp->tm_min == 0) { 773: tputs(bell, 1, outc); 774: fflush(stdout); 775: sleep(2); 776: tputs(bell, 1, outc); 777: fflush(stdout); 778: beepable = 0; 779: } 780: } else 781: if (tp->tm_min != 0 && tp->tm_min != 30) 782: beepable = 1; 783: } 784: 785: /* 786: * whocheck -- check for file named .who and print it on the who line first 787: */ 788: whocheck() 789: { 790: int chss; 791: register char *p; 792: char buff[81]; 793: int whofile; 794: 795: if ((whofile = open(whofilename, 0)) < 0 && 796: (whofile = open(whofilename2, 0)) < 0) 797: return; 798: chss = read(whofile, buff, sizeof buff - 1); 799: close(whofile); 800: if (chss <= 0) 801: return; 802: buff[chss] = '\0'; 803: /* 804: * Remove all line feeds, and replace by spaces if they are within 805: * the message, else replace them by nulls. 806: */ 807: for (p = buff; *p;) 808: if (*p == '\n') 809: if (p[1]) 810: *p++ = ' '; 811: else 812: *p = '\0'; 813: else 814: p++; 815: stringcat(buff, p - buff); 816: stringspace(); 817: } 818: 819: /* 820: * ttyprint -- given the name of a tty, print in the string buffer its 821: * short name surrounded by parenthesis. 822: * ttyxx is printed as (xx) 823: * console is printed as (cty) 824: */ 825: ttyprint(name) 826: char *name; 827: { 828: char buff[11]; 829: 830: if (strncmp(name, "tty", 3) == 0) 831: stringprt("(%.*s)", LINESIZE - 3, name + 3); 832: else if (strcmp(name, "console") == 0) 833: stringcat("(cty)", -1); 834: else 835: stringprt("(%.*s)", LINESIZE, name); 836: } 837: 838: /* 839: * mail checking function 840: * returns 0 if no mail seen 841: */ 842: mailseen() 843: { 844: FILE *mfd; 845: register n; 846: register char *cp; 847: char lbuf[100], sendbuf[100], *bufend; 848: char seenspace; 849: int retval = 0; 850: 851: if (stat(username, &mstbuf) < 0) { 852: mailsize = 0; 853: return 0; 854: } 855: if (mstbuf.st_size <= mailsize || (mfd = fopen(username,"r")) == NULL) { 856: mailsize = mstbuf.st_size; 857: return 0; 858: } 859: fseek(mfd, mailsize, 0); 860: while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0 && 861: strncmp(lbuf, "From ", 5) != 0) 862: ; 863: if (n < 0) { 864: stringcat("Mail has just arrived", 0); 865: goto out; 866: } 867: retval = 1; 868: /* 869: * Found a From line, get second word, which is the sender, 870: * and print it. 871: */ 872: for (cp = lbuf + 5; *cp && *cp != ' '; cp++) /* skip to blank */ 873: ; 874: *cp = '\0'; /* terminate name */ 875: stringspace(); 876: stringprt("Mail from %s ", lbuf + 5); 877: /* 878: * Print subject, and skip over header. 879: */ 880: while ((n = readline(mfd, lbuf, sizeof lbuf)) > 0) 881: if (strncmp(lbuf, "Subject:", 8) == 0) 882: stringprt("on %s ", lbuf + 9); 883: if (!emacs) 884: stringcat(arrows, 2); 885: else 886: stringcat(": ", 2); 887: if (n < 0) /* already at eof */ 888: goto out; 889: /* 890: * Print as much of the letter as we can. 891: */ 892: cp = sendbuf; 893: if ((n = columns - chars) > sizeof sendbuf - 1) 894: n = sizeof sendbuf - 1; 895: bufend = cp + n; 896: seenspace = 0; 897: while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0) { 898: register char *rp; 899: 900: if (strncmp(lbuf, "From ", 5) == 0) 901: break; 902: if (cp >= bufend) 903: continue; 904: if (!seenspace) { 905: *cp++ = ' '; /* space before lines */ 906: seenspace = 1; 907: } 908: rp = lbuf; 909: while (*rp && cp < bufend) 910: if (isspace(*rp)) { 911: if (!seenspace) { 912: *cp++ = ' '; 913: seenspace = 1; 914: } 915: rp++; 916: } else { 917: *cp++ = *rp++; 918: seenspace = 0; 919: } 920: } 921: *cp = 0; 922: stringcat(sendbuf, -1); 923: /* 924: * Want to update write time so a star will 925: * appear after the number of users until the 926: * user reads his mail. 927: */ 928: out: 929: mailsize = linebeg; 930: fclose(mfd); 931: touch(username); 932: return retval; 933: } 934: 935: /* 936: * readline -- read a line from fp and store it in buf. 937: * return the number of characters read. 938: */ 939: readline(fp, buf, n) 940: register FILE *fp; 941: char *buf; 942: register n; 943: { 944: register c; 945: register char *cp = buf; 946: 947: linebeg = ftell(fp); /* remember loc where line begins */ 948: cp = buf; 949: while (--n > 0 && (c = getc(fp)) != EOF && c != '\n') 950: *cp++ = c; 951: *cp = 0; 952: if (c == EOF && cp - buf == 0) 953: return -1; 954: return cp - buf; 955: } 956: 957: 958: /* 959: * string hacking functions 960: */ 961: 962: stringinit() 963: { 964: sp = strarr; 965: chars = 0; 966: } 967: 968: /*VARARGS1*/ 969: stringprt(format, a, b, c) 970: char *format; 971: { 972: char tempbuf[150]; 973: 974: stringcat(sprintf(tempbuf, format, a, b, c), -1); 975: } 976: 977: stringdump() 978: { 979: char bigbuf[sizeof strarr + 200]; 980: register char *bp = bigbuf; 981: register int i; 982: 983: if (!emacs) { 984: if (sawmail) 985: bp = strcpy1(bp, bell); 986: if (eslok) 987: bp = strcpy1(bp, tparm(to_status_line, 988: leftline ? 0 : columns - chars)); 989: else { 990: bp = strcpy1(bp, to_status_line); 991: if (!shortline && !leftline) 992: for (i = columns - chars; --i >= 0;) 993: *bp++ = ' '; 994: } 995: if (reverse && revtime != 0) 996: bp = strcpy1(bp, rev_out); 997: } 998: *sp = 0; 999: bp = strcpy1(bp, strarr); 1000: if (!emacs) { 1001: if (reverse) 1002: bp = strcpy1(bp, rev_end); 1003: bp = strcpy1(bp, from_status_line); 1004: if (sawmail) 1005: bp = strcpy1(strcpy1(bp, bell), bell); 1006: *bp = 0; 1007: tputs(bigbuf, 1, outc); 1008: if (mustclear) { 1009: mustclear = 0; 1010: tputs(clr_eol, 1, outc); 1011: } 1012: if (dbug) 1013: putchar('\n'); 1014: fflush(stdout); 1015: } else 1016: write(2, bigbuf, bp - bigbuf); 1017: } 1018: 1019: stringspace() 1020: { 1021: if (reverse && revtime != 0) { 1022: #ifdef TERMINFO 1023: stringcat(rev_end, 1024: magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch); 1025: stringcat(" ", 1); 1026: stringcat(rev_out, 1027: magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch); 1028: #else 1029: stringcat(rev_end, 0); 1030: stringcat(" ", 1); 1031: stringcat(rev_out, 0); 1032: #endif TERMINFO 1033: } else 1034: stringcat(" ", 1); 1035: } 1036: 1037: /* 1038: * stringcat :: concatenate the characters in string str to the list we are 1039: * building to send out. 1040: * str - the string to print. may contain funny (terminal control) chars. 1041: * n - the number of printable characters in the string 1042: * or if -1 then str is all printable so we can truncate it, 1043: * otherwise don't print only half a string. 1044: */ 1045: stringcat(str, n) 1046: register char *str; 1047: register n; 1048: { 1049: register char *p = sp; 1050: 1051: if (n < 0) { /* truncate */ 1052: n = columns - chars; 1053: while ((*p++ = *str++) && --n >= 0) 1054: ; 1055: p--; 1056: chars += p - sp; 1057: sp = p; 1058: } else if (chars + n <= columns) { /* don't truncate */ 1059: while (*p++ = *str++) 1060: ; 1061: chars += n; 1062: sp = p - 1; 1063: } 1064: } 1065: 1066: /* 1067: * touch :: update the modify time of a file. 1068: */ 1069: touch(name) 1070: char *name; /* name of file */ 1071: { 1072: register fd; 1073: char buf; 1074: 1075: if ((fd = open(name, 2)) >= 0) { 1076: read(fd, &buf, 1); /* get first byte */ 1077: lseek(fd, 0L, 0); /* go to beginning */ 1078: write(fd, &buf, 1); /* and rewrite first byte */ 1079: close(fd); 1080: } 1081: } 1082: 1083: 1084: /* 1085: * clearbotl :: clear bottom line. 1086: * called when process quits or is killed. 1087: * it clears the bottom line of the terminal. 1088: */ 1089: clearbotl() 1090: { 1091: register int fd; 1092: int exit(); 1093: 1094: signal(SIGALRM, exit); 1095: alarm(30); /* if can't open in 30 secs, just die */ 1096: if (!emacs && (fd = open(ourtty, 1)) >= 0) { 1097: write(fd, dis_status_line, strlen(dis_status_line)); 1098: close(fd); 1099: } 1100: #ifdef PROF 1101: if (chdir("/usr/src/ucb/sysline") < 0) 1102: (void) chdir("/tmp"); 1103: #endif 1104: exit(0); 1105: } 1106: 1107: #ifdef TERMINFO 1108: initterm() 1109: { 1110: static char standbuf[40]; 1111: 1112: setupterm(0, 1, 0); 1113: if (!window && !has_status_line) { 1114: /* not an appropriate terminal */ 1115: if (!quiet) 1116: fprintf(stderr, "sysline: no status capability for %s\n", 1117: getenv("TERM")); 1118: exit(1); 1119: } 1120: if (window || status_line_esc_ok) { 1121: if (set_attributes) { 1122: /* reverse video mode */ 1123: strcpy(standbuf, 1124: tparm(set_attributes,0,0,1,0,0,0,0,0,0)); 1125: rev_out = standbuf; 1126: rev_end = exit_attribute_mode; 1127: } else if (enter_standout_mode && exit_standout_mode) { 1128: rev_out = enter_standout_mode; 1129: rev_end = exit_standout_mode; 1130: } else 1131: rev_out = rev_end = ""; 1132: } else 1133: rev_out = rev_end = ""; 1134: columns--; /* avoid cursor wraparound */ 1135: } 1136: 1137: #else /* TERMCAP */ 1138: 1139: initterm() 1140: { 1141: char *term, *cp; 1142: static char tbuf[1024]; 1143: char is2[40]; 1144: extern char *UP; 1145: 1146: if ((term = getenv("TERM")) == NULL) { 1147: if (!quiet) 1148: fprintf(stderr, 1149: "sysline: No TERM variable in enviroment\n"); 1150: exit(1); 1151: } 1152: if (tgetent(tbuf, term) <= 0) { 1153: if (!quiet) 1154: fprintf(stderr, 1155: "sysline: Unknown terminal type: %s\n", term); 1156: exit(1); 1157: } 1158: if (!window && tgetflag("hs") <= 0) { 1159: if (!strncmp(term, "h19", 3)) { 1160: /* for upward compatability with h19sys */ 1161: strcpy(to_status_line, 1162: "\033j\033x5\033x1\033Y8%+ \033o"); 1163: strcpy(from_status_line, "\033k\033y5"); 1164: strcpy(dis_status_line, "\033y1"); 1165: strcpy(rev_out, "\033p"); 1166: strcpy(rev_end, "\033q"); 1167: arrows = "\033Fhh\033G"; 1168: columns = 80; 1169: UP = "\b"; 1170: return; 1171: } 1172: if (!quiet) 1173: fprintf(stderr, 1174: "sysline: No status capability for %s\n", term); 1175: exit(1); 1176: } 1177: cp = is2; 1178: if (tgetstr("i2", &cp) != NULL) { 1179: /* someday tset will do this */ 1180: tputs(is2, 1, erroutc); 1181: fflush(stdout); 1182: } 1183: 1184: /* the "-1" below is to avoid cursor wraparound problems */ 1185: columns = tgetnum("co") - 1; 1186: if (window) { 1187: strcpy(to_status_line, "\r"); 1188: cp = dis_status_line; /* use the clear line sequence */ 1189: *cp++ = '\r'; 1190: tgetstr("ce", &cp); 1191: if (leftline) 1192: strcpy(from_status_line, dis_status_line + 1); 1193: else 1194: strcpy(from_status_line, ""); 1195: } else { 1196: cp = to_status_line; 1197: tgetstr("ts", &cp); 1198: cp = from_status_line; 1199: tgetstr("fs", &cp); 1200: cp = dis_status_line; 1201: tgetstr("ds", &cp); 1202: eslok = tgetflag("es"); 1203: } 1204: if (eslok || window) { 1205: cp = rev_out; 1206: tgetstr("so", &cp); 1207: cp = rev_end; 1208: tgetstr("se", &cp); 1209: cp = clr_eol; 1210: tgetstr("ce", &cp); 1211: } else 1212: reverse = 0; /* turn off reverse video */ 1213: UP = "\b"; 1214: if (!strncmp(term, "h19", 3)) 1215: arrows = "\033Fhh\033G"; /* "two tiny graphic arrows" */ 1216: else 1217: arrows = "->"; 1218: } 1219: #endif TERMINFO 1220: 1221: #ifdef pdp11 1222: loadav(ap) 1223: double ap[]; 1224: { 1225: register int i; 1226: short s_avenrun[3]; 1227: 1228: lseek(kmem, (long)nl[NL_AVEN].n_value, 0); 1229: read(kmem, s_avenrun, sizeof(s_avenrun)); 1230: for (i=0; i < (sizeof(s_avenrun)/sizeof(s_avenrun[0])); i++) 1231: ap[i] = s_avenrun[i] / 256.0; 1232: } 1233: #endif 1234: 1235: #ifdef RWHO 1236: char * 1237: sysrup(hp) 1238: register struct remotehost *hp; 1239: { 1240: char filename[100]; 1241: struct whod wd; 1242: #define WHOD_HDR_SIZE (sizeof (wd) - sizeof (wd.wd_we)) 1243: static char buffer[50]; 1244: time_t now; 1245: 1246: /* 1247: * rh_file is initially 0. 1248: * This is ok since standard input is assumed to exist. 1249: */ 1250: if (hp->rh_file == 0) { 1251: /* 1252: * Try rwho hostname file, and if that fails try ucbhostname. 1253: */ 1254: (void) strcpy1(strcpy1(filename, RWHOLEADER), hp->rh_host); 1255: if ((hp->rh_file = open(filename, 0)) < 0) { 1256: (void) strcpy1(strcpy1(strcpy1(filename, RWHOLEADER), 1257: NETPREFIX), hp->rh_host); 1258: hp->rh_file = open(filename, 0); 1259: } 1260: } 1261: if (hp->rh_file < 0) 1262: return sprintf(buffer, "%s?", hp->rh_host); 1263: (void) lseek(hp->rh_file, (off_t)0, 0); 1264: if (read(hp->rh_file, (char *)&wd, WHOD_HDR_SIZE) != WHOD_HDR_SIZE) 1265: return sprintf(buffer, "%s ?", hp->rh_host); 1266: (void) time(&now); 1267: if (now - wd.wd_recvtime > DOWN_THRESHOLD) { 1268: long interval; 1269: long days, hours, minutes; 1270: 1271: interval = now - wd.wd_recvtime; 1272: minutes = (interval + 59) / 60; /* round to minutes */ 1273: hours = minutes / 60; /* extract hours from minutes */ 1274: minutes %= 60; /* remove hours from minutes */ 1275: days = hours / 24; /* extract days from hours */ 1276: hours %= 24; /* remove days from hours */ 1277: if (days > 7 || days < 0) 1278: (void) sprintf(buffer, "%s down", hp->rh_host); 1279: else if (days > 0) 1280: (void) sprintf(buffer, "%s %d+%d:%02d", 1281: hp->rh_host, days, hours, minutes); 1282: else 1283: (void) sprintf(buffer, "%s %d:%02d", 1284: hp->rh_host, hours, minutes); 1285: } else 1286: (void) sprintf(buffer, "%s %.1f", 1287: hp->rh_host, wd.wd_loadav[0]/100.0); 1288: return buffer; 1289: } 1290: #endif RWHO 1291: 1292: getwinsize() 1293: { 1294: #ifdef TIOCGWINSZ 1295: struct winsize winsize; 1296: 1297: /* the "-1" below is to avoid cursor wraparound problems */ 1298: if (ioctl(2, TIOCGWINSZ, (char *)&winsize) >= 0 && winsize.ws_col != 0) 1299: columns = winsize.ws_col - 1; 1300: #endif 1301: } 1302: 1303: #ifdef SIGWINCH 1304: sigwinch() 1305: { 1306: winchanged++; 1307: } 1308: #endif 1309: 1310: char * 1311: strcpy1(p, q) 1312: register char *p, *q; 1313: { 1314: 1315: while (*p++ = *q++) 1316: ; 1317: return p - 1; 1318: } 1319: 1320: outc(c) 1321: char c; 1322: { 1323: if (dbug) 1324: printf("%s", unctrl(c)); 1325: else 1326: putchar(c); 1327: } 1328: 1329: erroutc(c) 1330: char c; 1331: { 1332: if (dbug) 1333: fprintf(stderr, "%s", unctrl(c)); 1334: else 1335: putc(c, stderr); 1336: }