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