1/* $OpenBSD: main.c,v 1.77 2022/12/04 18:01:57 cheloha 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 <math.h>
44#include <netdb.h>
45#include <signal.h>
46#include <stdio.h>
47#include <stdint.h>
48#include <stdlib.h>
49#include <string.h>
50#include <stdarg.h>
51#include <unistd.h>
52#include <utmp.h>
53
54#include "engine.h"
55#include "systat.h"
56
57#define TIMEPOS (80 - 8 - 20 - 1)
58
59double	dellave;
60
61kvm_t	*kd;
62char	*nlistf = NULL;
63char	*memf = NULL;
64double	avenrun[3];
65double	naptime = 5.0;
66int	verbose = 1;		/* to report kvm read errs */
67int	nflag = 1;
68int	ut, hz;
69char    hostname[HOST_NAME_MAX+1];
70WINDOW  *wnd;
71int	CMDLINE;
72char	timebuf[26];
73char	uloadbuf[TIMEPOS];
74
75
76int  ucount(void);
77void usage(void);
78double strtodnum(const char *, double, double, const char **);
79
80/* command prompt */
81
82void cmd_delay(const char *);
83void cmd_count(const char *);
84void cmd_compat(const char *);
85
86struct command cm_compat = {"Command", cmd_compat};
87struct command cm_delay = {"Seconds to delay", cmd_delay};
88struct command cm_count = {"Number of lines to display", cmd_count};
89
90
91/* display functions */
92
93int
94print_header(void)
95{
96	time_t now;
97	int start = dispstart + 1, end = dispstart + maxprint;
98	char tmpbuf[TIMEPOS];
99	char header[MAX_LINE_BUF];
100
101	if (end > num_disp)
102		end = num_disp;
103
104	tb_start();
105
106	if (!paused) {
107		char *ctim;
108
109		getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0]));
110
111		snprintf(uloadbuf, sizeof(uloadbuf),
112		    "%4d users Load %.2f %.2f %.2f",
113		    ucount(), avenrun[0], avenrun[1], avenrun[2]);
114
115		time(&now);
116		ctim = ctime(&now);
117		ctim[11+8] = '\0';
118		strlcpy(timebuf, ctim + 11, sizeof(timebuf));
119	}
120
121	if (num_disp && (start > 1 || end != num_disp))
122		snprintf(tmpbuf, sizeof(tmpbuf),
123		    "%s (%u-%u of %u) %s", uloadbuf, start, end, num_disp,
124		    paused ? "PAUSED" : "");
125	else
126		snprintf(tmpbuf, sizeof(tmpbuf),
127		    "%s %s", uloadbuf,
128		    paused ? "PAUSED" : "");
129
130	snprintf(header, sizeof(header), "%-*s %19.19s %s", TIMEPOS - 1,
131	    tmpbuf, hostname, timebuf);
132
133	if (rawmode)
134		printf("\n\n%s\n", header);
135	else
136		mvprintw(0, 0, "%s", header);
137
138	return (1);
139}
140
141/* compatibility functions, rearrange later */
142void
143error(const char *fmt, ...)
144{
145	va_list ap;
146	char buf[MAX_LINE_BUF];
147
148	va_start(ap, fmt);
149	vsnprintf(buf, sizeof buf, fmt, ap);
150	va_end(ap);
151
152	message_set(buf);
153}
154
155void
156nlisterr(struct nlist namelist[])
157{
158	int i, n;
159
160	n = 0;
161	clear();
162	mvprintw(2, 10, "systat: nlist: can't find following symbols:");
163	for (i = 0;
164	    namelist[i].n_name != NULL && *namelist[i].n_name != '\0'; i++)
165		if (namelist[i].n_value == 0)
166			mvprintw(2 + ++n, 10, "%s", namelist[i].n_name);
167	move(CMDLINE, 0);
168	clrtoeol();
169	refresh();
170	endwin();
171	exit(1);
172}
173
174void
175die(void)
176{
177	if (!rawmode)
178		endwin();
179	exit(0);
180}
181
182
183int
184prefix(char *s1, char *s2)
185{
186
187	while (*s1 == *s2) {
188		if (*s1 == '\0')
189			return (1);
190		s1++, s2++;
191	}
192	return (*s1 == '\0');
193}
194
195/* calculate number of users on the system */
196int
197ucount(void)
198{
199	int nusers = 0;
200	struct	utmp utmp;
201
202	if (ut < 0)
203		return (0);
204	lseek(ut, (off_t)0, SEEK_SET);
205	while (read(ut, &utmp, sizeof(utmp)))
206		if (utmp.ut_name[0] != '\0')
207			nusers++;
208
209	return (nusers);
210}
211
212/* main program functions */
213
214void
215usage(void)
216{
217	extern char *__progname;
218	fprintf(stderr, "usage: %s [-aBbhiNn] [-d count] "
219	    "[-s delay] [-w width] [view] [delay]\n", __progname);
220	exit(1);
221}
222
223void
224show_view(void)
225{
226	if (rawmode)
227		return;
228
229	tb_start();
230	tbprintf("%s %g", curr_view->name, naptime);
231	tb_end();
232	message_set(tmp_buf);
233}
234
235void
236add_view_tb(field_view *v)
237{
238	if (curr_view == v)
239		tbprintf("[%s] ", v->name);
240	else
241		tbprintf("%s ", v->name);
242}
243
244void
245show_help(void)
246{
247	if (rawmode)
248		return;
249
250	tb_start();
251	foreach_view(add_view_tb);
252	tb_end();
253	message_set(tmp_buf);
254}
255
256void
257add_order_tb(order_type *o)
258{
259	if (curr_view->mgr->order_curr == o)
260		tbprintf("[%s%s(%c)] ", o->name,
261		    o->func != NULL && sortdir == -1 ? "^" : "",
262		    (char) o->hotkey);
263	else
264		tbprintf("%s(%c) ", o->name, (char) o->hotkey);
265}
266
267void
268show_order(void)
269{
270	if (rawmode)
271		return;
272
273	tb_start();
274	if (foreach_order(add_order_tb) == -1) {
275		tbprintf("No orders available");
276	}
277	tb_end();
278	message_set(tmp_buf);
279}
280
281void
282cmd_compat(const char *buf)
283{
284	const char *s;
285
286	if (strcasecmp(buf, "help") == 0) {
287		message_toggle(MESSAGE_HELP);
288		return;
289	}
290	if (strcasecmp(buf, "quit") == 0 || strcasecmp(buf, "q") == 0) {
291		gotsig_close = 1;
292		return;
293	}
294	if (strcasecmp(buf, "stop") == 0) {
295		paused = 1;
296		gotsig_alarm = 1;
297		return;
298	}
299	if (strncasecmp(buf, "start", 5) == 0) {
300		paused = 0;
301		gotsig_alarm = 1;
302		cmd_delay(buf + 5);
303		return;
304	}
305	if (strncasecmp(buf, "order", 5) == 0) {
306		message_toggle(MESSAGE_ORDER);
307		return;
308	}
309	if (strncasecmp(buf, "human", 5) == 0) {
310		humanreadable = !humanreadable;
311		return;
312	}
313
314	for (s = buf; *s && strchr("0123456789+-.eE", *s) != NULL; s++)
315		;
316	if (*s) {
317		if (set_view(buf))
318			error("Invalid/ambiguous view: %s", buf);
319	} else
320		cmd_delay(buf);
321}
322
323void
324cmd_delay(const char *buf)
325{
326	double del;
327	const char *errstr;
328
329	if (buf[0] == '\0')
330		return;
331	del = strtodnum(buf, 0, UINT32_MAX / 1000000, &errstr);
332	if (errstr != NULL)
333		error("s: \"%s\": delay value is %s", buf, errstr);
334	else {
335		refresh_delay(del);
336		gotsig_alarm = 1;
337		naptime = del;
338	}
339}
340
341void
342cmd_count(const char *buf)
343{
344	const char *errstr;
345
346	maxprint = strtonum(buf, 1, lines - HEADER_LINES, &errstr);
347	if (errstr)
348		maxprint = lines - HEADER_LINES;
349}
350
351
352int
353keyboard_callback(int ch)
354{
355	switch (ch) {
356	case '?':
357		/* FALLTHROUGH */
358	case 'h':
359		message_toggle(MESSAGE_HELP);
360		break;
361	case CTRL_G:
362		message_toggle(MESSAGE_VIEW);
363		break;
364	case 'l':
365		command_set(&cm_count, NULL);
366		break;
367	case 's':
368		command_set(&cm_delay, NULL);
369		break;
370	case ',':
371		separate_thousands = !separate_thousands;
372		gotsig_alarm = 1;
373		break;
374	case ':':
375		command_set(&cm_compat, NULL);
376		break;
377	default:
378		return 0;
379	};
380
381	return 1;
382}
383
384void
385initialize(void)
386{
387	engine_initialize();
388
389	initvmstat();
390	initpigs();
391	initifstat();
392	initiostat();
393	initsensors();
394	initmembufs();
395	initnetstat();
396	initswap();
397	initpftop();
398	initpf();
399	initpool();
400	initmalloc();
401	initnfs();
402	initcpu();
403	inituvm();
404}
405
406void
407gethz(void)
408{
409	struct clockinfo cinf;
410	size_t  size = sizeof(cinf);
411	int	mib[2];
412
413	mib[0] = CTL_KERN;
414	mib[1] = KERN_CLOCKRATE;
415	if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1)
416		return;
417	hz = cinf.hz;
418}
419
420#define	INVALID		1
421#define	TOOSMALL	2
422#define	TOOLARGE	3
423
424double
425strtodnum(const char *nptr, double minval, double maxval, const char **errstrp)
426{
427	double d = 0;
428	int error = 0;
429	char *ep;
430	struct errval {
431		const char *errstr;
432		int err;
433	} ev[4] = {
434		{ NULL,		0 },
435		{ "invalid",	EINVAL },
436		{ "too small",	ERANGE },
437		{ "too large",	ERANGE },
438	};
439
440	ev[0].err = errno;
441	errno = 0;
442	if (minval > maxval) {
443		error = INVALID;
444	} else {
445		d = strtod(nptr, &ep);
446		if (nptr == ep || *ep != '\0')
447			error = INVALID;
448		else if ((d == -HUGE_VAL && errno == ERANGE) || d < minval)
449			error = TOOSMALL;
450		else if ((d == HUGE_VAL && errno == ERANGE) || d > maxval)
451			error = TOOLARGE;
452	}
453	if (errstrp != NULL)
454		*errstrp = ev[error].errstr;
455	errno = ev[error].err;
456	if (error)
457		d = 0;
458
459	return (d);
460}
461
462int
463main(int argc, char *argv[])
464{
465	char errbuf[_POSIX2_LINE_MAX];
466	const char *errstr;
467	extern char *optarg;
468	extern int optind;
469	double delay = 5, del;
470
471	char *viewstr = NULL;
472
473	gid_t gid;
474	int countmax = 0;
475	int maxlines = 0;
476
477	int ch;
478
479	ut = open(_PATH_UTMP, O_RDONLY);
480	if (ut == -1) {
481		warn("No utmp");
482	}
483
484	kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
485
486	gid = getgid();
487	if (setresgid(gid, gid, gid) == -1)
488		err(1, "setresgid");
489
490	while ((ch = getopt(argc, argv, "BNabd:hins:w:")) != -1) {
491		switch (ch) {
492		case 'a':
493			maxlines = -1;
494			break;
495		case 'B':
496			averageonly = 1;
497			if (countmax < 2)
498				countmax = 2;
499			/* FALLTHROUGH */
500		case 'b':
501			rawmode = 1;
502			interactive = 0;
503			break;
504		case 'd':
505			countmax = strtonum(optarg, 1, INT_MAX, &errstr);
506			if (errstr)
507				errx(1, "-d %s: %s", optarg, errstr);
508			break;
509		case 'h':
510			humanreadable = 1;
511			break;
512		case 'i':
513			interactive = 1;
514			break;
515		case 'N':
516			nflag = 0;
517			break;
518		case 'n':
519			/* this is a noop, -n is the default */
520			nflag = 1;
521			break;
522		case 's':
523			delay = strtodnum(optarg, 0, UINT32_MAX / 1000000,
524			    &errstr);
525			if (errstr != NULL)
526				errx(1, "-s \"%s\": delay value is %s", optarg,
527				    errstr);
528			break;
529		case 'w':
530			rawwidth = strtonum(optarg, 1, MAX_LINE_BUF-1, &errstr);
531			if (errstr)
532				errx(1, "-w %s: %s", optarg, errstr);
533			break;
534		default:
535			usage();
536			/* NOTREACHED */
537		}
538	}
539
540	if (kd == NULL)
541		warnx("kvm_openfiles: %s", errbuf);
542
543	argc -= optind;
544	argv += optind;
545
546	if (argc == 1) {
547		del = strtodnum(argv[0], 0, UINT32_MAX / 1000000, &errstr);
548		if (errstr != NULL)
549			viewstr = argv[0];
550		else
551			delay = del;
552	} else if (argc == 2) {
553		viewstr = argv[0];
554		delay = strtodnum(argv[1], 0, UINT32_MAX / 1000000, &errstr);
555		if (errstr != NULL)
556			errx(1, "\"%s\": delay value is %s", argv[1], errstr);
557	}
558
559	refresh_delay(delay);
560	naptime = delay;
561
562	gethostname(hostname, sizeof (hostname));
563	gethz();
564
565	initialize();
566
567	set_order(NULL);
568	if (viewstr && set_view(viewstr)) {
569		fprintf(stderr, "Unknown/ambiguous view name: %s\n", viewstr);
570		return 1;
571	}
572
573	if (check_termcap()) {
574		rawmode = 1;
575		interactive = 0;
576	}
577
578	setup_term(maxlines);
579
580	if (unveil("/", "r") == -1)
581		err(1, "unveil /");
582	if (unveil(NULL, NULL) == -1)
583		err(1, "unveil");
584
585	if (rawmode && countmax == 0)
586		countmax = 1;
587
588	gotsig_alarm = 1;
589
590	engine_loop(countmax);
591
592	return 0;
593}
594