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