/* $OpenBSD: main.c,v 1.78 2024/09/20 02:00:46 jsg Exp $ */ /* * Copyright (c) 2001, 2007 Can Erkin Acar * Copyright (c) 2001 Daniel Hartmeier * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "engine.h" #include "systat.h" #define TIMEPOS (80 - 8 - 20 - 1) double dellave; kvm_t *kd; char *nlistf = NULL; char *memf = NULL; double avenrun[3]; double naptime = 5.0; int verbose = 1; /* to report kvm read errs */ int nflag = 1; int ut, hz; char hostname[HOST_NAME_MAX+1]; WINDOW *wnd; int CMDLINE; char timebuf[26]; char uloadbuf[TIMEPOS]; int ucount(void); void usage(void); double strtodnum(const char *, double, double, const char **); /* command prompt */ void cmd_delay(const char *); void cmd_count(const char *); void cmd_compat(const char *); struct command cm_compat = {"Command", cmd_compat}; struct command cm_delay = {"Seconds to delay", cmd_delay}; struct command cm_count = {"Number of lines to display", cmd_count}; /* display functions */ int print_header(void) { time_t now; int start = dispstart + 1, end = dispstart + maxprint; char tmpbuf[TIMEPOS]; char header[MAX_LINE_BUF]; if (end > num_disp) end = num_disp; tb_start(); if (!paused) { char *ctim; getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])); snprintf(uloadbuf, sizeof(uloadbuf), "%4d users Load %.2f %.2f %.2f", ucount(), avenrun[0], avenrun[1], avenrun[2]); time(&now); ctim = ctime(&now); ctim[11+8] = '\0'; strlcpy(timebuf, ctim + 11, sizeof(timebuf)); } if (num_disp && (start > 1 || end != num_disp)) snprintf(tmpbuf, sizeof(tmpbuf), "%s (%u-%u of %u) %s", uloadbuf, start, end, num_disp, paused ? "PAUSED" : ""); else snprintf(tmpbuf, sizeof(tmpbuf), "%s %s", uloadbuf, paused ? "PAUSED" : ""); snprintf(header, sizeof(header), "%-*s %19.19s %s", TIMEPOS - 1, tmpbuf, hostname, timebuf); if (rawmode) printf("\n\n%s\n", header); else mvprintw(0, 0, "%s", header); return (1); } /* compatibility functions, rearrange later */ void error(const char *fmt, ...) { va_list ap; char buf[MAX_LINE_BUF]; va_start(ap, fmt); vsnprintf(buf, sizeof buf, fmt, ap); va_end(ap); message_set(buf); } void nlisterr(struct nlist namelist[]) { int i, n; n = 0; clear(); mvprintw(2, 10, "systat: nlist: can't find following symbols:"); for (i = 0; namelist[i].n_name != NULL && *namelist[i].n_name != '\0'; i++) if (namelist[i].n_value == 0) mvprintw(2 + ++n, 10, "%s", namelist[i].n_name); move(CMDLINE, 0); clrtoeol(); refresh(); endwin(); exit(1); } void die(void) { if (!rawmode) endwin(); exit(0); } int prefix(char *s1, char *s2) { while (*s1 == *s2) { if (*s1 == '\0') return (1); s1++, s2++; } return (*s1 == '\0'); } /* calculate number of users on the system */ int ucount(void) { int nusers = 0; struct utmp utmp; if (ut < 0) return (0); lseek(ut, (off_t)0, SEEK_SET); while (read(ut, &utmp, sizeof(utmp))) if (utmp.ut_name[0] != '\0') nusers++; return (nusers); } /* main program functions */ void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-aBbhiNn] [-d count] " "[-s delay] [-w width] [view] [delay]\n", __progname); exit(1); } void show_view(void) { if (rawmode) return; tb_start(); tbprintf("%s %g", curr_view->name, naptime); tb_end(); message_set(tmp_buf); } void add_view_tb(field_view *v) { if (curr_view == v) tbprintf("[%s] ", v->name); else tbprintf("%s ", v->name); } void show_help(void) { if (rawmode) return; tb_start(); foreach_view(add_view_tb); tb_end(); message_set(tmp_buf); } void add_order_tb(order_type *o) { if (curr_view->mgr->order_curr == o) tbprintf("[%s%s(%c)] ", o->name, o->func != NULL && sortdir == -1 ? "^" : "", (char) o->hotkey); else tbprintf("%s(%c) ", o->name, (char) o->hotkey); } void show_order(void) { if (rawmode) return; tb_start(); if (foreach_order(add_order_tb) == -1) { tbprintf("No orders available"); } tb_end(); message_set(tmp_buf); } void cmd_compat(const char *buf) { const char *s; if (strcasecmp(buf, "help") == 0) { message_toggle(MESSAGE_HELP); return; } if (strcasecmp(buf, "quit") == 0 || strcasecmp(buf, "q") == 0) { gotsig_close = 1; return; } if (strcasecmp(buf, "stop") == 0) { paused = 1; gotsig_alarm = 1; return; } if (strncasecmp(buf, "start", 5) == 0) { paused = 0; gotsig_alarm = 1; cmd_delay(buf + 5); return; } if (strncasecmp(buf, "order", 5) == 0) { message_toggle(MESSAGE_ORDER); return; } if (strncasecmp(buf, "human", 5) == 0) { humanreadable = !humanreadable; return; } for (s = buf; *s && strchr("0123456789+-.eE", *s) != NULL; s++) ; if (*s) { if (set_view(buf)) error("Invalid/ambiguous view: %s", buf); } else cmd_delay(buf); } void cmd_delay(const char *buf) { double del; const char *errstr; if (buf[0] == '\0') return; del = strtodnum(buf, 0, UINT32_MAX / 1000000, &errstr); if (errstr != NULL) error("s: \"%s\": delay value is %s", buf, errstr); else { refresh_delay(del); gotsig_alarm = 1; naptime = del; } } void cmd_count(const char *buf) { const char *errstr; maxprint = strtonum(buf, 1, lines - HEADER_LINES, &errstr); if (errstr) maxprint = lines - HEADER_LINES; } int keyboard_callback(int ch) { switch (ch) { case '?': /* FALLTHROUGH */ case 'h': message_toggle(MESSAGE_HELP); break; case CTRL_G: message_toggle(MESSAGE_VIEW); break; case 'l': command_set(&cm_count, NULL); break; case 's': command_set(&cm_delay, NULL); break; case ',': separate_thousands = !separate_thousands; gotsig_alarm = 1; break; case ':': command_set(&cm_compat, NULL); break; default: return 0; } return 1; } void initialize(void) { engine_initialize(); initvmstat(); initpigs(); initifstat(); initiostat(); initsensors(); initmembufs(); initnetstat(); initswap(); initpftop(); initpf(); initpool(); initmalloc(); initnfs(); initcpu(); inituvm(); } void gethz(void) { struct clockinfo cinf; size_t size = sizeof(cinf); int mib[2]; mib[0] = CTL_KERN; mib[1] = KERN_CLOCKRATE; if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1) return; hz = cinf.hz; } #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 double strtodnum(const char *nptr, double minval, double maxval, const char **errstrp) { double d = 0; int error = 0; char *ep; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) { error = INVALID; } else { d = strtod(nptr, &ep); if (nptr == ep || *ep != '\0') error = INVALID; else if ((d == -HUGE_VAL && errno == ERANGE) || d < minval) error = TOOSMALL; else if ((d == HUGE_VAL && errno == ERANGE) || d > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = ev[error].errstr; errno = ev[error].err; if (error) d = 0; return (d); } int main(int argc, char *argv[]) { char errbuf[_POSIX2_LINE_MAX]; const char *errstr; extern char *optarg; extern int optind; double delay = 5, del; char *viewstr = NULL; gid_t gid; int countmax = 0; int maxlines = 0; int ch; ut = open(_PATH_UTMP, O_RDONLY); if (ut == -1) { warn("No utmp"); } kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); gid = getgid(); if (setresgid(gid, gid, gid) == -1) err(1, "setresgid"); while ((ch = getopt(argc, argv, "BNabd:hins:w:")) != -1) { switch (ch) { case 'a': maxlines = -1; break; case 'B': averageonly = 1; if (countmax < 2) countmax = 2; /* FALLTHROUGH */ case 'b': rawmode = 1; interactive = 0; break; case 'd': countmax = strtonum(optarg, 1, INT_MAX, &errstr); if (errstr) errx(1, "-d %s: %s", optarg, errstr); break; case 'h': humanreadable = 1; break; case 'i': interactive = 1; break; case 'N': nflag = 0; break; case 'n': /* this is a noop, -n is the default */ nflag = 1; break; case 's': delay = strtodnum(optarg, 0, UINT32_MAX / 1000000, &errstr); if (errstr != NULL) errx(1, "-s \"%s\": delay value is %s", optarg, errstr); break; case 'w': rawwidth = strtonum(optarg, 1, MAX_LINE_BUF-1, &errstr); if (errstr) errx(1, "-w %s: %s", optarg, errstr); break; default: usage(); /* NOTREACHED */ } } if (kd == NULL) warnx("kvm_openfiles: %s", errbuf); argc -= optind; argv += optind; if (argc == 1) { del = strtodnum(argv[0], 0, UINT32_MAX / 1000000, &errstr); if (errstr != NULL) viewstr = argv[0]; else delay = del; } else if (argc == 2) { viewstr = argv[0]; delay = strtodnum(argv[1], 0, UINT32_MAX / 1000000, &errstr); if (errstr != NULL) errx(1, "\"%s\": delay value is %s", argv[1], errstr); } refresh_delay(delay); naptime = delay; gethostname(hostname, sizeof (hostname)); gethz(); initialize(); set_order(NULL); if (viewstr && set_view(viewstr)) { fprintf(stderr, "Unknown/ambiguous view name: %s\n", viewstr); return 1; } if (check_termcap()) { rawmode = 1; interactive = 0; } setup_term(maxlines); if (unveil("/", "r") == -1) err(1, "unveil /"); if (unveil(NULL, NULL) == -1) err(1, "unveil"); if (rawmode && countmax == 0) countmax = 1; gotsig_alarm = 1; engine_loop(countmax); return 0; }