Annotation of src/usr.bin/systat/main.c, Revision 1.73
1.73 ! martijn 1: /* $OpenBSD: main.c,v 1.72 2020/01/12 20:51:08 martijn Exp $ */
1.38 canacar 2: /*
3: * Copyright (c) 2001, 2007 Can Erkin Acar
4: * Copyright (c) 2001 Daniel Hartmeier
5: * All rights reserved.
1.1 deraadt 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: *
1.38 canacar 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: *
1.1 deraadt 31: */
32:
1.38 canacar 33: #include <sys/types.h>
1.21 deraadt 34: #include <sys/sysctl.h>
1.1 deraadt 35:
1.38 canacar 36:
37: #include <ctype.h>
38: #include <curses.h>
1.1 deraadt 39: #include <err.h>
1.38 canacar 40: #include <errno.h>
41: #include <fcntl.h>
42: #include <limits.h>
1.73 ! martijn 43: #include <math.h>
1.38 canacar 44: #include <netdb.h>
1.1 deraadt 45: #include <signal.h>
46: #include <stdio.h>
1.73 ! martijn 47: #include <stdint.h>
1.38 canacar 48: #include <stdlib.h>
1.1 deraadt 49: #include <string.h>
1.38 canacar 50: #include <stdarg.h>
1.3 deraadt 51: #include <unistd.h>
1.35 deraadt 52: #include <utmp.h>
1.1 deraadt 53:
1.38 canacar 54: #include "engine.h"
1.1 deraadt 55: #include "systat.h"
56:
1.66 otto 57: #define TIMEPOS (80 - 8 - 20 - 1)
1.58 lum 58:
1.21 deraadt 59: double dellave;
1.1 deraadt 60:
1.21 deraadt 61: kvm_t *kd;
62: char *nlistf = NULL;
1.20 pvalchev 63: char *memf = NULL;
1.21 deraadt 64: double avenrun[3];
1.36 tedu 65: double naptime = 5.0;
1.21 deraadt 66: int verbose = 1; /* to report kvm read errs */
1.38 canacar 67: int nflag = 1;
1.35 deraadt 68: int ut, hz, stathz;
1.61 deraadt 69: char hostname[HOST_NAME_MAX+1];
1.1 deraadt 70: WINDOW *wnd;
1.32 deraadt 71: int CMDLINE;
1.58 lum 72: char timebuf[26];
73: char uloadbuf[TIMEPOS];
1.1 deraadt 74:
1.38 canacar 75:
1.50 canacar 76: int ucount(void);
77: void usage(void);
1.73 ! martijn 78: double strtodnum(const char *, double, double, const char **);
1.50 canacar 79:
1.38 canacar 80: /* command prompt */
81:
1.46 canacar 82: void cmd_delay(const char *);
83: void cmd_count(const char *);
84: void cmd_compat(const char *);
1.38 canacar 85:
86: struct command cm_compat = {"Command", cmd_compat};
87: struct command cm_delay = {"Seconds to delay", cmd_delay};
88: struct command cm_count = {"Number of lines to display", cmd_count};
1.1 deraadt 89:
1.38 canacar 90:
91: /* display functions */
1.3 deraadt 92:
1.2 deraadt 93: int
1.38 canacar 94: print_header(void)
1.1 deraadt 95: {
1.50 canacar 96: time_t now;
1.40 deraadt 97: int start = dispstart + 1, end = dispstart + maxprint;
1.58 lum 98: char tmpbuf[TIMEPOS];
99: char header[MAX_LINE_BUF];
1.38 canacar 100:
101: if (end > num_disp)
102: end = num_disp;
103:
104: tb_start();
1.35 deraadt 105:
1.58 lum 106: if (!paused) {
1.65 tedu 107: char *ctim;
108:
1.58 lum 109: getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0]));
1.38 canacar 110:
1.58 lum 111: snprintf(uloadbuf, sizeof(uloadbuf),
1.66 otto 112: "%4d users Load %.2f %.2f %.2f",
1.58 lum 113: ucount(), avenrun[0], avenrun[1], avenrun[2]);
1.25 deraadt 114:
1.58 lum 115: time(&now);
1.65 tedu 116: ctim = ctime(&now);
117: ctim[11+8] = '\0';
118: strlcpy(timebuf, ctim + 11, sizeof(timebuf));
1.58 lum 119: }
1.25 deraadt 120:
1.58 lum 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:
1.66 otto 130: snprintf(header, sizeof(header), "%-*s %19.19s %s", TIMEPOS - 1,
131: tmpbuf, hostname, timebuf);
1.58 lum 132:
133: if (rawmode)
134: printf("\n\n%s\n", header);
135: else
136: mvprintw(0, 0, "%s", header);
1.24 deraadt 137:
1.38 canacar 138: return (1);
1.1 deraadt 139: }
1.3 deraadt 140:
1.38 canacar 141: /* compatibility functions, rearrange later */
1.21 deraadt 142: void
1.38 canacar 143: error(const char *fmt, ...)
1.21 deraadt 144: {
1.38 canacar 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);
1.21 deraadt 151:
1.38 canacar 152: message_set(buf);
1.21 deraadt 153: }
154:
1.38 canacar 155: void
156: nlisterr(struct nlist namelist[])
1.3 deraadt 157: {
1.38 canacar 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();
1.3 deraadt 171: exit(1);
172: }
173:
1.1 deraadt 174: void
1.38 canacar 175: die(void)
1.1 deraadt 176: {
1.38 canacar 177: if (!rawmode)
178: endwin();
179: exit(0);
1.1 deraadt 180: }
181:
1.38 canacar 182:
183: int
184: prefix(char *s1, char *s2)
1.1 deraadt 185: {
1.38 canacar 186:
187: while (*s1 == *s2) {
188: if (*s1 == '\0')
189: return (1);
190: s1++, s2++;
191: }
192: return (*s1 == '\0');
1.21 deraadt 193: }
194:
1.38 canacar 195: /* calculate number of users on the system */
196: int
197: ucount(void)
1.21 deraadt 198: {
1.38 canacar 199: int nusers = 0;
200: struct utmp utmp;
1.35 deraadt 201:
1.38 canacar 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++;
1.35 deraadt 208:
1.38 canacar 209: return (nusers);
1.1 deraadt 210: }
211:
1.38 canacar 212: /* main program functions */
213:
1.1 deraadt 214: void
1.50 canacar 215: usage(void)
1.1 deraadt 216: {
1.38 canacar 217: extern char *__progname;
1.72 martijn 218: fprintf(stderr, "usage: %s [-aBbhiNn] [-d count] "
1.43 matthieu 219: "[-s delay] [-w width] [view] [delay]\n", __progname);
1.38 canacar 220: exit(1);
1.1 deraadt 221: }
222:
1.45 canacar 223: void
224: show_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: }
1.21 deraadt 234:
1.1 deraadt 235: void
1.38 canacar 236: add_view_tb(field_view *v)
1.1 deraadt 237: {
1.38 canacar 238: if (curr_view == v)
239: tbprintf("[%s] ", v->name);
240: else
241: tbprintf("%s ", v->name);
1.29 deraadt 242: }
243:
244: void
1.38 canacar 245: show_help(void)
1.29 deraadt 246: {
1.38 canacar 247: if (rawmode)
248: return;
249:
250: tb_start();
251: foreach_view(add_view_tb);
252: tb_end();
253: message_set(tmp_buf);
1.21 deraadt 254: }
255:
256: void
1.67 martijn 257: add_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:
267: void
268: show_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:
281: void
1.46 canacar 282: cmd_compat(const char *buf)
1.21 deraadt 283: {
1.46 canacar 284: const char *s;
1.38 canacar 285:
1.46 canacar 286: if (strcasecmp(buf, "help") == 0) {
1.38 canacar 287: show_help();
288: need_update = 1;
289: return;
290: }
1.46 canacar 291: if (strcasecmp(buf, "quit") == 0 || strcasecmp(buf, "q") == 0) {
1.38 canacar 292: gotsig_close = 1;
293: return;
294: }
1.46 canacar 295: if (strcasecmp(buf, "stop") == 0) {
296: paused = 1;
297: gotsig_alarm = 1;
298: return;
299: }
300: if (strncasecmp(buf, "start", 5) == 0) {
301: paused = 0;
302: gotsig_alarm = 1;
1.47 canacar 303: cmd_delay(buf + 5);
1.67 martijn 304: return;
305: }
306: if (strncasecmp(buf, "order", 5) == 0) {
307: show_order();
308: need_update = 1;
1.46 canacar 309: return;
310: }
1.72 martijn 311: if (strncasecmp(buf, "human", 5) == 0) {
312: humanreadable = !humanreadable;
313: return;
314: }
1.38 canacar 315:
1.46 canacar 316: for (s = buf; *s && strchr("0123456789+-.eE", *s) != NULL; s++)
1.38 canacar 317: ;
318: if (*s) {
1.46 canacar 319: if (set_view(buf))
1.49 espie 320: error("Invalid/ambiguous view: %s", buf);
1.38 canacar 321: } else
1.46 canacar 322: cmd_delay(buf);
1.38 canacar 323: }
324:
325: void
1.46 canacar 326: cmd_delay(const char *buf)
1.38 canacar 327: {
328: double del;
1.73 ! martijn 329: const char *errstr;
1.39 canacar 330:
1.73 ! martijn 331: if (buf[0] == '\0')
! 332: return;
! 333: del = strtodnum(buf, 0, UINT32_MAX / 1000000, &errstr);
! 334: if (errstr != NULL)
! 335: error("s: \"%s\": delay value is %s", buf, errstr);
! 336: else {
1.38 canacar 337: udelay = (useconds_t)(del * 1000000);
338: gotsig_alarm = 1;
1.39 canacar 339: naptime = del;
1.13 deraadt 340: }
1.1 deraadt 341: }
1.14 kstailey 342:
343: void
1.46 canacar 344: cmd_count(const char *buf)
1.14 kstailey 345: {
1.63 deraadt 346: const char *errstr;
1.38 canacar 347:
1.63 deraadt 348: maxprint = strtonum(buf, 1, lines - HEADER_LINES, &errstr);
349: if (errstr)
1.38 canacar 350: maxprint = lines - HEADER_LINES;
1.14 kstailey 351: }
352:
1.38 canacar 353:
354: int
355: keyboard_callback(int ch)
1.1 deraadt 356: {
1.38 canacar 357: switch (ch) {
358: case '?':
359: /* FALLTHROUGH */
360: case 'h':
361: show_help();
1.45 canacar 362: need_update = 1;
363: break;
364: case CTRL_G:
365: show_view();
1.38 canacar 366: need_update = 1;
367: break;
368: case 'l':
369: command_set(&cm_count, NULL);
370: break;
371: case 's':
372: command_set(&cm_delay, NULL);
1.59 mpf 373: break;
374: case ',':
375: separate_thousands = !separate_thousands;
376: gotsig_alarm = 1;
1.38 canacar 377: break;
378: case ':':
379: command_set(&cm_compat, NULL);
380: break;
381: default:
382: return 0;
383: };
384:
385: return 1;
386: }
1.23 millert 387:
1.38 canacar 388: void
389: initialize(void)
390: {
391: engine_initialize();
392:
393: initvmstat();
394: initpigs();
395: initifstat();
396: initiostat();
397: initsensors();
398: initmembufs();
399: initnetstat();
400: initswap();
401: initpftop();
402: initpf();
1.48 canacar 403: initpool();
1.51 canacar 404: initmalloc();
1.52 jasper 405: initnfs();
1.60 reyk 406: initcpu();
1.68 krw 407: inituvm();
1.1 deraadt 408: }
409:
410: void
1.38 canacar 411: gethz(void)
1.1 deraadt 412: {
1.38 canacar 413: struct clockinfo cinf;
414: size_t size = sizeof(cinf);
415: int mib[2];
1.1 deraadt 416:
1.38 canacar 417: mib[0] = CTL_KERN;
418: mib[1] = KERN_CLOCKRATE;
419: if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1)
420: return;
421: stathz = cinf.stathz;
422: hz = cinf.hz;
1.35 deraadt 423: }
424:
1.73 ! martijn 425: #define INVALID 1
! 426: #define TOOSMALL 2
! 427: #define TOOLARGE 3
! 428:
! 429: double
! 430: strtodnum(const char *nptr, double minval, double maxval, const char **errstrp)
! 431: {
! 432: double d = 0;
! 433: int error = 0;
! 434: char *ep;
! 435: struct errval {
! 436: const char *errstr;
! 437: int err;
! 438: } ev[4] = {
! 439: { NULL, 0 },
! 440: { "invalid", EINVAL },
! 441: { "too small", ERANGE },
! 442: { "too large", ERANGE },
! 443: };
! 444:
! 445: ev[0].err = errno;
! 446: errno = 0;
! 447: if (minval > maxval) {
! 448: error = INVALID;
! 449: } else {
! 450: d = strtod(nptr, &ep);
! 451: if (nptr == ep || *ep != '\0')
! 452: error = INVALID;
! 453: else if ((d == -HUGE_VAL && errno == ERANGE) || d < minval)
! 454: error = TOOSMALL;
! 455: else if ((d == HUGE_VAL && errno == ERANGE) || d > maxval)
! 456: error = TOOLARGE;
! 457: }
! 458: if (errstrp != NULL)
! 459: *errstrp = ev[error].errstr;
! 460: errno = ev[error].err;
! 461: if (error)
! 462: d = 0;
! 463:
! 464: return (d);
! 465: }
! 466:
1.35 deraadt 467: int
1.38 canacar 468: main(int argc, char *argv[])
1.35 deraadt 469: {
1.38 canacar 470: char errbuf[_POSIX2_LINE_MAX];
1.63 deraadt 471: const char *errstr;
1.38 canacar 472: extern char *optarg;
473: extern int optind;
1.73 ! martijn 474: double delay = 5, del;
1.38 canacar 475:
476: char *viewstr = NULL;
477:
478: gid_t gid;
479: int countmax = 0;
480: int maxlines = 0;
481:
482: int ch;
483:
484: ut = open(_PATH_UTMP, O_RDONLY);
1.70 deraadt 485: if (ut == -1) {
1.38 canacar 486: warn("No utmp");
487: }
488:
1.62 claudio 489: kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
1.38 canacar 490:
491: gid = getgid();
492: if (setresgid(gid, gid, gid) == -1)
493: err(1, "setresgid");
494:
1.72 martijn 495: while ((ch = getopt(argc, argv, "BNabd:hins:w:")) != -1) {
1.38 canacar 496: switch (ch) {
497: case 'a':
498: maxlines = -1;
499: break;
1.60 reyk 500: case 'B':
501: averageonly = 1;
502: if (countmax < 2)
503: countmax = 2;
504: /* FALLTHROUGH */
1.38 canacar 505: case 'b':
506: rawmode = 1;
507: interactive = 0;
508: break;
509: case 'd':
1.63 deraadt 510: countmax = strtonum(optarg, 1, INT_MAX, &errstr);
511: if (errstr)
512: errx(1, "-d %s: %s", optarg, errstr);
1.72 martijn 513: break;
514: case 'h':
515: humanreadable = 1;
1.38 canacar 516: break;
517: case 'i':
518: interactive = 1;
519: break;
1.55 sthen 520: case 'N':
521: nflag = 0;
522: break;
1.38 canacar 523: case 'n':
1.55 sthen 524: /* this is a noop, -n is the default */
1.38 canacar 525: nflag = 1;
526: break;
527: case 's':
1.73 ! martijn 528: delay = strtodnum(optarg, 0, UINT32_MAX / 1000000,
! 529: &errstr);
! 530: if (errstr != NULL)
! 531: errx(1, "-s \"%s\": delay value is %s", optarg,
! 532: errstr);
1.38 canacar 533: break;
534: case 'w':
1.63 deraadt 535: rawwidth = strtonum(optarg, 1, MAX_LINE_BUF-1, &errstr);
536: if (errstr)
537: errx(1, "-w %s: %s", optarg, errstr);
1.38 canacar 538: break;
539: default:
540: usage();
541: /* NOTREACHED */
542: }
543: }
1.43 matthieu 544:
545: if (kd == NULL)
546: warnx("kvm_openfiles: %s", errbuf);
1.38 canacar 547:
548: argc -= optind;
549: argv += optind;
550:
551: if (argc == 1) {
1.73 ! martijn 552: del = strtodnum(argv[0], 0, UINT32_MAX / 1000000, &errstr);
! 553: if (errstr != NULL)
1.38 canacar 554: viewstr = argv[0];
555: else
556: delay = del;
557: } else if (argc == 2) {
558: viewstr = argv[0];
1.73 ! martijn 559: delay = strtodnum(argv[1], 0, UINT32_MAX / 1000000, &errstr);
! 560: if (errstr != NULL)
! 561: errx(1, "\"%s\": delay value is %s", argv[1], errstr);
1.38 canacar 562: }
563:
564: udelay = (useconds_t)(delay * 1000000.0);
565: if (udelay < 1)
566: udelay = 1;
1.39 canacar 567:
568: naptime = (double)udelay / 1000000.0;
1.38 canacar 569:
570: gethostname(hostname, sizeof (hostname));
571: gethz();
572:
573: initialize();
574:
575: set_order(NULL);
576: if (viewstr && set_view(viewstr)) {
1.49 espie 577: fprintf(stderr, "Unknown/ambiguous view name: %s\n", viewstr);
1.38 canacar 578: return 1;
579: }
580:
1.57 lum 581: if (check_termcap()) {
1.38 canacar 582: rawmode = 1;
583: interactive = 0;
584: }
585:
586: setup_term(maxlines);
1.71 deraadt 587:
588: if (unveil("/", "r") == -1)
589: err(1, "unveil");
590: if (unveil(NULL, NULL) == -1)
591: err(1, "unveil");
1.38 canacar 592:
593: if (rawmode && countmax == 0)
594: countmax = 1;
595:
596: gotsig_alarm = 1;
1.35 deraadt 597:
1.38 canacar 598: engine_loop(countmax);
1.35 deraadt 599:
1.38 canacar 600: return 0;
1.1 deraadt 601: }