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