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.9 (Berkeley) 10/25/87"; 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 uid_t 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 (void)sprintf(tempbuf, format, a, b, c); 975 stringcat(tempbuf, -1); 976 } 977 978 stringdump() 979 { 980 char bigbuf[sizeof strarr + 200]; 981 register char *bp = bigbuf; 982 register int i; 983 984 if (!emacs) { 985 if (sawmail) 986 bp = strcpy1(bp, bell); 987 if (eslok) 988 bp = strcpy1(bp, tparm(to_status_line, 989 leftline ? 0 : columns - chars)); 990 else { 991 bp = strcpy1(bp, to_status_line); 992 if (!shortline && !leftline) 993 for (i = columns - chars; --i >= 0;) 994 *bp++ = ' '; 995 } 996 if (reverse && revtime != 0) 997 bp = strcpy1(bp, rev_out); 998 } 999 *sp = 0; 1000 bp = strcpy1(bp, strarr); 1001 if (!emacs) { 1002 if (reverse) 1003 bp = strcpy1(bp, rev_end); 1004 bp = strcpy1(bp, from_status_line); 1005 if (sawmail) 1006 bp = strcpy1(strcpy1(bp, bell), bell); 1007 *bp = 0; 1008 tputs(bigbuf, 1, outc); 1009 if (mustclear) { 1010 mustclear = 0; 1011 tputs(clr_eol, 1, outc); 1012 } 1013 if (dbug) 1014 putchar('\n'); 1015 fflush(stdout); 1016 } else 1017 write(2, bigbuf, bp - bigbuf); 1018 } 1019 1020 stringspace() 1021 { 1022 if (reverse && revtime != 0) { 1023 #ifdef TERMINFO 1024 stringcat(rev_end, 1025 magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch); 1026 stringcat(" ", 1); 1027 stringcat(rev_out, 1028 magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch); 1029 #else 1030 stringcat(rev_end, 0); 1031 stringcat(" ", 1); 1032 stringcat(rev_out, 0); 1033 #endif TERMINFO 1034 } else 1035 stringcat(" ", 1); 1036 } 1037 1038 /* 1039 * stringcat :: concatenate the characters in string str to the list we are 1040 * building to send out. 1041 * str - the string to print. may contain funny (terminal control) chars. 1042 * n - the number of printable characters in the string 1043 * or if -1 then str is all printable so we can truncate it, 1044 * otherwise don't print only half a string. 1045 */ 1046 stringcat(str, n) 1047 register char *str; 1048 register n; 1049 { 1050 register char *p = sp; 1051 1052 if (n < 0) { /* truncate */ 1053 n = columns - chars; 1054 while ((*p++ = *str++) && --n >= 0) 1055 ; 1056 p--; 1057 chars += p - sp; 1058 sp = p; 1059 } else if (chars + n <= columns) { /* don't truncate */ 1060 while (*p++ = *str++) 1061 ; 1062 chars += n; 1063 sp = p - 1; 1064 } 1065 } 1066 1067 /* 1068 * touch :: update the modify time of a file. 1069 */ 1070 touch(name) 1071 char *name; /* name of file */ 1072 { 1073 register fd; 1074 char buf; 1075 1076 if ((fd = open(name, 2)) >= 0) { 1077 read(fd, &buf, 1); /* get first byte */ 1078 lseek(fd, 0L, 0); /* go to beginning */ 1079 write(fd, &buf, 1); /* and rewrite first byte */ 1080 close(fd); 1081 } 1082 } 1083 1084 1085 /* 1086 * clearbotl :: clear bottom line. 1087 * called when process quits or is killed. 1088 * it clears the bottom line of the terminal. 1089 */ 1090 clearbotl() 1091 { 1092 register int fd; 1093 int exit(); 1094 1095 signal(SIGALRM, exit); 1096 alarm(30); /* if can't open in 30 secs, just die */ 1097 if (!emacs && (fd = open(ourtty, 1)) >= 0) { 1098 write(fd, dis_status_line, strlen(dis_status_line)); 1099 close(fd); 1100 } 1101 #ifdef PROF 1102 if (chdir("/usr/src/ucb/sysline") < 0) 1103 (void) chdir("/tmp"); 1104 #endif 1105 exit(0); 1106 } 1107 1108 #ifdef TERMINFO 1109 initterm() 1110 { 1111 static char standbuf[40]; 1112 1113 setupterm(0, 1, 0); 1114 if (!window && !has_status_line) { 1115 /* not an appropriate terminal */ 1116 if (!quiet) 1117 fprintf(stderr, "sysline: no status capability for %s\n", 1118 getenv("TERM")); 1119 exit(1); 1120 } 1121 if (window || status_line_esc_ok) { 1122 if (set_attributes) { 1123 /* reverse video mode */ 1124 strcpy(standbuf, 1125 tparm(set_attributes,0,0,1,0,0,0,0,0,0)); 1126 rev_out = standbuf; 1127 rev_end = exit_attribute_mode; 1128 } else if (enter_standout_mode && exit_standout_mode) { 1129 rev_out = enter_standout_mode; 1130 rev_end = exit_standout_mode; 1131 } else 1132 rev_out = rev_end = ""; 1133 } else 1134 rev_out = rev_end = ""; 1135 columns--; /* avoid cursor wraparound */ 1136 } 1137 1138 #else /* TERMCAP */ 1139 1140 initterm() 1141 { 1142 char *term, *cp; 1143 static char tbuf[1024]; 1144 char is2[40]; 1145 extern char *UP; 1146 1147 if ((term = getenv("TERM")) == NULL) { 1148 if (!quiet) 1149 fprintf(stderr, 1150 "sysline: No TERM variable in enviroment\n"); 1151 exit(1); 1152 } 1153 if (tgetent(tbuf, term) <= 0) { 1154 if (!quiet) 1155 fprintf(stderr, 1156 "sysline: Unknown terminal type: %s\n", term); 1157 exit(1); 1158 } 1159 if (!window && tgetflag("hs") <= 0) { 1160 if (!strncmp(term, "h19", 3)) { 1161 /* for upward compatability with h19sys */ 1162 strcpy(to_status_line, 1163 "\033j\033x5\033x1\033Y8%+ \033o"); 1164 strcpy(from_status_line, "\033k\033y5"); 1165 strcpy(dis_status_line, "\033y1"); 1166 strcpy(rev_out, "\033p"); 1167 strcpy(rev_end, "\033q"); 1168 arrows = "\033Fhh\033G"; 1169 columns = 80; 1170 UP = "\b"; 1171 return; 1172 } 1173 if (!quiet) 1174 fprintf(stderr, 1175 "sysline: No status capability for %s\n", term); 1176 exit(1); 1177 } 1178 cp = is2; 1179 if (tgetstr("i2", &cp) != NULL) { 1180 /* someday tset will do this */ 1181 tputs(is2, 1, erroutc); 1182 fflush(stdout); 1183 } 1184 1185 /* the "-1" below is to avoid cursor wraparound problems */ 1186 columns = tgetnum("ws"); 1187 if (columns < 0) 1188 columns = tgetnum("co"); 1189 columns -= 1; 1190 if (window) { 1191 strcpy(to_status_line, "\r"); 1192 cp = dis_status_line; /* use the clear line sequence */ 1193 *cp++ = '\r'; 1194 tgetstr("ce", &cp); 1195 if (leftline) 1196 strcpy(from_status_line, dis_status_line + 1); 1197 else 1198 strcpy(from_status_line, ""); 1199 } else { 1200 cp = to_status_line; 1201 tgetstr("ts", &cp); 1202 cp = from_status_line; 1203 tgetstr("fs", &cp); 1204 cp = dis_status_line; 1205 tgetstr("ds", &cp); 1206 eslok = tgetflag("es"); 1207 } 1208 if (eslok || window) { 1209 cp = rev_out; 1210 tgetstr("so", &cp); 1211 cp = rev_end; 1212 tgetstr("se", &cp); 1213 cp = clr_eol; 1214 tgetstr("ce", &cp); 1215 } else 1216 reverse = 0; /* turn off reverse video */ 1217 UP = "\b"; 1218 if (!strncmp(term, "h19", 3)) 1219 arrows = "\033Fhh\033G"; /* "two tiny graphic arrows" */ 1220 else 1221 arrows = "->"; 1222 } 1223 #endif TERMINFO 1224 1225 #ifdef pdp11 1226 loadav(ap) 1227 double ap[]; 1228 { 1229 register int i; 1230 short s_avenrun[3]; 1231 1232 lseek(kmem, (long)nl[NL_AVEN].n_value, 0); 1233 read(kmem, s_avenrun, sizeof(s_avenrun)); 1234 for (i=0; i < (sizeof(s_avenrun)/sizeof(s_avenrun[0])); i++) 1235 ap[i] = s_avenrun[i] / 256.0; 1236 } 1237 #endif 1238 1239 #ifdef RWHO 1240 char * 1241 sysrup(hp) 1242 register struct remotehost *hp; 1243 { 1244 char filename[100]; 1245 struct whod wd; 1246 #define WHOD_HDR_SIZE (sizeof (wd) - sizeof (wd.wd_we)) 1247 static char buffer[50]; 1248 time_t now; 1249 1250 /* 1251 * rh_file is initially 0. 1252 * This is ok since standard input is assumed to exist. 1253 */ 1254 if (hp->rh_file == 0) { 1255 /* 1256 * Try rwho hostname file, and if that fails try ucbhostname. 1257 */ 1258 (void) strcpy1(strcpy1(filename, RWHOLEADER), hp->rh_host); 1259 if ((hp->rh_file = open(filename, 0)) < 0) { 1260 (void) strcpy1(strcpy1(strcpy1(filename, RWHOLEADER), 1261 NETPREFIX), hp->rh_host); 1262 hp->rh_file = open(filename, 0); 1263 } 1264 } 1265 if (hp->rh_file < 0) { 1266 (void) sprintf(buffer, "%s?", hp->rh_host); 1267 return(buffer); 1268 } 1269 (void) lseek(hp->rh_file, (off_t)0, 0); 1270 if (read(hp->rh_file, (char *)&wd, WHOD_HDR_SIZE) != WHOD_HDR_SIZE) { 1271 (void) sprintf(buffer, "%s ?", hp->rh_host); 1272 return(buffer); 1273 } 1274 (void) time(&now); 1275 if (now - wd.wd_recvtime > DOWN_THRESHOLD) { 1276 long interval; 1277 long days, hours, minutes; 1278 1279 interval = now - wd.wd_recvtime; 1280 minutes = (interval + 59) / 60; /* round to minutes */ 1281 hours = minutes / 60; /* extract hours from minutes */ 1282 minutes %= 60; /* remove hours from minutes */ 1283 days = hours / 24; /* extract days from hours */ 1284 hours %= 24; /* remove days from hours */ 1285 if (days > 7 || days < 0) 1286 (void) sprintf(buffer, "%s down", hp->rh_host); 1287 else if (days > 0) 1288 (void) sprintf(buffer, "%s %d+%d:%02d", 1289 hp->rh_host, days, hours, minutes); 1290 else 1291 (void) sprintf(buffer, "%s %d:%02d", 1292 hp->rh_host, hours, minutes); 1293 } else 1294 (void) sprintf(buffer, "%s %.1f", 1295 hp->rh_host, wd.wd_loadav[0]/100.0); 1296 return buffer; 1297 } 1298 #endif RWHO 1299 1300 getwinsize() 1301 { 1302 #ifdef TIOCGWINSZ 1303 struct winsize winsize; 1304 1305 /* the "-1" below is to avoid cursor wraparound problems */ 1306 if (ioctl(2, TIOCGWINSZ, (char *)&winsize) >= 0 && winsize.ws_col != 0) 1307 columns = winsize.ws_col - 1; 1308 #endif 1309 } 1310 1311 #ifdef SIGWINCH 1312 sigwinch() 1313 { 1314 winchanged++; 1315 } 1316 #endif 1317 1318 char * 1319 strcpy1(p, q) 1320 register char *p, *q; 1321 { 1322 1323 while (*p++ = *q++) 1324 ; 1325 return p - 1; 1326 } 1327 1328 outc(c) 1329 char c; 1330 { 1331 if (dbug) 1332 printf("%s", unctrl(c)); 1333 else 1334 putchar(c); 1335 } 1336 1337 erroutc(c) 1338 char c; 1339 { 1340 if (dbug) 1341 fprintf(stderr, "%s", unctrl(c)); 1342 else 1343 putc(c, stderr); 1344 } 1345