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