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