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