main.c revision 1.35
1/*	$OpenBSD: main.c,v 1.35 2007/02/25 18:21:24 deraadt Exp $	*/
2/*	$NetBSD: main.c,v 1.8 1996/05/10 23:16:36 thorpej Exp $	*/
3
4/*-
5 * Copyright (c) 1980, 1992, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34static char copyright[] =
35"@(#) Copyright (c) 1980, 1992, 1993\n\
36	The Regents of the University of California.  All rights reserved.\n";
37#endif /* not lint */
38
39#ifndef lint
40#if 0
41static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
42#endif
43static char rcsid[] = "$OpenBSD: main.c,v 1.35 2007/02/25 18:21:24 deraadt Exp $";
44#endif /* not lint */
45
46#include <sys/param.h>
47#include <sys/sysctl.h>
48
49#include <err.h>
50#include <nlist.h>
51#include <signal.h>
52#include <ctype.h>
53#include <stdio.h>
54#include <string.h>
55#include <unistd.h>
56#include <utmp.h>
57#include <stdlib.h>
58#include <limits.h>
59#include <stdarg.h>
60
61#include "systat.h"
62#include "extern.h"
63
64double	dellave;
65
66kvm_t	*kd;
67char	*nlistf = NULL;
68char	*memf = NULL;
69double	avenrun[3];
70u_int	naptime = 5;
71int	verbose = 1;		/* to report kvm read errs */
72int	nflag = 0;
73int	ut, hz, stathz;
74char    hostname[MAXHOSTNAMELEN];
75WINDOW  *wnd;
76int	CMDLINE;
77
78WINDOW *wload;			/* one line window for load average */
79
80static void usage(void);
81
82int
83main(int argc, char *argv[])
84{
85	char errbuf[_POSIX2_LINE_MAX];
86	const char *errstr;
87	gid_t gid;
88	int ch;
89
90	ut = open(_PATH_UTMP, O_RDONLY);
91	if (ut < 0) {
92		error("No utmp");
93		exit(1);
94	}
95
96	kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
97	if (kd == NULL) {
98		error("%s", errbuf);
99		exit(1);
100	}
101
102	gid = getgid();
103	if (setresgid(gid, gid, gid) == -1)
104		err(1, "setresgid");
105
106	while ((ch = getopt(argc, argv, "nw:")) != -1)
107		switch (ch) {
108		case 'n':
109			nflag = 1;
110			break;
111		case 'w':
112			naptime = (u_int)strtonum(optarg, 1, 1000, &errstr);
113			if (errstr)
114				errx(1, "interval %s: %s", errstr, optarg);
115			break;
116		default:
117			usage();
118		}
119	argc -= optind;
120	argv += optind;
121
122	while (argc > 0) {
123		if (isdigit(argv[0][0])) {
124			naptime = (u_int)strtonum(argv[0], 1, 1000, &errstr);
125			if (errstr)
126				naptime = 5;
127		} else {
128			struct cmdtab *p;
129
130			p = lookup(&argv[0][0]);
131			if (p == (struct cmdtab *)-1)
132				errx(1, "ambiguous request: %s", &argv[0][0]);
133			if (p == 0)
134				errx(1, "unknown request: %s", &argv[0][0]);
135			curcmd = p;
136		}
137		argc--;
138		argv++;
139	}
140
141	signal(SIGINT, sigdie);
142	siginterrupt(SIGINT, 1);
143	signal(SIGQUIT, sigdie);
144	siginterrupt(SIGQUIT, 1);
145	signal(SIGTERM, sigdie);
146	siginterrupt(SIGTERM, 1);
147	signal(SIGTSTP, sigtstp);
148	siginterrupt(SIGTSTP, 1);
149
150	/*
151	 * Initialize display.  Load average appears in a one line
152	 * window of its own.  Current command's display appears in
153	 * an overlapping sub-window of stdscr configured by the display
154	 * routines to minimize update work by curses.
155	 */
156	if (initscr() == NULL) {
157		warnx("couldn't initialize screen");
158		exit(0);
159	}
160
161	CMDLINE = LINES - 1;
162	wnd = (*curcmd->c_open)();
163	if (wnd == NULL) {
164		warnx("couldn't initialize display");
165		die();
166	}
167	wload = newwin(1, 0, 1, 20);
168	if (wload == NULL) {
169		warnx("couldn't set up load average window");
170		die();
171	}
172	gethostname(hostname, sizeof (hostname));
173	gethz();
174	(*curcmd->c_init)();
175	curcmd->c_flags |= CF_INIT;
176	labels();
177
178	dellave = 0.0;
179
180	signal(SIGALRM, sigdisplay);
181	siginterrupt(SIGALRM, 1);
182	signal(SIGWINCH, sigwinch);
183	siginterrupt(SIGWINCH, 1);
184	gotdisplay = 1;
185	noecho();
186	crmode();
187	keyboard();
188	/*NOTREACHED*/
189}
190
191void
192gethz(void)
193{
194	struct clockinfo cinf;
195	size_t  size = sizeof(cinf);
196	int	mib[2];
197
198	mib[0] = CTL_KERN;
199	mib[1] = KERN_CLOCKRATE;
200	if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1)
201		return;
202	stathz = cinf.stathz;
203	hz = cinf.hz;
204}
205
206static void
207usage(void)
208{
209	fprintf(stderr, "usage: systat [-n] [-w wait] [display] [refresh-interval]\n");
210	exit(1);
211}
212
213
214void
215labels(void)
216{
217	if (curcmd->c_flags & CF_LOADAV)
218		mvprintw(0, 2 + 4, "users    Load");
219	(*curcmd->c_label)();
220#ifdef notdef
221	mvprintw(21, 25, "CPU usage on %s", hostname);
222#endif
223	refresh();
224}
225
226/*ARGSUSED*/
227void
228sigdisplay(int signo)
229{
230	gotdisplay = 1;
231}
232
233void
234display(void)
235{
236	/* Get the load average over the last minute. */
237	(void) getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0]));
238	(*curcmd->c_fetch)();
239	if (curcmd->c_flags & CF_LOADAV) {
240		extern int ucount();
241		char tbuf[26];
242		time_t now;
243
244		time(&now);
245		strlcpy(tbuf, ctime(&now), sizeof tbuf);
246
247		putint(ucount(), 0, 2, 3);
248		putfloat(avenrun[0], 0, 2 + 17, 6, 2, 0);
249		putfloat(avenrun[1], 0, 2 + 23, 6, 2, 0);
250		putfloat(avenrun[2], 0, 2 + 29, 6, 2, 0);
251		mvaddstr(0, 2 + 53, tbuf);
252	}
253	(*curcmd->c_refresh)();
254	if (curcmd->c_flags & CF_LOADAV)
255		wrefresh(wload);
256	wrefresh(wnd);
257	move(CMDLINE, 0);
258	refresh();
259	alarm(naptime);
260}
261
262void
263load(void)
264{
265
266	(void) getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0]));
267	mvprintw(CMDLINE, 0, "%4.1f %4.1f %4.1f",
268	    avenrun[0], avenrun[1], avenrun[2]);
269	clrtoeol();
270}
271
272volatile sig_atomic_t gotdie;
273volatile sig_atomic_t gotdisplay;
274volatile sig_atomic_t gotwinch;
275volatile sig_atomic_t gottstp;
276
277/*ARGSUSED*/
278void
279sigdie(int signo)
280{
281	gotdie = 1;
282}
283
284/*ARGSUSED*/
285void
286sigtstp(int signo)
287{
288	gottstp = 1;
289}
290
291void
292die(void)
293{
294	if (wnd) {
295		move(CMDLINE, 0);
296		clrtoeol();
297		refresh();
298		endwin();
299	}
300	exit(0);
301}
302
303/*ARGSUSED*/
304void
305sigwinch(int signo)
306{
307	gotwinch = 1;
308}
309
310void
311error(const char *fmt, ...)
312{
313	va_list ap;
314	char buf[255];
315	int oy, ox;
316
317	va_start(ap, fmt);
318	if (wnd) {
319		getyx(stdscr, oy, ox);
320		(void) vsnprintf(buf, sizeof buf, fmt, ap);
321		clrtoeol();
322		standout();
323		mvaddstr(CMDLINE, 0, buf);
324		standend();
325		move(oy, ox);
326		refresh();
327	} else {
328		(void) vfprintf(stderr, fmt, ap);
329		fprintf(stderr, "\n");
330	}
331	va_end(ap);
332}
333
334void
335nlisterr(struct nlist namelist[])
336{
337	int i, n;
338
339	n = 0;
340	clear();
341	mvprintw(2, 10, "systat: nlist: can't find following symbols:");
342	for (i = 0;
343	    namelist[i].n_name != NULL && *namelist[i].n_name != '\0'; i++)
344		if (namelist[i].n_value == 0)
345			mvprintw(2 + ++n, 10, "%s", namelist[i].n_name);
346	move(CMDLINE, 0);
347	clrtoeol();
348	refresh();
349	endwin();
350	exit(1);
351}
352
353/* calculate number of users on the system */
354int
355ucount(void)
356{
357	int nusers = 0;
358	struct	utmp utmp;
359
360	if (ut < 0)
361		return (0);
362	lseek(ut, (off_t)0, SEEK_SET);
363	while (read(ut, &utmp, sizeof(utmp)))
364		if (utmp.ut_name[0] != '\0')
365			nusers++;
366
367	return (nusers);
368}
369