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