1 /* $NetBSD: main.c,v 1.55 2019/01/25 15:31:11 christos Exp $ */ 2 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 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1980, 1992, 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #if 0 37 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; 38 #endif 39 __RCSID("$NetBSD: main.c,v 1.55 2019/01/25 15:31:11 christos Exp $"); 40 #endif /* not lint */ 41 42 #include <sys/param.h> 43 #include <sys/sysctl.h> 44 #include <sys/ioctl.h> 45 46 #include <ctype.h> 47 #include <err.h> 48 #include <errno.h> 49 #include <limits.h> 50 #include <signal.h> 51 #include <stdarg.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 #include <termios.h> 57 58 #include "systat.h" 59 #include "extern.h" 60 #include "drvstats.h" 61 62 static int dellave; 63 64 kvm_t *kd; 65 char *memf = NULL; 66 char *nlistf = NULL; 67 sig_t sigtstpdfl; 68 double avenrun[3]; 69 int col; 70 double naptime = 1; 71 int verbose = 1; /* to report kvm read errs */ 72 int hz, stathz, maxslp; 73 char c; 74 char *namp; 75 char hostname[MAXHOSTNAMELEN + 1]; 76 WINDOW *wnd; 77 int CMDLINE; 78 int turns = 2; /* stay how many refresh-turns in 'all' mode? */ 79 int allflag; 80 int allcounter; 81 sig_atomic_t needsredraw = 0; 82 float hertz; 83 double etime; 84 85 static WINDOW *wload; /* one line window for load average */ 86 87 static void (*sv_stop_handler)(int); 88 89 static void stop(int); 90 __dead static void usage(void); 91 92 gid_t egid; /* XXX needed by initiostat() and initkre() */ 93 94 int 95 main(int argc, char **argv) 96 { 97 int ch; 98 char errbuf[_POSIX2_LINE_MAX]; 99 const char *all; 100 struct clockinfo clk; 101 size_t len; 102 int bflag = 0; 103 104 all = "all"; 105 egid = getegid(); 106 (void)setegid(getgid()); 107 108 while ((ch = getopt(argc, argv, "M:N:bnw:t:")) != -1) 109 switch(ch) { 110 case 'M': 111 memf = optarg; 112 break; 113 case 'N': 114 nlistf = optarg; 115 break; 116 case 'b': 117 bflag = !bflag; 118 break; 119 case 'n': 120 nflag = !nflag; 121 break; 122 case 't': 123 if ((turns = atoi(optarg)) <= 0) 124 errx(1, "turns <= 0."); 125 break; 126 case 'w': 127 if ((naptime = strtod(optarg, NULL)) <= 0) 128 errx(1, "interval <= 0."); 129 break; 130 case '?': 131 default: 132 usage(); 133 } 134 argc -= optind; 135 argv += optind; 136 137 138 for ( ; argc > 0; argc--, argv++) { 139 struct mode *p; 140 int modefound = 0; 141 142 if (isdigit((unsigned char)argv[0][0])) { 143 naptime = strtod(argv[0], NULL); 144 if (naptime <= 0) 145 naptime = 5; 146 continue; 147 } 148 149 for (p = modes; p->c_name ; p++) { 150 if (strstr(p->c_name, argv[0]) == p->c_name) { 151 curmode = p; 152 modefound++; 153 break; 154 } 155 156 if (strstr(all, argv[0]) == all) { 157 allcounter=0; 158 allflag=1; 159 } 160 } 161 162 163 if (!modefound && !allflag) 164 error("%s: Unknown command.", argv[0]); 165 } 166 167 /* 168 * Discard setgid privileges. If not the running kernel, we toss 169 * them away totally so that bad guys can't print interesting stuff 170 * from kernel memory, otherwise switch back to kmem for the 171 * duration of the kvm_openfiles() call. 172 */ 173 if (nlistf != NULL || memf != NULL) 174 (void)setgid(getgid()); 175 else 176 (void)setegid(egid); 177 178 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 179 if (kd == NULL) 180 errx(1, "%s", errbuf); 181 182 /* Get rid of privs for now. */ 183 if (nlistf == NULL && memf == NULL) 184 (void)setegid(getgid()); 185 186 signal(SIGINT, die); 187 signal(SIGQUIT, die); 188 signal(SIGTERM, die); 189 sv_stop_handler = signal(SIGTSTP, stop); 190 191 /* 192 * Initialize display. Load average appears in a one line 193 * window of its own. Current command's display appears in 194 * an overlapping sub-window of stdscr configured by the display 195 * routines to minimize update work by curses. 196 */ 197 if (initscr() == NULL) 198 errx(1, "couldn't initialize screen"); 199 200 CMDLINE = LINES - 1; 201 wnd = (*curmode->c_open)(); 202 if (wnd == NULL) { 203 move(CMDLINE, 0); 204 clrtoeol(); 205 refresh(); 206 endwin(); 207 warnx("couldn't initialize display"); 208 die(0); 209 } 210 wload = newwin(1, 0, 3, 20); 211 if (wload == NULL) { 212 move(CMDLINE, 0); 213 clrtoeol(); 214 refresh(); 215 endwin(); 216 warnx("couldn't set up load average window"); 217 die(0); 218 } 219 gethostname(hostname, sizeof (hostname)); 220 hostname[sizeof(hostname) - 1] = '\0'; 221 222 len = sizeof(clk); 223 if (sysctlbyname("kern.clockrate", &clk, &len, NULL, 0)) 224 error("can't get \"kern.clockrate\": %s", strerror(errno)); 225 hz = clk.hz; 226 stathz = clk.stathz; 227 228 len = sizeof(maxslp); 229 if (sysctlbyname("vm.maxslp", &maxslp, &len, NULL, 0)) 230 error("can't get \"vm.maxslp\": %s", strerror(errno)); 231 232 (*curmode->c_init)(); 233 curmode->c_flags |= CF_INIT; 234 labels(); 235 236 dellave = 0.0; 237 238 display(0); 239 if (!bflag) { 240 noecho(); 241 cbreak(); 242 keyboard(); 243 } else 244 die(0); 245 /*NOTREACHED*/ 246 } 247 248 static void 249 usage(void) 250 { 251 fprintf(stderr, "usage: systat [-bn] [-M core] [-N system] [-w wait] " 252 "[-t turns]\n\t\t[display] [refresh-interval]\n"); 253 exit(1); 254 } 255 256 257 void 258 labels(void) 259 { 260 if (curmode->c_flags & CF_LOADAV) { 261 mvaddstr(2, 20, 262 "/0 /1 /2 /3 /4 /5 /6 /7 /8 /9 /10"); 263 mvaddstr(3, 5, "Load Average"); 264 } 265 (*curmode->c_label)(); 266 #ifdef notdef 267 mvprintw(21, 25, "CPU usage on %s", hostname); 268 #endif 269 refresh(); 270 } 271 272 void 273 display(int signo) 274 { 275 static int skip; 276 int j; 277 struct mode *p; 278 int ms_delay; 279 280 if (signo == SIGALRM && skip-- > 0) 281 /* Don't display on this timeout */ 282 return; 283 284 /* Get the load average over the last minute. */ 285 (void)getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])); 286 (*curmode->c_fetch)(); 287 if (curmode->c_flags & CF_LOADAV) { 288 j = 5.0*avenrun[0] + 0.5; 289 dellave -= avenrun[0]; 290 if (dellave >= 0.0) 291 c = '<'; 292 else { 293 c = '>'; 294 dellave = -dellave; 295 } 296 if (dellave < 0.1) 297 c = '|'; 298 dellave = avenrun[0]; 299 wmove(wload, 0, 0); 300 wclrtoeol(wload); 301 whline(wload, c, (j > 50) ? 50 : j); 302 if (j > 50) 303 wprintw(wload, " %4.1f", avenrun[0]); 304 } 305 (*curmode->c_refresh)(); 306 if (curmode->c_flags & CF_LOADAV) 307 wrefresh(wload); 308 wrefresh(wnd); 309 move(CMDLINE, col); 310 refresh(); 311 312 if (allflag && signo==SIGALRM) { 313 if (allcounter >= turns){ 314 p = curmode; 315 p++; 316 if (p->c_name == NULL) 317 p = modes; 318 switch_mode(p); 319 allcounter=0; 320 } else 321 allcounter++; 322 } 323 324 /* curses timeout() uses VTIME, limited to 255 1/10th secs */ 325 ms_delay = naptime * 1000; 326 if (ms_delay < 25500) { 327 timeout(ms_delay); 328 skip = 0; 329 } else { 330 skip = ms_delay / 25500; 331 timeout(ms_delay / (skip + 1)); 332 } 333 } 334 335 void 336 redraw(void) 337 { 338 CMDLINE = LINES - 1; 339 labels(); 340 341 display(0); 342 needsredraw = 0; 343 } 344 345 static void 346 stop(int signo) 347 { 348 sigset_t set; 349 350 signal(SIGTSTP, sv_stop_handler); 351 /* unblock SIGTSTP */ 352 sigemptyset(&set); 353 sigaddset(&set, SIGTSTP); 354 sigprocmask(SIG_UNBLOCK, &set, NULL); 355 /* stop ourselves */ 356 kill(0, SIGTSTP); 357 /* must have been restarted */ 358 signal(SIGTSTP, stop); 359 needsredraw = signo; 360 } 361 362 void 363 die(int signo) 364 { 365 move(CMDLINE, 0); 366 clrtoeol(); 367 refresh(); 368 endwin(); 369 exit(0); 370 } 371 372 void 373 error(const char *fmt, ...) 374 { 375 va_list ap; 376 char buf[255]; 377 int oy, ox; 378 379 va_start(ap, fmt); 380 381 if (wnd) { 382 getyx(stdscr, oy, ox); 383 (void) vsnprintf(buf, sizeof(buf), fmt, ap); 384 clrtoeol(); 385 standout(); 386 mvaddstr(CMDLINE, 0, buf); 387 standend(); 388 move(oy, ox); 389 refresh(); 390 } else { 391 (void) vfprintf(stderr, fmt, ap); 392 fprintf(stderr, "\n"); 393 } 394 va_end(ap); 395 } 396 397 void 398 clearerror(void) 399 { 400 401 error("%s", ""); 402 } 403 404 void 405 nlisterr(struct nlist name_list[]) 406 { 407 int i, n; 408 409 n = 0; 410 clear(); 411 mvprintw(2, 10, "systat: nlist: can't find following symbols:"); 412 for (i = 0; 413 name_list[i].n_name != NULL && *name_list[i].n_name != '\0'; i++) 414 if (name_list[i].n_value == 0) 415 mvprintw(2 + ++n, 10, "%s", name_list[i].n_name); 416 move(CMDLINE, 0); 417 clrtoeol(); 418 refresh(); 419 endwin(); 420 exit(1); 421 } 422 423 bool 424 toofast(int *failcnt) 425 { 426 static char pigs[] = "pigs"; 427 etime = cur.cp_etime; 428 /* < 1 ticks - sleep for a tick */ 429 /* this is often triggered by repeated SIGWINCH */ 430 if ((etime * hertz) >= 1.0) 431 return false; 432 433 if ((*failcnt)++ <= MAXFAIL) { 434 struct timespec interval = { 0, 1000000000L / hertz }; 435 while (nanosleep(&interval, &interval) == -1) 436 continue; 437 return true; 438 } 439 clear(); 440 mvprintw(2, 10, "The alternate system clock has died!"); 441 mvprintw(3, 10, "Reverting to ``pigs'' display."); 442 move(CMDLINE, 0); 443 refresh(); 444 failcnt = 0; 445 sleep(5); 446 command(pigs); 447 return true; 448 } 449