new copyright notice
[unix-history] / usr / src / old / sysline / sysline.c
CommitLineData
e1e8d3ca
KM
1/*
2 * Copyright (c) 1980 Regents of the University of California.
f9e4427b
KB
3 * All rights reserved.
4 *
cb956e54 5 * %sccs.include.redist.c%
e1e8d3ca
KM
6 */
7
8#ifndef lint
9char copyright[] =
10"@(#) Copyright (c) 1980 Regents of the University of California.\n\
11 All rights reserved.\n";
f9e4427b 12#endif /* not lint */
e1e8d3ca
KM
13
14#ifndef lint
cb956e54 15static char sccsid[] = "@(#)sysline.c 5.15 (Berkeley) %G%";
f9e4427b 16#endif /* not lint */
e1e8d3ca
KM
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 */
e1e8d3ca
KM
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>
bcc006b0 75#include <sys/types.h>
e1e8d3ca
KM
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
6efb822a
MK
100#include <protocols/rwhod.h>
101
e1e8d3ca 102#define DOWN_THRESHOLD (11 * 60)
e1e8d3ca 103
e1e8d3ca
KM
104struct remotehost {
105 char *rh_host;
106 int rh_file;
107} remotehost[10];
108int nremotes = 0;
109#endif RWHO
110
b528bd5e
KB
111#include "pathnames.h"
112
e1e8d3ca
KM
113struct 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
e1e8d3ca
KM
122#ifdef VMUNIX
123 { "_nproc" },
bcc006b0 124#define NL_NPROC 2
e1e8d3ca
KM
125#endif
126 0
127};
128
129 /* stuff for the kernel */
b528bd5e 130int kmem; /* file descriptor for _PATH_KMEM */
e1e8d3ca
KM
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) */
f9e4427b 223int hasws = 0; /* is "ws" explicitly defined? */
e1e8d3ca
KM
224int columns;
225#define tparm(cap, parm) tgoto((cap), 0, (parm))
226char *tgoto();
227#endif
228
229 /* to deal with window size changes */
230#ifdef SIGWINCH
231int sigwinch();
232char winchanged; /* window size has changed since last update */
233#endif
234
235 /* random globals */
236char *username;
237char *ourtty; /* keep track of what tty we're on */
238struct stat stbuf, mstbuf; /* mstbuf for mail check only */
239unsigned delay = DEFDELAY;
658d7227 240uid_t uid;
e1e8d3ca
KM
241double loadavg = 0.0; /* current load average */
242int users = 0;
243
244char *getenv();
245char *ttyname();
246char *strcpy1();
247char *sysrup();
248char *calloc();
249char *malloc();
250int outc();
251int erroutc();
252
253main(argc,argv)
254 register char **argv;
255{
256 int clearbotl();
257 register char *cp;
258 char *home;
897f1e78 259 extern char *index();
e1e8d3ca 260
e1e8d3ca
KM
261#ifdef HOSTNAME
262 gethostname(hostname, sizeof hostname - 1);
897f1e78
JB
263 if ((cp = index(hostname, '.')) != NULL)
264 *cp = '\0';
e1e8d3ca
KM
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
b528bd5e
KB
401 if ((kmem = open(_PATH_KMEM,0)) < 0) {
402 fprintf(stderr, "Can't open %s\n", _PATH_KMEM);
e1e8d3ca
KM
403 exit(1);
404 }
405 readnamelist();
406 if (proccheck)
407 initprocread();
e1e8d3ca
KM
408 if (mailcheck)
409 if ((username = getenv("USER")) == 0)
410 mailcheck = 0;
411 else {
b528bd5e 412 chdir(_PATH_MBOX);
e1e8d3ca
KM
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
436isloggedin()
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
447readnamelist()
448{
449 time_t bootime, clock, nintv, time();
450
b528bd5e 451 nlist(_PATH_UNIX, nl);
e1e8d3ca
KM
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");
bcc006b0 465 nl[NL_PROC].n_value = 0;
e1e8d3ca
KM
466 }
467}
468
4874204a
EW
469readutmp(nflag)
470 char nflag;
e1e8d3ca 471{
4874204a
EW
472 static time_t lastmod; /* initially zero */
473 static off_t utmpsize; /* ditto */
e1e8d3ca
KM
474 struct stat st;
475
b528bd5e
KB
476 if (ut < 0 && (ut = open(_PATH_UTMP, 0)) < 0) {
477 fprintf(stderr, "sysline: Can't open %s.\n", _PATH_UTMP);
e1e8d3ca
KM
478 exit(1);
479 }
4874204a 480 if (fstat(ut, &st) < 0 || st.st_mtime == lastmod)
e1e8d3ca
KM
481 return 0;
482 lastmod = st.st_mtime;
4874204a
EW
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 }
e1e8d3ca 500 lseek(ut, 0L, 0);
4874204a 501 (void) read(ut, (char *) (nflag ? new : old), utmpsize);
e1e8d3ca
KM
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 */
509initprocread()
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 */
535readproctab()
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
545prtinfo()
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 */
bcc006b0 593 if (getloadavg(avenrun, 3) > 0) {
e1e8d3ca
KM
594 double diff;
595
596 stringspace();
bcc006b0 597
e1e8d3ca
KM
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 */
4874204a 620 if (readutmp(0))
e1e8d3ca
KM
621 for (i = 0; i < nentries; i++)
622 if (old[i].ut_name[0])
623 users++;
4874204a 624 } else if (fullprocess && readutmp(1)) {
e1e8d3ca
KM
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 }
730bottom:
731 /* dump out what we know */
732 stringdump();
733}
734
735timeprint()
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 */
776whocheck()
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 */
813ttyprint(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 */
830mailseen()
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) {
f9e4427b 852 stringcat("Mail has just arrived", -1);
e1e8d3ca
KM
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 */
916out:
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 */
927readline(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
950stringinit()
951{
952 sp = strarr;
953 chars = 0;
954}
955
956/*VARARGS1*/
957stringprt(format, a, b, c)
958 char *format;
959{
960 char tempbuf[150];
961
06eef7c4
KB
962 (void)sprintf(tempbuf, format, a, b, c);
963 stringcat(tempbuf, -1);
e1e8d3ca
KM
964}
965
966stringdump()
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
1008stringspace()
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 */
1034stringcat(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 */
1058touch(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 */
1078clearbotl()
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
b528bd5e
KB
1090 if (chdir(_PATH_SYSLINE) < 0)
1091 (void) chdir(_PATH_TMP);
e1e8d3ca
KM
1092#endif
1093 exit(0);
1094}
1095
1096#ifdef TERMINFO
1097initterm()
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
1128initterm()
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 */
42f1384d 1174 columns = tgetnum("ws");
f9e4427b
KB
1175 hasws = columns >= 0;
1176 if (!hasws)
42f1384d
KB
1177 columns = tgetnum("co");
1178 columns -= 1;
e1e8d3ca
KM
1179 if (window) {
1180 strcpy(to_status_line, "\r");
e1e8d3ca
KM
1181 cp = dis_status_line; /* use the clear line sequence */
1182 *cp++ = '\r';
1183 tgetstr("ce", &cp);
e2e4d5a4
MK
1184 if (leftline)
1185 strcpy(from_status_line, dis_status_line + 1);
1186 else
1187 strcpy(from_status_line, "");
e1e8d3ca
KM
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
e1e8d3ca
KM
1214#ifdef RWHO
1215char *
1216sysrup(hp)
1217 register struct remotehost *hp;
1218{
1219 char filename[100];
1220 struct whod wd;
45ac4a67 1221#define WHOD_HDR_SIZE (sizeof (wd) - sizeof (wd.wd_we))
e1e8d3ca
KM
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 */
b528bd5e 1233 (void) strcpy1(strcpy1(filename, _PATH_RWHO), hp->rh_host);
e1e8d3ca 1234 if ((hp->rh_file = open(filename, 0)) < 0) {
b528bd5e 1235 (void) strcpy1(strcpy1(strcpy1(filename, _PATH_RWHO),
e1e8d3ca
KM
1236 NETPREFIX), hp->rh_host);
1237 hp->rh_file = open(filename, 0);
1238 }
1239 }
42f1384d
KB
1240 if (hp->rh_file < 0) {
1241 (void) sprintf(buffer, "%s?", hp->rh_host);
1242 return(buffer);
1243 }
e1e8d3ca 1244 (void) lseek(hp->rh_file, (off_t)0, 0);
42f1384d
KB
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 }
e1e8d3ca
KM
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
1275getwinsize()
1276{
1277#ifdef TIOCGWINSZ
1278 struct winsize winsize;
1279
1280 /* the "-1" below is to avoid cursor wraparound problems */
f9e4427b
KB
1281 if (!hasws && ioctl(2, TIOCGWINSZ, (char *)&winsize) >= 0 &&
1282 winsize.ws_col != 0)
e1e8d3ca
KM
1283 columns = winsize.ws_col - 1;
1284#endif
1285}
1286
1287#ifdef SIGWINCH
1288sigwinch()
1289{
1290 winchanged++;
1291}
1292#endif
1293
1294char *
1295strcpy1(p, q)
1296 register char *p, *q;
1297{
1298
1299 while (*p++ = *q++)
1300 ;
1301 return p - 1;
1302}
1303
1304outc(c)
1305 char c;
1306{
1307 if (dbug)
1308 printf("%s", unctrl(c));
1309 else
1310 putchar(c);
1311}
1312
1313erroutc(c)
1314 char c;
1315{
1316 if (dbug)
1317 fprintf(stderr, "%s", unctrl(c));
1318 else
1319 putc(c, stderr);
1320}