1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1980, 1992, 1993 5 * The Regents of the University of California. 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 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 33 34 #include <sys/param.h> 35 #include <sys/time.h> 36 #include <sys/sysctl.h> 37 #include <sys/queue.h> 38 39 #include <err.h> 40 #include <limits.h> 41 #include <locale.h> 42 #include <nlist.h> 43 #include <paths.h> 44 #include <signal.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 #include "systat.h" 51 #include "extern.h" 52 53 static int dellave; 54 55 kvm_t *kd; 56 double avenrun[3]; 57 int col; 58 unsigned int delay = 5000000; /* in microseconds */ 59 int verbose = 1; /* to report kvm read errs */ 60 static struct clockinfo clkinfo; 61 double hertz; 62 char c; 63 char *namp; 64 char hostname[MAXHOSTNAMELEN]; 65 WINDOW *wnd; 66 int CMDLINE; 67 68 static WINDOW *wload; /* one line window for load average */ 69 70 struct cmdentry { 71 SLIST_ENTRY(cmdentry) link; 72 char *cmd; /* Command name */ 73 char *argv; /* Arguments vector for a command */ 74 }; 75 static SLIST_HEAD(, cmdentry) commands; 76 77 static void 78 parse_cmd_args (int argc, char **argv) 79 { 80 int in_command = 0; 81 struct cmdentry *cmd = NULL; 82 double t; 83 84 while (argc) { 85 if (argv[0][0] == '-') { 86 if (in_command) 87 SLIST_INSERT_HEAD(&commands, cmd, link); 88 89 if (memcmp(argv[0], "--", 3) == 0) { 90 in_command = 0; /*-- ends a command explicitly*/ 91 argc --, argv ++; 92 continue; 93 } 94 cmd = calloc(1, sizeof(struct cmdentry)); 95 if (cmd == NULL) 96 errx(1, "memory allocating failure"); 97 cmd->cmd = strdup(&argv[0][1]); 98 if (cmd->cmd == NULL) 99 errx(1, "memory allocating failure"); 100 in_command = 1; 101 } 102 else if (!in_command) { 103 t = strtod(argv[0], NULL) * 1000000.0; 104 if (t > 0 && t < (double)UINT_MAX) 105 delay = (unsigned int)t; 106 } 107 else if (cmd != NULL) { 108 cmd->argv = strdup(argv[0]); 109 if (cmd->argv == NULL) 110 errx(1, "memory allocating failure"); 111 in_command = 0; 112 SLIST_INSERT_HEAD(&commands, cmd, link); 113 } 114 else 115 errx(1, "invalid arguments list"); 116 117 argc--, argv++; 118 } 119 if (in_command && cmd != NULL) 120 SLIST_INSERT_HEAD(&commands, cmd, link); 121 122 } 123 124 static void 125 resize(int signo __unused) 126 { 127 128 endwin(); 129 refresh(); 130 clear(); 131 132 CMDLINE = LINES - 1; 133 labels(); 134 display(); 135 status(); 136 } 137 138 139 int 140 main(int argc, char **argv) 141 { 142 char errbuf[_POSIX2_LINE_MAX], dummy; 143 size_t size; 144 struct cmdentry *cmd = NULL; 145 short cf, cb; 146 147 (void) setlocale(LC_ALL, ""); 148 149 SLIST_INIT(&commands); 150 argc--, argv++; 151 if (argc > 0) { 152 if (argv[0][0] == '-') { 153 struct cmdtab *p; 154 155 p = lookup(&argv[0][1]); 156 if (p == (struct cmdtab *)-1) 157 errx(1, "%s: ambiguous request", &argv[0][1]); 158 if (p == (struct cmdtab *)0) 159 errx(1, "%s: unknown request", &argv[0][1]); 160 curcmd = p; 161 argc--, argv++; 162 } 163 parse_cmd_args (argc, argv); 164 165 } 166 kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); 167 if (kd != NULL) { 168 struct nlist namelist[] = { { .n_name = "allproc" }, 169 { .n_name = NULL } }; 170 171 /* 172 * Try to actually read something, we may be in a jail, and 173 * have /dev/null opened as /dev/mem. 174 */ 175 if (kvm_nlist(kd, namelist) != 0 || namelist[0].n_value == 0 || 176 kvm_read(kd, namelist[0].n_value, &dummy, sizeof(dummy)) != 177 sizeof(dummy)) { 178 kvm_close(kd); 179 kd = NULL; 180 } 181 } 182 if (kd == NULL) { 183 /* 184 * Maybe we are lacking permissions? Retry, this time with bogus 185 * devices. We can now use sysctl only. 186 */ 187 kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL, 188 O_RDONLY, errbuf); 189 if (kd == NULL) { 190 error("%s", errbuf); 191 exit(1); 192 } 193 } 194 signal(SIGHUP, die); 195 signal(SIGINT, die); 196 signal(SIGQUIT, die); 197 signal(SIGTERM, die); 198 signal(SIGWINCH, resize); 199 200 /* 201 * Initialize display. Load average appears in a one line 202 * window of its own. Current command's display appears in 203 * an overlapping sub-window of stdscr configured by the display 204 * routines to minimize update work by curses. 205 */ 206 initscr(); 207 start_color(); 208 use_default_colors(); 209 pair_content(0, &cf, &cb); 210 init_pair(1, COLOR_GREEN, cb); 211 init_pair(2, COLOR_MAGENTA, cb); 212 init_pair(3, COLOR_RED, cb); 213 init_pair(4, COLOR_BLUE, cb); 214 CMDLINE = LINES - 1; 215 wnd = (*curcmd->c_open)(); 216 if (wnd == NULL) { 217 warnx("couldn't initialize display"); 218 die(0); 219 } 220 wload = newwin(1, 0, 1, 20); 221 if (wload == NULL) { 222 warnx("couldn't set up load average window"); 223 die(0); 224 } 225 gethostname(hostname, sizeof (hostname)); 226 size = sizeof(clkinfo); 227 if (sysctlbyname("kern.clockrate", &clkinfo, &size, NULL, 0) 228 || size != sizeof(clkinfo)) { 229 error("kern.clockrate"); 230 die(0); 231 } 232 hertz = clkinfo.stathz; 233 (*curcmd->c_init)(); 234 curcmd->c_flags |= CF_INIT; 235 labels(); 236 237 if (curcmd->c_cmd != NULL) 238 SLIST_FOREACH (cmd, &commands, link) 239 if (!curcmd->c_cmd(cmd->cmd, cmd->argv)) 240 warnx("command is not understood"); 241 242 dellave = 0.0; 243 display(); 244 noecho(); 245 crmode(); 246 keyboard(); 247 /*NOTREACHED*/ 248 249 return EXIT_SUCCESS; 250 } 251 252 void 253 labels(void) 254 { 255 if (curcmd->c_flags & CF_LOADAV) { 256 mvaddstr(0, 20, 257 "/0 /1 /2 /3 /4 /5 /6 /7 /8 /9 /10"); 258 mvaddstr(1, 5, "Load Average"); 259 } 260 if (curcmd->c_flags & CF_ZFSARC) { 261 mvaddstr(0, 20, 262 " Total MFU MRU Anon Hdr L2Hdr Other"); 263 mvaddstr(1, 5, "ZFS ARC "); 264 } 265 (*curcmd->c_label)(); 266 #ifdef notdef 267 mvprintw(21, 25, "CPU usage on %s", hostname); 268 #endif 269 refresh(); 270 } 271 272 void 273 display(void) 274 { 275 uint64_t arc_stat; 276 unsigned int ui; 277 int i, j; 278 279 /* Get the load average over the last minute. */ 280 (void) getloadavg(avenrun, nitems(avenrun)); 281 (*curcmd->c_fetch)(); 282 if (curcmd->c_flags & CF_LOADAV) { 283 j = 5.0*avenrun[0] + 0.5; 284 dellave -= avenrun[0]; 285 if (dellave >= 0.0) 286 c = '<'; 287 else { 288 c = '>'; 289 dellave = -dellave; 290 } 291 if (dellave < 0.1) 292 c = '|'; 293 dellave = avenrun[0]; 294 wmove(wload, 0, 0); wclrtoeol(wload); 295 for (i = MIN(j, 50); i > 0; i--) 296 waddch(wload, c); 297 if (j > 50) 298 wprintw(wload, " %4.1f", avenrun[0]); 299 } 300 if (curcmd->c_flags & CF_ZFSARC) { 301 uint64_t arc[7] = {}; 302 size_t size = sizeof(arc[0]); 303 if (sysctlbyname("kstat.zfs.misc.arcstats.size", 304 &arc[0], &size, NULL, 0) == 0 ) { 305 GETSYSCTL("kstat.zfs.misc.arcstats.mfu_size", arc[1]); 306 GETSYSCTL("kstat.zfs.misc.arcstats.mru_size", arc[2]); 307 GETSYSCTL("kstat.zfs.misc.arcstats.anon_size", arc[3]); 308 GETSYSCTL("kstat.zfs.misc.arcstats.hdr_size", arc[4]); 309 GETSYSCTL("kstat.zfs.misc.arcstats.l2_hdr_size", arc[5]); 310 GETSYSCTL("kstat.zfs.misc.arcstats.bonus_size", arc[6]); 311 GETSYSCTL("kstat.zfs.misc.arcstats.dnode_size", arc_stat); 312 arc[6] += arc_stat; 313 GETSYSCTL("kstat.zfs.misc.arcstats.dbuf_size", arc_stat); 314 arc[6] += arc_stat; 315 wmove(wload, 0, 0); wclrtoeol(wload); 316 for (ui = 0 ; ui < nitems(arc); ui++) 317 sysputuint64(wload, 0, ui*8+2, 6, arc[ui], 0); 318 } 319 } 320 (*curcmd->c_refresh)(); 321 if (curcmd->c_flags & (CF_LOADAV |CF_ZFSARC)) 322 wrefresh(wload); 323 wrefresh(wnd); 324 move(CMDLINE, col); 325 refresh(); 326 } 327 328 void 329 load(void) 330 { 331 332 (void) getloadavg(avenrun, nitems(avenrun)); 333 mvprintw(CMDLINE, 0, "%4.1f %4.1f %4.1f", 334 avenrun[0], avenrun[1], avenrun[2]); 335 clrtoeol(); 336 } 337 338 void 339 die(int signo __unused) 340 { 341 move(CMDLINE, 0); 342 clrtoeol(); 343 refresh(); 344 endwin(); 345 exit(0); 346 } 347 348 #include <stdarg.h> 349 350 void 351 error(const char *fmt, ...) 352 { 353 va_list ap; 354 char buf[255]; 355 int oy, ox; 356 357 va_start(ap, fmt); 358 if (wnd) { 359 getyx(stdscr, oy, ox); 360 (void) vsnprintf(buf, sizeof(buf), fmt, ap); 361 clrtoeol(); 362 standout(); 363 mvaddstr(CMDLINE, 0, buf); 364 standend(); 365 move(oy, ox); 366 refresh(); 367 } else { 368 (void) vfprintf(stderr, fmt, ap); 369 fprintf(stderr, "\n"); 370 } 371 va_end(ap); 372 } 373 374 void 375 nlisterr(struct nlist n_list[]) 376 { 377 int i, n; 378 379 n = 0; 380 clear(); 381 mvprintw(2, 10, "systat: nlist: can't find following symbols:"); 382 for (i = 0; 383 n_list[i].n_name != NULL && *n_list[i].n_name != '\0'; i++) 384 if (n_list[i].n_value == 0) 385 mvprintw(2 + ++n, 10, "%s", n_list[i].n_name); 386 move(CMDLINE, 0); 387 clrtoeol(); 388 refresh(); 389 endwin(); 390 exit(1); 391 } 392