xref: /openbsd-src/usr.bin/systat/main.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $Id: main.c,v 1.64 2016/01/02 15:02:05 benno Exp $	 */
2 /*
3  * Copyright (c) 2001, 2007 Can Erkin Acar
4  * Copyright (c) 2001 Daniel Hartmeier
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  *    - Redistributions of source code must retain the above copyright
12  *      notice, this list of conditions and the following disclaimer.
13  *    - Redistributions in binary form must reproduce the above
14  *      copyright notice, this list of conditions and the following
15  *      disclaimer in the documentation and/or other materials provided
16  *      with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 
33 #include <sys/types.h>
34 #include <sys/sysctl.h>
35 
36 
37 #include <ctype.h>
38 #include <curses.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <limits.h>
43 #include <netdb.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <stdarg.h>
49 #include <unistd.h>
50 #include <utmp.h>
51 
52 #include "engine.h"
53 #include "systat.h"
54 
55 #define TIMEPOS 55
56 
57 double	dellave;
58 
59 kvm_t	*kd;
60 char	*nlistf = NULL;
61 char	*memf = NULL;
62 double	avenrun[3];
63 double	naptime = 5.0;
64 int	verbose = 1;		/* to report kvm read errs */
65 int	nflag = 1;
66 int	ut, hz, stathz;
67 char    hostname[HOST_NAME_MAX+1];
68 WINDOW  *wnd;
69 int	CMDLINE;
70 char	timebuf[26];
71 char	uloadbuf[TIMEPOS];
72 
73 
74 int  ucount(void);
75 void usage(void);
76 
77 /* command prompt */
78 
79 void cmd_delay(const char *);
80 void cmd_count(const char *);
81 void cmd_compat(const char *);
82 
83 struct command cm_compat = {"Command", cmd_compat};
84 struct command cm_delay = {"Seconds to delay", cmd_delay};
85 struct command cm_count = {"Number of lines to display", cmd_count};
86 
87 
88 /* display functions */
89 
90 int
91 print_header(void)
92 {
93 	time_t now;
94 	int start = dispstart + 1, end = dispstart + maxprint;
95 	char tmpbuf[TIMEPOS];
96 	char header[MAX_LINE_BUF];
97 
98 	if (end > num_disp)
99 		end = num_disp;
100 
101 	tb_start();
102 
103 	if (!paused) {
104 		getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0]));
105 
106 		snprintf(uloadbuf, sizeof(uloadbuf),
107 		    "%5d users    Load %.2f %.2f %.2f",
108 		    ucount(), avenrun[0], avenrun[1], avenrun[2]);
109 
110 		time(&now);
111 		strlcpy(timebuf, ctime(&now), sizeof(timebuf));
112 	}
113 
114 	if (num_disp && (start > 1 || end != num_disp))
115 		snprintf(tmpbuf, sizeof(tmpbuf),
116 		    "%s (%u-%u of %u) %s", uloadbuf, start, end, num_disp,
117 		    paused ? "PAUSED" : "");
118 	else
119 		snprintf(tmpbuf, sizeof(tmpbuf),
120 		    "%s %s", uloadbuf,
121 		    paused ? "PAUSED" : "");
122 
123 	snprintf(header, sizeof(header), "%-55s%s", tmpbuf, timebuf);
124 
125 	if (rawmode)
126 		printf("\n\n%s\n", header);
127 	else
128 		mvprintw(0, 0, "%s", header);
129 
130 	return (1);
131 }
132 
133 /* compatibility functions, rearrange later */
134 void
135 error(const char *fmt, ...)
136 {
137 	va_list ap;
138 	char buf[MAX_LINE_BUF];
139 
140 	va_start(ap, fmt);
141 	vsnprintf(buf, sizeof buf, fmt, ap);
142 	va_end(ap);
143 
144 	message_set(buf);
145 }
146 
147 void
148 nlisterr(struct nlist namelist[])
149 {
150 	int i, n;
151 
152 	n = 0;
153 	clear();
154 	mvprintw(2, 10, "systat: nlist: can't find following symbols:");
155 	for (i = 0;
156 	    namelist[i].n_name != NULL && *namelist[i].n_name != '\0'; i++)
157 		if (namelist[i].n_value == 0)
158 			mvprintw(2 + ++n, 10, "%s", namelist[i].n_name);
159 	move(CMDLINE, 0);
160 	clrtoeol();
161 	refresh();
162 	endwin();
163 	exit(1);
164 }
165 
166 void
167 die(void)
168 {
169 	if (!rawmode)
170 		endwin();
171 	exit(0);
172 }
173 
174 
175 int
176 prefix(char *s1, char *s2)
177 {
178 
179 	while (*s1 == *s2) {
180 		if (*s1 == '\0')
181 			return (1);
182 		s1++, s2++;
183 	}
184 	return (*s1 == '\0');
185 }
186 
187 /* calculate number of users on the system */
188 int
189 ucount(void)
190 {
191 	int nusers = 0;
192 	struct	utmp utmp;
193 
194 	if (ut < 0)
195 		return (0);
196 	lseek(ut, (off_t)0, SEEK_SET);
197 	while (read(ut, &utmp, sizeof(utmp)))
198 		if (utmp.ut_name[0] != '\0')
199 			nusers++;
200 
201 	return (nusers);
202 }
203 
204 /* main program functions */
205 
206 void
207 usage(void)
208 {
209 	extern char *__progname;
210 	fprintf(stderr, "usage: %s [-aBbiNn] [-d count] "
211 	    "[-s delay] [-w width] [view] [delay]\n", __progname);
212 	exit(1);
213 }
214 
215 void
216 show_view(void)
217 {
218 	if (rawmode)
219 		return;
220 
221 	tb_start();
222 	tbprintf("%s %g", curr_view->name, naptime);
223 	tb_end();
224 	message_set(tmp_buf);
225 }
226 
227 void
228 add_view_tb(field_view *v)
229 {
230 	if (curr_view == v)
231 		tbprintf("[%s] ", v->name);
232 	else
233 		tbprintf("%s ", v->name);
234 }
235 
236 void
237 show_help(void)
238 {
239 	if (rawmode)
240 		return;
241 
242 	tb_start();
243 	foreach_view(add_view_tb);
244 	tb_end();
245 	message_set(tmp_buf);
246 }
247 
248 void
249 cmd_compat(const char *buf)
250 {
251 	const char *s;
252 
253 	if (strcasecmp(buf, "help") == 0) {
254 		show_help();
255 		need_update = 1;
256 		return;
257 	}
258 	if (strcasecmp(buf, "quit") == 0 || strcasecmp(buf, "q") == 0) {
259 		gotsig_close = 1;
260 		return;
261 	}
262 	if (strcasecmp(buf, "stop") == 0) {
263 		paused = 1;
264 		gotsig_alarm = 1;
265 		return;
266 	}
267 	if (strncasecmp(buf, "start", 5) == 0) {
268 		paused = 0;
269 		gotsig_alarm = 1;
270 		cmd_delay(buf + 5);
271 		return;
272 	}
273 
274 	for (s = buf; *s && strchr("0123456789+-.eE", *s) != NULL; s++)
275 		;
276 	if (*s) {
277 		if (set_view(buf))
278 			error("Invalid/ambiguous view: %s", buf);
279 	} else
280 		cmd_delay(buf);
281 }
282 
283 void
284 cmd_delay(const char *buf)
285 {
286 	double del;
287 	del = atof(buf);
288 
289 	if (del > 0) {
290 		udelay = (useconds_t)(del * 1000000);
291 		gotsig_alarm = 1;
292 		naptime = del;
293 	}
294 }
295 
296 void
297 cmd_count(const char *buf)
298 {
299 	const char *errstr;
300 
301 	maxprint = strtonum(buf, 1, lines - HEADER_LINES, &errstr);
302 	if (errstr)
303 		maxprint = lines - HEADER_LINES;
304 }
305 
306 
307 int
308 keyboard_callback(int ch)
309 {
310 	switch (ch) {
311 	case '?':
312 		/* FALLTHROUGH */
313 	case 'h':
314 		show_help();
315 		need_update = 1;
316 		break;
317 	case CTRL_G:
318 		show_view();
319 		need_update = 1;
320 		break;
321 	case 'l':
322 		command_set(&cm_count, NULL);
323 		break;
324 	case 's':
325 		command_set(&cm_delay, NULL);
326 		break;
327 	case ',':
328 		separate_thousands = !separate_thousands;
329 		gotsig_alarm = 1;
330 		break;
331 	case ':':
332 		command_set(&cm_compat, NULL);
333 		break;
334 	default:
335 		return 0;
336 	};
337 
338 	return 1;
339 }
340 
341 void
342 initialize(void)
343 {
344 	engine_initialize();
345 
346 	initvmstat();
347 	initpigs();
348 	initifstat();
349 	initiostat();
350 	initsensors();
351 	initmembufs();
352 	initnetstat();
353 	initswap();
354 	initpftop();
355 	initpf();
356 	initpool();
357 	initmalloc();
358 	initnfs();
359 	initcpu();
360 }
361 
362 void
363 gethz(void)
364 {
365 	struct clockinfo cinf;
366 	size_t  size = sizeof(cinf);
367 	int	mib[2];
368 
369 	mib[0] = CTL_KERN;
370 	mib[1] = KERN_CLOCKRATE;
371 	if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1)
372 		return;
373 	stathz = cinf.stathz;
374 	hz = cinf.hz;
375 }
376 
377 int
378 main(int argc, char *argv[])
379 {
380 	char errbuf[_POSIX2_LINE_MAX];
381 	const char *errstr;
382 	extern char *optarg;
383 	extern int optind;
384 	double delay = 5;
385 
386 	char *viewstr = NULL;
387 
388 	gid_t gid;
389 	int countmax = 0;
390 	int maxlines = 0;
391 
392 	int ch;
393 
394 	ut = open(_PATH_UTMP, O_RDONLY);
395 	if (ut < 0) {
396 		warn("No utmp");
397 	}
398 
399 	kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
400 
401 	gid = getgid();
402 	if (setresgid(gid, gid, gid) == -1)
403 		err(1, "setresgid");
404 
405 	while ((ch = getopt(argc, argv, "BNabd:ins:w:")) != -1) {
406 		switch (ch) {
407 		case 'a':
408 			maxlines = -1;
409 			break;
410 		case 'B':
411 			averageonly = 1;
412 			if (countmax < 2)
413 				countmax = 2;
414 			/* FALLTHROUGH */
415 		case 'b':
416 			rawmode = 1;
417 			interactive = 0;
418 			break;
419 		case 'd':
420 			countmax = strtonum(optarg, 1, INT_MAX, &errstr);
421 			if (errstr)
422 				errx(1, "-d %s: %s", optarg, errstr);
423 			break;
424 		case 'i':
425 			interactive = 1;
426 			break;
427 		case 'N':
428 			nflag = 0;
429 			break;
430 		case 'n':
431 			/* this is a noop, -n is the default */
432 			nflag = 1;
433 			break;
434 		case 's':
435 			delay = atof(optarg);
436 			if (delay <= 0)
437 				delay = 5;
438 			break;
439 		case 'w':
440 			rawwidth = strtonum(optarg, 1, MAX_LINE_BUF-1, &errstr);
441 			if (errstr)
442 				errx(1, "-w %s: %s", optarg, errstr);
443 			break;
444 		default:
445 			usage();
446 			/* NOTREACHED */
447 		}
448 	}
449 
450 	if (kd == NULL)
451 		warnx("kvm_openfiles: %s", errbuf);
452 
453 	argc -= optind;
454 	argv += optind;
455 
456 	if (argc == 1) {
457 		double del = atof(argv[0]);
458 		if (del == 0)
459 			viewstr = argv[0];
460 		else
461 			delay = del;
462 	} else if (argc == 2) {
463 		viewstr = argv[0];
464 		delay = atof(argv[1]);
465 		if (delay <= 0)
466 			delay = 5;
467 	}
468 
469 	udelay = (useconds_t)(delay * 1000000.0);
470 	if (udelay < 1)
471 		udelay = 1;
472 
473 	naptime = (double)udelay / 1000000.0;
474 
475 	gethostname(hostname, sizeof (hostname));
476 	gethz();
477 
478 	initialize();
479 
480 	set_order(NULL);
481 	if (viewstr && set_view(viewstr)) {
482 		fprintf(stderr, "Unknown/ambiguous view name: %s\n", viewstr);
483 		return 1;
484 	}
485 
486 	if (check_termcap()) {
487 		rawmode = 1;
488 		interactive = 0;
489 	}
490 
491 	setup_term(maxlines);
492 
493 	if (rawmode && countmax == 0)
494 		countmax = 1;
495 
496 	gotsig_alarm = 1;
497 
498 	engine_loop(countmax);
499 
500 	return 0;
501 }
502