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