BSD 4_4 release
[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 *
ad787160
C
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
e1e8d3ca
KM
32 */
33
34#ifndef lint
35char copyright[] =
36"@(#) Copyright (c) 1980 Regents of the University of California.\n\
37 All rights reserved.\n";
f9e4427b 38#endif /* not lint */
e1e8d3ca
KM
39
40#ifndef lint
ad787160 41static char sccsid[] = "@(#)sysline.c 5.18 (Berkeley) 3/4/91";
f9e4427b 42#endif /* not lint */
e1e8d3ca
KM
43
44/*
45 * sysline - system status display on 25th line of terminal
46 * j.k.foderaro
47 *
48 * Prints a variety of information on the special status line of terminals
49 * that have a status display capability. Cursor motions, status commands,
50 * etc. are gleamed from /etc/termcap.
51 * By default, all information is printed, and flags are given on the command
52 * line to disable the printing of information. The information and
53 * disabling flags are:
54 *
55 * flag what
56 * ----- ----
57 * time of day
58 * load average and change in load average in the last 5 mins
59 * number of user logged on
60 * -p # of processes the users owns which are runnable and the
61 * number which are suspended. Processes whose parent is 1
62 * are not counted.
63 * -l users who've logged on and off.
64 * -m summarize new mail which has arrived
65 *
66 * <other flags>
67 * -r use non reverse video
68 * -c turn off 25th line for 5 seconds before redisplaying.
69 * -b beep once one the half hour, twice on the hour
70 * +N refresh display every N seconds.
71 * -i print pid first thing
72 * -e do simple print designed for an emacs buffer line
73 * -w do the right things for a window
74 * -h print hostname between time and load average
75 * -D print day/date before time of day
76 * -d debug mode - print status line data in human readable format
77 * -q quiet mode - don't output diagnostic messages
78 * -s print Short (left-justified) line if escapes not allowed
79 * -j Print left Justified line regardless
80 */
81
82#define BSD4_2 /* for 4.2 BSD */
83#define WHO /* turn this on always */
e1e8d3ca 84#define RWHO /* 4.1a or greater, with rwho */
e1e8d3ca
KM
85#define NEW_BOOTTIME /* 4.1c or greater */
86
87#define NETPREFIX "ucb"
88#define DEFDELAY 60 /* update status once per minute */
e1e8d3ca
KM
89/*
90 * if MAXLOAD is defined, then if the load average exceeded MAXLOAD
91 * then the process table will not be scanned and the log in/out data
92 * will not be checked. The purpose of this is to reduced the load
93 * on the system when it is loaded.
94 */
95#define MAXLOAD 6.0
96
e1e8d3ca 97#include <sys/param.h>
e1e8d3ca
KM
98#include <sys/time.h>
99#include <sys/stat.h>
e1e8d3ca
KM
100#include <sys/vtimes.h>
101#include <sys/proc.h>
97c40fdf
KB
102#include <sys/ioctl.h>
103#include <signal.h>
104#include <fcntl.h>
105#include <utmp.h>
106#include <nlist.h>
e1e8d3ca
KM
107#include <curses.h>
108#undef nl
109#ifdef TERMINFO
110#include <term.h>
97c40fdf
KB
111#endif
112#include <stdarg.h>
113#include <unistd.h>
114#include <stdlib.h>
115#include <string.h>
116#include <stdio.h>
117#include <ctype.h>
e1e8d3ca
KM
118
119#ifdef RWHO
6efb822a
MK
120#include <protocols/rwhod.h>
121
e1e8d3ca 122#define DOWN_THRESHOLD (11 * 60)
e1e8d3ca 123
e1e8d3ca
KM
124struct remotehost {
125 char *rh_host;
126 int rh_file;
127} remotehost[10];
128int nremotes = 0;
129#endif RWHO
130
b528bd5e
KB
131#include "pathnames.h"
132
e1e8d3ca 133struct nlist nl[] = {
97c40fdf 134 { "_boottime" },
e1e8d3ca
KM
135#define NL_BOOT 0
136 { "_proc" },
137#define NL_PROC 1
e1e8d3ca 138 { "_nproc" },
bcc006b0 139#define NL_NPROC 2
e1e8d3ca
KM
140 0
141};
142
143 /* stuff for the kernel */
b528bd5e 144int kmem; /* file descriptor for _PATH_KMEM */
e1e8d3ca
KM
145struct proc *proc, *procNPROC;
146int nproc;
147int procadr;
148double avenrun[3]; /* used for storing load averages */
149
150/*
151 * In order to determine how many people are logged on and who has
152 * logged in or out, we read in the /etc/utmp file. We also keep track of
153 * the previous utmp file.
154 */
4874204a 155int ut = -1; /* the file descriptor */
e1e8d3ca
KM
156struct utmp *new, *old;
157char *status; /* per tty status bits, see below */
158int nentries; /* number of utmp entries */
159 /* string lengths for printing */
160#define LINESIZE (sizeof old->ut_line)
161#define NAMESIZE (sizeof old->ut_name)
162/*
163 * Status codes to say what has happened to a particular entry in utmp.
164 * NOCH means no change, ON means new person logged on,
165 * OFF means person logged off.
166 */
167#define NOCH 0
168#define ON 0x1
169#define OFF 0x2
170
171#ifdef WHO
172char whofilename[100];
173char whofilename2[100];
174#endif
175
6efb822a 176char hostname[MAXHOSTNAMELEN+1]; /* one more for null termination */
e1e8d3ca
KM
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
97c40fdf 242void sigwinch();
e1e8d3ca
KM
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
e1e8d3ca
KM
255char *strcpy1();
256char *sysrup();
e1e8d3ca
KM
257int outc();
258int erroutc();
259
97c40fdf
KB
260main(argc, argv)
261 int argc;
e1e8d3ca
KM
262 register char **argv;
263{
e1e8d3ca
KM
264 register char *cp;
265 char *home;
97c40fdf 266 void clearbotl();
e1e8d3ca 267
e1e8d3ca 268 gethostname(hostname, sizeof hostname - 1);
897f1e78
JB
269 if ((cp = index(hostname, '.')) != NULL)
270 *cp = '\0';
97c40fdf 271 (void)setvbuf(stdout, (char *)NULL, _IOFBF, 0);
e1e8d3ca
KM
272
273 for (argv++; *argv != 0; argv++)
274 switch (**argv) {
275 case '-':
276 for (cp = *argv + 1; *cp; cp++) {
277 switch(*cp) {
278 case 'r' : /* turn off reverse video */
279 reverse = 0;
280 break;
281 case 'c':
282 clr_bet_ref = 1;
283 break;
284 case 'h':
285 hostprint = 1;
286 break;
287 case 'D':
288 dateprint = 1;
289 break;
290#ifdef RWHO
291 case 'H':
292 if (argv[1] == 0)
293 break;
294 argv++;
295 if (strcmp(hostname, *argv) &&
296 strcmp(&hostname[sizeof NETPREFIX - 1], *argv))
297 remotehost[nremotes++].rh_host = *argv;
298 break;
299#endif RWHO
300 case 'm':
301 mailcheck = 0;
302 break;
303 case 'p':
304 proccheck = 0;
305 break;
306 case 'l':
307 logcheck = 0;
308 break;
309 case 'b':
310 beep = 1;
311 break;
312 case 'i':
313 printid = 1;
314 break;
315 case 'w':
316 window = 1;
317 break;
318 case 'e':
319 emacs = 1;
320 break;
321 case 'd':
322 dbug = 1;
323 break;
324 case 'q':
325 quiet = 1;
326 break;
327 case 's':
328 shortline = 1;
329 break;
330 case 'j':
331 leftline = 1;
332 break;
333 default:
334 fprintf(stderr,
335 "sysline: bad flag: %c\n", *cp);
336 }
337 }
338 break;
339 case '+':
340 delay = atoi(*argv + 1);
341 if (delay < 10)
342 delay = 10;
343 else if (delay > 500)
344 delay = 500;
345 synch = 0; /* no more sync */
346 break;
347 default:
348 fprintf(stderr, "sysline: illegal argument %s\n",
349 argv[0]);
350 }
351 if (emacs) {
352 reverse = 0;
353 columns = 79;
354 } else /* if not to emacs window, initialize terminal dependent info */
355 initterm();
356#ifdef SIGWINCH
357 /*
358 * When the window size changes and we are the foreground
359 * process (true if -w), we get this signal.
360 */
361 signal(SIGWINCH, sigwinch);
362#endif
363 getwinsize(); /* get window size from ioctl */
364
365 /* immediately fork and let the parent die if not emacs mode */
366 if (!emacs && !window && !dbug) {
367 if (fork())
368 exit(0);
369 /* pgrp should take care of things, but ignore them anyway */
370 signal(SIGINT, SIG_IGN);
371 signal(SIGQUIT, SIG_IGN);
e1e8d3ca 372 signal(SIGTTOU, SIG_IGN);
e1e8d3ca
KM
373 }
374 /*
375 * When we logoff, init will do a "vhangup()" on this
376 * tty which turns off I/O access and sends a SIGHUP
377 * signal. We catch this and thereby clear the status
378 * display. Note that a bug in 4.1bsd caused the SIGHUP
379 * signal to be sent to the wrong process, so you had to
380 * `kill -HUP' yourself in your .logout file.
381 * Do the same thing for SIGTERM, which is the default kill
382 * signal.
383 */
384 signal(SIGHUP, clearbotl);
385 signal(SIGTERM, clearbotl);
386 /*
387 * This is so kill -ALRM to force update won't screw us up..
388 */
389 signal(SIGALRM, SIG_IGN);
390
391 uid = getuid();
392 ourtty = ttyname(2); /* remember what tty we are on */
393 if (printid) {
394 printf("%d\n", getpid());
395 fflush(stdout);
396 }
397 dup2(2, 1);
398
399 if ((home = getenv("HOME")) == 0)
400 home = "";
401 strcpy1(strcpy1(whofilename, home), "/.who");
402 strcpy1(strcpy1(whofilename2, home), "/.sysline");
403 strcpy1(strcpy1(lockfilename, home), "/.syslinelock");
404
b528bd5e
KB
405 if ((kmem = open(_PATH_KMEM,0)) < 0) {
406 fprintf(stderr, "Can't open %s\n", _PATH_KMEM);
e1e8d3ca
KM
407 exit(1);
408 }
409 readnamelist();
410 if (proccheck)
411 initprocread();
e1e8d3ca
KM
412 if (mailcheck)
413 if ((username = getenv("USER")) == 0)
414 mailcheck = 0;
415 else {
e51aa00c 416 chdir(_PATH_MAILDIR);
e1e8d3ca
KM
417 if (stat(username, &mstbuf) >= 0)
418 mailsize = mstbuf.st_size;
419 else
420 mailsize = 0;
421 }
422
423 while (emacs || window || isloggedin())
424 if (access(lockfilename, 0) >= 0)
425 sleep(60);
426 else {
427 prtinfo();
428 sleep(delay);
429 if (clr_bet_ref) {
430 tputs(dis_status_line, 1, outc);
431 fflush(stdout);
432 sleep(5);
433 }
434 revtime = (1 + revtime) % REVOFF;
435 }
436 clearbotl();
437 /*NOTREACHED*/
438}
439
440isloggedin()
441{
442 /*
443 * you can tell if a person has logged out if the owner of
444 * the tty has changed
445 */
446 struct stat statbuf;
447
448 return fstat(2, &statbuf) == 0 && statbuf.st_uid == uid;
449}
450
451readnamelist()
452{
453 time_t bootime, clock, nintv, time();
454
b528bd5e 455 nlist(_PATH_UNIX, nl);
e1e8d3ca
KM
456 if (nl[0].n_value == 0) {
457 if (!quiet)
458 fprintf(stderr, "No namelist\n");
459 return;
460 }
461 lseek(kmem, (long)nl[NL_BOOT].n_value, 0);
462 read(kmem, &bootime, sizeof(bootime));
463 (void) time(&clock);
464 nintv = clock - bootime;
465 if (nintv <= 0L || nintv > 60L*60L*24L*365L) {
466 if (!quiet)
467 fprintf(stderr,
468 "Time makes no sense... namelist must be wrong\n");
bcc006b0 469 nl[NL_PROC].n_value = 0;
e1e8d3ca
KM
470 }
471}
472
4874204a
EW
473readutmp(nflag)
474 char nflag;
e1e8d3ca 475{
4874204a
EW
476 static time_t lastmod; /* initially zero */
477 static off_t utmpsize; /* ditto */
e1e8d3ca
KM
478 struct stat st;
479
b528bd5e
KB
480 if (ut < 0 && (ut = open(_PATH_UTMP, 0)) < 0) {
481 fprintf(stderr, "sysline: Can't open %s.\n", _PATH_UTMP);
e1e8d3ca
KM
482 exit(1);
483 }
4874204a 484 if (fstat(ut, &st) < 0 || st.st_mtime == lastmod)
e1e8d3ca
KM
485 return 0;
486 lastmod = st.st_mtime;
4874204a
EW
487 if (utmpsize != st.st_size) {
488 utmpsize = st.st_size;
489 nentries = utmpsize / sizeof (struct utmp);
490 if (old == 0) {
491 old = (struct utmp *)calloc(utmpsize, 1);
492 new = (struct utmp *)calloc(utmpsize, 1);
493 } else {
494 old = (struct utmp *)realloc((char *)old, utmpsize);
495 new = (struct utmp *)realloc((char *)new, utmpsize);
496 free(status);
497 }
498 status = malloc(nentries * sizeof *status);
499 if (old == 0 || new == 0 || status == 0) {
500 fprintf(stderr, "sysline: Out of memory.\n");
501 exit(1);
502 }
503 }
e1e8d3ca 504 lseek(ut, 0L, 0);
4874204a 505 (void) read(ut, (char *) (nflag ? new : old), utmpsize);
e1e8d3ca
KM
506 return 1;
507}
508
509/*
510 * read in the process table locations and sizes, and allocate space
511 * for storing the process table. This is done only once.
512 */
513initprocread()
514{
515
516 if (nl[NL_PROC].n_value == 0)
517 return;
e1e8d3ca
KM
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);
e1e8d3ca
KM
522 if ((proc = (struct proc *) calloc(nproc, sizeof (struct proc))) == 0) {
523 fprintf(stderr, "Out of memory.\n");
524 exit(1);
525 }
526 procNPROC = proc + nproc;
527}
528
529/*
530 * read in the process table. This assumes that initprocread has alread been
531 * called to set up storage.
532 */
533readproctab()
534{
535
536 if (nl[NL_PROC].n_value == 0)
537 return (0);
538 lseek(kmem, (long)procadr, 0);
539 read(kmem, (char *)proc, nproc * sizeof (struct proc));
540 return (1);
541}
542
543prtinfo()
544{
545 int on, off;
546 register i;
547 char fullprocess;
548
549 stringinit();
550#ifdef SIGWINCH
551 if (winchanged) {
552 winchanged = 0;
553 getwinsize();
554 mustclear = 1;
555 }
556#endif
557#ifdef WHO
558 /* check for file named .who in the home directory */
559 whocheck();
560#endif
561 timeprint();
562 /*
563 * if mail is seen, don't print rest of info, just the mail
564 * reverse new and old so that next time we run, we won't lose log
565 * in and out information
566 */
567 if (mailcheck && (sawmail = mailseen()))
568 goto bottom;
e1e8d3ca
KM
569#ifdef RWHO
570 for (i = 0; i < nremotes; i++) {
571 char *tmp;
572
573 stringspace();
574 tmp = sysrup(remotehost + i);
575 stringcat(tmp, strlen(tmp));
576 }
577#endif
578 /*
579 * print hostname info if requested
580 */
581 if (hostprint) {
582 stringspace();
583 stringcat(hostname, -1);
584 }
e1e8d3ca
KM
585 /*
586 * print load average and difference between current load average
587 * and the load average 5 minutes ago
588 */
bcc006b0 589 if (getloadavg(avenrun, 3) > 0) {
e1e8d3ca
KM
590 double diff;
591
592 stringspace();
bcc006b0 593
e1e8d3ca
KM
594 if ((diff = avenrun[0] - avenrun[1]) < 0.0)
595 stringprt("%.1f %.1f", avenrun[0], diff);
596 else
597 stringprt("%.1f +%.1f", avenrun[0], diff);
598 loadavg = avenrun[0]; /* remember load average */
599 }
600 /*
601 * print log on and off information
602 */
603 stringspace();
604 fullprocess = 1;
605#ifdef MAXLOAD
606 if (loadavg > MAXLOAD)
607 fullprocess = 0; /* too loaded to run */
608#endif
609 /*
610 * Read utmp file (logged in data) only if we are doing a full
611 * process, or if this is the first time and we are calculating
612 * the number of users.
613 */
614 on = off = 0;
615 if (users == 0) { /* first time */
4874204a 616 if (readutmp(0))
e1e8d3ca
KM
617 for (i = 0; i < nentries; i++)
618 if (old[i].ut_name[0])
619 users++;
4874204a 620 } else if (fullprocess && readutmp(1)) {
e1e8d3ca
KM
621 struct utmp *tmp;
622
623 users = 0;
624 for (i = 0; i < nentries; i++) {
625 if (strncmp(old[i].ut_name,
626 new[i].ut_name, NAMESIZE) == 0)
627 status[i] = NOCH;
628 else if (old[i].ut_name[0] == '\0') {
629 status[i] = ON;
630 on++;
631 } else if (new[i].ut_name[0] == '\0') {
632 status[i] = OFF;
633 off++;
634 } else {
635 status[i] = ON | OFF;
636 on++;
637 off++;
638 }
639 if (new[i].ut_name[0])
640 users++;
641 }
642 tmp = new;
643 new = old;
644 old = tmp;
645 }
646 /*
647 * Print:
648 * 1. number of users
649 * 2. a * for unread mail
650 * 3. a - if load is too high
651 * 4. number of processes running and stopped
652 */
653 stringprt("%du", users);
654 if (mailsize > 0 && mstbuf.st_mtime >= mstbuf.st_atime)
655 stringcat("*", -1);
656 if (!fullprocess && (proccheck || logcheck))
657 stringcat("-", -1);
658 if (fullprocess && proccheck && readproctab()) {
659 register struct proc *p;
660 int procrun, procstop;
661
662 /*
663 * We are only interested in processes which have the same
664 * uid as us, and whose parent process id is not 1.
665 */
666 procrun = procstop = 0;
667 for (p = proc; p < procNPROC; p++) {
668 if (p->p_stat == 0 || p->p_pgrp == 0 ||
669 p->p_uid != uid || p->p_ppid == 1)
670 continue;
671 switch (p->p_stat) {
672 case SSTOP:
673 procstop++;
674 break;
675 case SSLEEP:
676 /*
677 * Sleep can mean waiting for a signal or just
678 * in a disk or page wait queue ready to run.
679 * We can tell if it is the later by the pri
680 * being negative.
681 */
682 if (p->p_pri < PZERO)
683 procrun++;
684 break;
685 case SWAIT:
686 case SRUN:
687 case SIDL:
688 procrun++;
689 }
690 }
691 if (procrun > 0 || procstop > 0) {
692 stringspace();
693 if (procrun > 0 && procstop > 0)
694 stringprt("%dr %ds", procrun, procstop);
695 else if (procrun > 0)
696 stringprt("%dr", procrun);
697 else
698 stringprt("%ds", procstop);
699 }
700 }
701 /*
702 * If anyone has logged on or off, and we are interested in it,
703 * print it out.
704 */
705 if (logcheck) {
706 /* old and new have already been swapped */
707 if (on) {
708 stringspace();
709 stringcat("on:", -1);
710 for (i = 0; i < nentries; i++)
711 if (status[i] & ON) {
712 stringprt(" %.8s", old[i].ut_name);
713 ttyprint(old[i].ut_line);
714 }
715 }
716 if (off) {
717 stringspace();
718 stringcat("off:", -1);
719 for (i = 0; i < nentries; i++)
720 if (status[i] & OFF) {
721 stringprt(" %.8s", new[i].ut_name);
722 ttyprint(new[i].ut_line);
723 }
724 }
725 }
726bottom:
727 /* dump out what we know */
728 stringdump();
729}
730
731timeprint()
732{
733 long curtime;
734 struct tm *tp, *localtime();
735 static int beepable = 1;
736
737 /* always print time */
738 time(&curtime);
739 tp = localtime(&curtime);
740 if (dateprint)
741 stringprt("%.11s", ctime(&curtime));
742 stringprt("%d:%02d", tp->tm_hour > 12 ? tp->tm_hour - 12 :
743 (tp->tm_hour == 0 ? 12 : tp->tm_hour), tp->tm_min);
744 if (synch) /* sync with clock */
745 delay = 60 - tp->tm_sec;
746 /*
747 * Beepable is used to insure that we get at most one set of beeps
748 * every half hour.
749 */
750 if (beep)
751 if (beepable) {
752 if (tp->tm_min == 30) {
753 tputs(bell, 1, outc);
754 fflush(stdout);
755 beepable = 0;
756 } else if (tp->tm_min == 0) {
757 tputs(bell, 1, outc);
758 fflush(stdout);
759 sleep(2);
760 tputs(bell, 1, outc);
761 fflush(stdout);
762 beepable = 0;
763 }
764 } else
765 if (tp->tm_min != 0 && tp->tm_min != 30)
766 beepable = 1;
767}
768
769/*
770 * whocheck -- check for file named .who and print it on the who line first
771 */
772whocheck()
773{
774 int chss;
775 register char *p;
776 char buff[81];
777 int whofile;
778
779 if ((whofile = open(whofilename, 0)) < 0 &&
780 (whofile = open(whofilename2, 0)) < 0)
781 return;
782 chss = read(whofile, buff, sizeof buff - 1);
783 close(whofile);
784 if (chss <= 0)
785 return;
786 buff[chss] = '\0';
787 /*
788 * Remove all line feeds, and replace by spaces if they are within
789 * the message, else replace them by nulls.
790 */
791 for (p = buff; *p;)
792 if (*p == '\n')
793 if (p[1])
794 *p++ = ' ';
795 else
796 *p = '\0';
797 else
798 p++;
799 stringcat(buff, p - buff);
800 stringspace();
801}
802
803/*
804 * ttyprint -- given the name of a tty, print in the string buffer its
805 * short name surrounded by parenthesis.
806 * ttyxx is printed as (xx)
807 * console is printed as (cty)
808 */
809ttyprint(name)
810 char *name;
811{
812 char buff[11];
813
814 if (strncmp(name, "tty", 3) == 0)
815 stringprt("(%.*s)", LINESIZE - 3, name + 3);
816 else if (strcmp(name, "console") == 0)
817 stringcat("(cty)", -1);
818 else
819 stringprt("(%.*s)", LINESIZE, name);
820}
821
822/*
823 * mail checking function
824 * returns 0 if no mail seen
825 */
826mailseen()
827{
828 FILE *mfd;
829 register n;
830 register char *cp;
831 char lbuf[100], sendbuf[100], *bufend;
832 char seenspace;
833 int retval = 0;
834
835 if (stat(username, &mstbuf) < 0) {
836 mailsize = 0;
837 return 0;
838 }
839 if (mstbuf.st_size <= mailsize || (mfd = fopen(username,"r")) == NULL) {
840 mailsize = mstbuf.st_size;
841 return 0;
842 }
843 fseek(mfd, mailsize, 0);
844 while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0 &&
845 strncmp(lbuf, "From ", 5) != 0)
846 ;
847 if (n < 0) {
f9e4427b 848 stringcat("Mail has just arrived", -1);
e1e8d3ca
KM
849 goto out;
850 }
851 retval = 1;
852 /*
853 * Found a From line, get second word, which is the sender,
854 * and print it.
855 */
856 for (cp = lbuf + 5; *cp && *cp != ' '; cp++) /* skip to blank */
857 ;
858 *cp = '\0'; /* terminate name */
859 stringspace();
860 stringprt("Mail from %s ", lbuf + 5);
861 /*
862 * Print subject, and skip over header.
863 */
864 while ((n = readline(mfd, lbuf, sizeof lbuf)) > 0)
865 if (strncmp(lbuf, "Subject:", 8) == 0)
866 stringprt("on %s ", lbuf + 9);
867 if (!emacs)
868 stringcat(arrows, 2);
869 else
870 stringcat(": ", 2);
871 if (n < 0) /* already at eof */
872 goto out;
873 /*
874 * Print as much of the letter as we can.
875 */
876 cp = sendbuf;
877 if ((n = columns - chars) > sizeof sendbuf - 1)
878 n = sizeof sendbuf - 1;
879 bufend = cp + n;
880 seenspace = 0;
881 while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0) {
882 register char *rp;
883
884 if (strncmp(lbuf, "From ", 5) == 0)
885 break;
886 if (cp >= bufend)
887 continue;
888 if (!seenspace) {
889 *cp++ = ' '; /* space before lines */
890 seenspace = 1;
891 }
892 rp = lbuf;
893 while (*rp && cp < bufend)
894 if (isspace(*rp)) {
895 if (!seenspace) {
896 *cp++ = ' ';
897 seenspace = 1;
898 }
899 rp++;
900 } else {
901 *cp++ = *rp++;
902 seenspace = 0;
903 }
904 }
905 *cp = 0;
906 stringcat(sendbuf, -1);
907 /*
908 * Want to update write time so a star will
909 * appear after the number of users until the
910 * user reads his mail.
911 */
912out:
913 mailsize = linebeg;
914 fclose(mfd);
915 touch(username);
916 return retval;
917}
918
919/*
920 * readline -- read a line from fp and store it in buf.
921 * return the number of characters read.
922 */
923readline(fp, buf, n)
924 register FILE *fp;
925 char *buf;
926 register n;
927{
928 register c;
929 register char *cp = buf;
930
931 linebeg = ftell(fp); /* remember loc where line begins */
932 cp = buf;
933 while (--n > 0 && (c = getc(fp)) != EOF && c != '\n')
934 *cp++ = c;
935 *cp = 0;
936 if (c == EOF && cp - buf == 0)
937 return -1;
938 return cp - buf;
939}
940
941
942/*
943 * string hacking functions
944 */
945
946stringinit()
947{
948 sp = strarr;
949 chars = 0;
950}
951
952/*VARARGS1*/
97c40fdf
KB
953stringprt(fmt)
954 char *fmt;
e1e8d3ca 955{
97c40fdf 956 va_list ap;
e1e8d3ca
KM
957 char tempbuf[150];
958
97c40fdf
KB
959 va_start(ap, fmt);
960 (void)vsnprintf(tempbuf, sizeof(tempbuf), fmt, ap);
961 va_end(ap);
06eef7c4 962 stringcat(tempbuf, -1);
e1e8d3ca
KM
963}
964
965stringdump()
966{
967 char bigbuf[sizeof strarr + 200];
968 register char *bp = bigbuf;
969 register int i;
970
971 if (!emacs) {
972 if (sawmail)
973 bp = strcpy1(bp, bell);
974 if (eslok)
975 bp = strcpy1(bp, tparm(to_status_line,
976 leftline ? 0 : columns - chars));
977 else {
978 bp = strcpy1(bp, to_status_line);
979 if (!shortline && !leftline)
980 for (i = columns - chars; --i >= 0;)
981 *bp++ = ' ';
982 }
983 if (reverse && revtime != 0)
984 bp = strcpy1(bp, rev_out);
985 }
986 *sp = 0;
987 bp = strcpy1(bp, strarr);
988 if (!emacs) {
989 if (reverse)
990 bp = strcpy1(bp, rev_end);
991 bp = strcpy1(bp, from_status_line);
992 if (sawmail)
993 bp = strcpy1(strcpy1(bp, bell), bell);
994 *bp = 0;
995 tputs(bigbuf, 1, outc);
996 if (mustclear) {
997 mustclear = 0;
998 tputs(clr_eol, 1, outc);
999 }
1000 if (dbug)
1001 putchar('\n');
1002 fflush(stdout);
1003 } else
1004 write(2, bigbuf, bp - bigbuf);
1005}
1006
1007stringspace()
1008{
1009 if (reverse && revtime != 0) {
1010#ifdef TERMINFO
1011 stringcat(rev_end,
1012 magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch);
1013 stringcat(" ", 1);
1014 stringcat(rev_out,
1015 magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch);
1016#else
1017 stringcat(rev_end, 0);
1018 stringcat(" ", 1);
1019 stringcat(rev_out, 0);
1020#endif TERMINFO
1021 } else
1022 stringcat(" ", 1);
1023}
1024
1025/*
1026 * stringcat :: concatenate the characters in string str to the list we are
1027 * building to send out.
1028 * str - the string to print. may contain funny (terminal control) chars.
1029 * n - the number of printable characters in the string
1030 * or if -1 then str is all printable so we can truncate it,
1031 * otherwise don't print only half a string.
1032 */
1033stringcat(str, n)
1034 register char *str;
1035 register n;
1036{
1037 register char *p = sp;
1038
1039 if (n < 0) { /* truncate */
1040 n = columns - chars;
1041 while ((*p++ = *str++) && --n >= 0)
1042 ;
1043 p--;
1044 chars += p - sp;
1045 sp = p;
1046 } else if (chars + n <= columns) { /* don't truncate */
1047 while (*p++ = *str++)
1048 ;
1049 chars += n;
1050 sp = p - 1;
1051 }
1052}
1053
1054/*
1055 * touch :: update the modify time of a file.
1056 */
1057touch(name)
1058 char *name; /* name of file */
1059{
1060 register fd;
1061 char buf;
1062
1063 if ((fd = open(name, 2)) >= 0) {
1064 read(fd, &buf, 1); /* get first byte */
1065 lseek(fd, 0L, 0); /* go to beginning */
1066 write(fd, &buf, 1); /* and rewrite first byte */
1067 close(fd);
1068 }
1069}
1070
1071
1072/*
1073 * clearbotl :: clear bottom line.
1074 * called when process quits or is killed.
1075 * it clears the bottom line of the terminal.
1076 */
1fb3a5b8 1077void
e1e8d3ca
KM
1078clearbotl()
1079{
1080 register int fd;
97c40fdf 1081 void sigexit();
e1e8d3ca 1082
97c40fdf 1083 signal(SIGALRM, sigexit);
e1e8d3ca
KM
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
97c40fdf 1288void
e1e8d3ca
KM
1289sigwinch()
1290{
1291 winchanged++;
1292}
1293#endif
1294
97c40fdf
KB
1295void
1296sigexit()
1297{
1298 exit(1);
1299}
1300
e1e8d3ca
KM
1301char *
1302strcpy1(p, q)
1303 register char *p, *q;
1304{
1305
1306 while (*p++ = *q++)
1307 ;
1308 return p - 1;
1309}
1310
1311outc(c)
1312 char c;
1313{
1314 if (dbug)
1315 printf("%s", unctrl(c));
1316 else
1317 putchar(c);
1318}
1319
1320erroutc(c)
1321 char c;
1322{
1323 if (dbug)
1324 fprintf(stderr, "%s", unctrl(c));
1325 else
1326 putc(c, stderr);
1327}