xref: /openbsd/usr.bin/systat/main.c (revision 4cfece93)
1 /* $OpenBSD: main.c,v 1.72 2020/01/12 20:51:08 martijn 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 (80 - 8 - 20 - 1)
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 		char *ctim;
105 
106 		getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0]));
107 
108 		snprintf(uloadbuf, sizeof(uloadbuf),
109 		    "%4d users Load %.2f %.2f %.2f",
110 		    ucount(), avenrun[0], avenrun[1], avenrun[2]);
111 
112 		time(&now);
113 		ctim = ctime(&now);
114 		ctim[11+8] = '\0';
115 		strlcpy(timebuf, ctim + 11, sizeof(timebuf));
116 	}
117 
118 	if (num_disp && (start > 1 || end != num_disp))
119 		snprintf(tmpbuf, sizeof(tmpbuf),
120 		    "%s (%u-%u of %u) %s", uloadbuf, start, end, num_disp,
121 		    paused ? "PAUSED" : "");
122 	else
123 		snprintf(tmpbuf, sizeof(tmpbuf),
124 		    "%s %s", uloadbuf,
125 		    paused ? "PAUSED" : "");
126 
127 	snprintf(header, sizeof(header), "%-*s %19.19s %s", TIMEPOS - 1,
128 	    tmpbuf, hostname, timebuf);
129 
130 	if (rawmode)
131 		printf("\n\n%s\n", header);
132 	else
133 		mvprintw(0, 0, "%s", header);
134 
135 	return (1);
136 }
137 
138 /* compatibility functions, rearrange later */
139 void
140 error(const char *fmt, ...)
141 {
142 	va_list ap;
143 	char buf[MAX_LINE_BUF];
144 
145 	va_start(ap, fmt);
146 	vsnprintf(buf, sizeof buf, fmt, ap);
147 	va_end(ap);
148 
149 	message_set(buf);
150 }
151 
152 void
153 nlisterr(struct nlist namelist[])
154 {
155 	int i, n;
156 
157 	n = 0;
158 	clear();
159 	mvprintw(2, 10, "systat: nlist: can't find following symbols:");
160 	for (i = 0;
161 	    namelist[i].n_name != NULL && *namelist[i].n_name != '\0'; i++)
162 		if (namelist[i].n_value == 0)
163 			mvprintw(2 + ++n, 10, "%s", namelist[i].n_name);
164 	move(CMDLINE, 0);
165 	clrtoeol();
166 	refresh();
167 	endwin();
168 	exit(1);
169 }
170 
171 void
172 die(void)
173 {
174 	if (!rawmode)
175 		endwin();
176 	exit(0);
177 }
178 
179 
180 int
181 prefix(char *s1, char *s2)
182 {
183 
184 	while (*s1 == *s2) {
185 		if (*s1 == '\0')
186 			return (1);
187 		s1++, s2++;
188 	}
189 	return (*s1 == '\0');
190 }
191 
192 /* calculate number of users on the system */
193 int
194 ucount(void)
195 {
196 	int nusers = 0;
197 	struct	utmp utmp;
198 
199 	if (ut < 0)
200 		return (0);
201 	lseek(ut, (off_t)0, SEEK_SET);
202 	while (read(ut, &utmp, sizeof(utmp)))
203 		if (utmp.ut_name[0] != '\0')
204 			nusers++;
205 
206 	return (nusers);
207 }
208 
209 /* main program functions */
210 
211 void
212 usage(void)
213 {
214 	extern char *__progname;
215 	fprintf(stderr, "usage: %s [-aBbhiNn] [-d count] "
216 	    "[-s delay] [-w width] [view] [delay]\n", __progname);
217 	exit(1);
218 }
219 
220 void
221 show_view(void)
222 {
223 	if (rawmode)
224 		return;
225 
226 	tb_start();
227 	tbprintf("%s %g", curr_view->name, naptime);
228 	tb_end();
229 	message_set(tmp_buf);
230 }
231 
232 void
233 add_view_tb(field_view *v)
234 {
235 	if (curr_view == v)
236 		tbprintf("[%s] ", v->name);
237 	else
238 		tbprintf("%s ", v->name);
239 }
240 
241 void
242 show_help(void)
243 {
244 	if (rawmode)
245 		return;
246 
247 	tb_start();
248 	foreach_view(add_view_tb);
249 	tb_end();
250 	message_set(tmp_buf);
251 }
252 
253 void
254 add_order_tb(order_type *o)
255 {
256 	if (curr_view->mgr->order_curr == o)
257 		tbprintf("[%s%s(%c)] ", o->name,
258 		    o->func != NULL && sortdir == -1 ? "^" : "",
259 		    (char) o->hotkey);
260 	else
261 		tbprintf("%s(%c) ", o->name, (char) o->hotkey);
262 }
263 
264 void
265 show_order(void)
266 {
267 	if (rawmode)
268 		return;
269 
270 	tb_start();
271 	if (foreach_order(add_order_tb) == -1) {
272 		tbprintf("No orders available");
273 	}
274 	tb_end();
275 	message_set(tmp_buf);
276 }
277 
278 void
279 cmd_compat(const char *buf)
280 {
281 	const char *s;
282 
283 	if (strcasecmp(buf, "help") == 0) {
284 		show_help();
285 		need_update = 1;
286 		return;
287 	}
288 	if (strcasecmp(buf, "quit") == 0 || strcasecmp(buf, "q") == 0) {
289 		gotsig_close = 1;
290 		return;
291 	}
292 	if (strcasecmp(buf, "stop") == 0) {
293 		paused = 1;
294 		gotsig_alarm = 1;
295 		return;
296 	}
297 	if (strncasecmp(buf, "start", 5) == 0) {
298 		paused = 0;
299 		gotsig_alarm = 1;
300 		cmd_delay(buf + 5);
301 		return;
302 	}
303 	if (strncasecmp(buf, "order", 5) == 0) {
304 		show_order();
305 		need_update = 1;
306 		return;
307 	}
308 	if (strncasecmp(buf, "human", 5) == 0) {
309 		humanreadable = !humanreadable;
310 		return;
311 	}
312 
313 	for (s = buf; *s && strchr("0123456789+-.eE", *s) != NULL; s++)
314 		;
315 	if (*s) {
316 		if (set_view(buf))
317 			error("Invalid/ambiguous view: %s", buf);
318 	} else
319 		cmd_delay(buf);
320 }
321 
322 void
323 cmd_delay(const char *buf)
324 {
325 	double del;
326 	del = atof(buf);
327 
328 	if (del > 0) {
329 		udelay = (useconds_t)(del * 1000000);
330 		gotsig_alarm = 1;
331 		naptime = del;
332 	}
333 }
334 
335 void
336 cmd_count(const char *buf)
337 {
338 	const char *errstr;
339 
340 	maxprint = strtonum(buf, 1, lines - HEADER_LINES, &errstr);
341 	if (errstr)
342 		maxprint = lines - HEADER_LINES;
343 }
344 
345 
346 int
347 keyboard_callback(int ch)
348 {
349 	switch (ch) {
350 	case '?':
351 		/* FALLTHROUGH */
352 	case 'h':
353 		show_help();
354 		need_update = 1;
355 		break;
356 	case CTRL_G:
357 		show_view();
358 		need_update = 1;
359 		break;
360 	case 'l':
361 		command_set(&cm_count, NULL);
362 		break;
363 	case 's':
364 		command_set(&cm_delay, NULL);
365 		break;
366 	case ',':
367 		separate_thousands = !separate_thousands;
368 		gotsig_alarm = 1;
369 		break;
370 	case ':':
371 		command_set(&cm_compat, NULL);
372 		break;
373 	default:
374 		return 0;
375 	};
376 
377 	return 1;
378 }
379 
380 void
381 initialize(void)
382 {
383 	engine_initialize();
384 
385 	initvmstat();
386 	initpigs();
387 	initifstat();
388 	initiostat();
389 	initsensors();
390 	initmembufs();
391 	initnetstat();
392 	initswap();
393 	initpftop();
394 	initpf();
395 	initpool();
396 	initmalloc();
397 	initnfs();
398 	initcpu();
399 	inituvm();
400 }
401 
402 void
403 gethz(void)
404 {
405 	struct clockinfo cinf;
406 	size_t  size = sizeof(cinf);
407 	int	mib[2];
408 
409 	mib[0] = CTL_KERN;
410 	mib[1] = KERN_CLOCKRATE;
411 	if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1)
412 		return;
413 	stathz = cinf.stathz;
414 	hz = cinf.hz;
415 }
416 
417 int
418 main(int argc, char *argv[])
419 {
420 	char errbuf[_POSIX2_LINE_MAX];
421 	const char *errstr;
422 	extern char *optarg;
423 	extern int optind;
424 	double delay = 5;
425 
426 	char *viewstr = NULL;
427 
428 	gid_t gid;
429 	int countmax = 0;
430 	int maxlines = 0;
431 
432 	int ch;
433 
434 	ut = open(_PATH_UTMP, O_RDONLY);
435 	if (ut == -1) {
436 		warn("No utmp");
437 	}
438 
439 	kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
440 
441 	gid = getgid();
442 	if (setresgid(gid, gid, gid) == -1)
443 		err(1, "setresgid");
444 
445 	while ((ch = getopt(argc, argv, "BNabd:hins:w:")) != -1) {
446 		switch (ch) {
447 		case 'a':
448 			maxlines = -1;
449 			break;
450 		case 'B':
451 			averageonly = 1;
452 			if (countmax < 2)
453 				countmax = 2;
454 			/* FALLTHROUGH */
455 		case 'b':
456 			rawmode = 1;
457 			interactive = 0;
458 			break;
459 		case 'd':
460 			countmax = strtonum(optarg, 1, INT_MAX, &errstr);
461 			if (errstr)
462 				errx(1, "-d %s: %s", optarg, errstr);
463 			break;
464 		case 'h':
465 			humanreadable = 1;
466 			break;
467 		case 'i':
468 			interactive = 1;
469 			break;
470 		case 'N':
471 			nflag = 0;
472 			break;
473 		case 'n':
474 			/* this is a noop, -n is the default */
475 			nflag = 1;
476 			break;
477 		case 's':
478 			delay = atof(optarg);
479 			if (delay <= 0)
480 				delay = 5;
481 			break;
482 		case 'w':
483 			rawwidth = strtonum(optarg, 1, MAX_LINE_BUF-1, &errstr);
484 			if (errstr)
485 				errx(1, "-w %s: %s", optarg, errstr);
486 			break;
487 		default:
488 			usage();
489 			/* NOTREACHED */
490 		}
491 	}
492 
493 	if (kd == NULL)
494 		warnx("kvm_openfiles: %s", errbuf);
495 
496 	argc -= optind;
497 	argv += optind;
498 
499 	if (argc == 1) {
500 		double del = atof(argv[0]);
501 		if (del == 0)
502 			viewstr = argv[0];
503 		else
504 			delay = del;
505 	} else if (argc == 2) {
506 		viewstr = argv[0];
507 		delay = atof(argv[1]);
508 		if (delay <= 0)
509 			delay = 5;
510 	}
511 
512 	udelay = (useconds_t)(delay * 1000000.0);
513 	if (udelay < 1)
514 		udelay = 1;
515 
516 	naptime = (double)udelay / 1000000.0;
517 
518 	gethostname(hostname, sizeof (hostname));
519 	gethz();
520 
521 	initialize();
522 
523 	set_order(NULL);
524 	if (viewstr && set_view(viewstr)) {
525 		fprintf(stderr, "Unknown/ambiguous view name: %s\n", viewstr);
526 		return 1;
527 	}
528 
529 	if (check_termcap()) {
530 		rawmode = 1;
531 		interactive = 0;
532 	}
533 
534 	setup_term(maxlines);
535 
536 	if (unveil("/", "r") == -1)
537 		err(1, "unveil");
538 	if (unveil(NULL, NULL) == -1)
539 		err(1, "unveil");
540 
541 	if (rawmode && countmax == 0)
542 		countmax = 1;
543 
544 	gotsig_alarm = 1;
545 
546 	engine_loop(countmax);
547 
548 	return 0;
549 }
550