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