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