1 /* $OpenBSD: main.c,v 1.73 2021/01/30 08:44:42 martijn Exp $ */ 2 /* 3 * Copyright (c) 2001, 2007 Can Erkin Acar 4 * Copyright (c) 2001 Daniel Hartmeier 5 * 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 * 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 * 31 */ 32 33 #include <sys/types.h> 34 #include <sys/sysctl.h> 35 36 37 #include <ctype.h> 38 #include <curses.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <limits.h> 43 #include <math.h> 44 #include <netdb.h> 45 #include <signal.h> 46 #include <stdio.h> 47 #include <stdint.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <stdarg.h> 51 #include <unistd.h> 52 #include <utmp.h> 53 54 #include "engine.h" 55 #include "systat.h" 56 57 #define TIMEPOS (80 - 8 - 20 - 1) 58 59 double dellave; 60 61 kvm_t *kd; 62 char *nlistf = NULL; 63 char *memf = NULL; 64 double avenrun[3]; 65 double naptime = 5.0; 66 int verbose = 1; /* to report kvm read errs */ 67 int nflag = 1; 68 int ut, hz, stathz; 69 char hostname[HOST_NAME_MAX+1]; 70 WINDOW *wnd; 71 int CMDLINE; 72 char timebuf[26]; 73 char uloadbuf[TIMEPOS]; 74 75 76 int ucount(void); 77 void usage(void); 78 double strtodnum(const char *, double, double, const char **); 79 80 /* command prompt */ 81 82 void cmd_delay(const char *); 83 void cmd_count(const char *); 84 void cmd_compat(const char *); 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}; 89 90 91 /* display functions */ 92 93 int 94 print_header(void) 95 { 96 time_t now; 97 int start = dispstart + 1, end = dispstart + maxprint; 98 char tmpbuf[TIMEPOS]; 99 char header[MAX_LINE_BUF]; 100 101 if (end > num_disp) 102 end = num_disp; 103 104 tb_start(); 105 106 if (!paused) { 107 char *ctim; 108 109 getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])); 110 111 snprintf(uloadbuf, sizeof(uloadbuf), 112 "%4d users Load %.2f %.2f %.2f", 113 ucount(), avenrun[0], avenrun[1], avenrun[2]); 114 115 time(&now); 116 ctim = ctime(&now); 117 ctim[11+8] = '\0'; 118 strlcpy(timebuf, ctim + 11, sizeof(timebuf)); 119 } 120 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 130 snprintf(header, sizeof(header), "%-*s %19.19s %s", TIMEPOS - 1, 131 tmpbuf, hostname, timebuf); 132 133 if (rawmode) 134 printf("\n\n%s\n", header); 135 else 136 mvprintw(0, 0, "%s", header); 137 138 return (1); 139 } 140 141 /* compatibility functions, rearrange later */ 142 void 143 error(const char *fmt, ...) 144 { 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); 151 152 message_set(buf); 153 } 154 155 void 156 nlisterr(struct nlist namelist[]) 157 { 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(); 171 exit(1); 172 } 173 174 void 175 die(void) 176 { 177 if (!rawmode) 178 endwin(); 179 exit(0); 180 } 181 182 183 int 184 prefix(char *s1, char *s2) 185 { 186 187 while (*s1 == *s2) { 188 if (*s1 == '\0') 189 return (1); 190 s1++, s2++; 191 } 192 return (*s1 == '\0'); 193 } 194 195 /* calculate number of users on the system */ 196 int 197 ucount(void) 198 { 199 int nusers = 0; 200 struct utmp utmp; 201 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++; 208 209 return (nusers); 210 } 211 212 /* main program functions */ 213 214 void 215 usage(void) 216 { 217 extern char *__progname; 218 fprintf(stderr, "usage: %s [-aBbhiNn] [-d count] " 219 "[-s delay] [-w width] [view] [delay]\n", __progname); 220 exit(1); 221 } 222 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 } 234 235 void 236 add_view_tb(field_view *v) 237 { 238 if (curr_view == v) 239 tbprintf("[%s] ", v->name); 240 else 241 tbprintf("%s ", v->name); 242 } 243 244 void 245 show_help(void) 246 { 247 if (rawmode) 248 return; 249 250 tb_start(); 251 foreach_view(add_view_tb); 252 tb_end(); 253 message_set(tmp_buf); 254 } 255 256 void 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 282 cmd_compat(const char *buf) 283 { 284 const char *s; 285 286 if (strcasecmp(buf, "help") == 0) { 287 show_help(); 288 need_update = 1; 289 return; 290 } 291 if (strcasecmp(buf, "quit") == 0 || strcasecmp(buf, "q") == 0) { 292 gotsig_close = 1; 293 return; 294 } 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; 303 cmd_delay(buf + 5); 304 return; 305 } 306 if (strncasecmp(buf, "order", 5) == 0) { 307 show_order(); 308 need_update = 1; 309 return; 310 } 311 if (strncasecmp(buf, "human", 5) == 0) { 312 humanreadable = !humanreadable; 313 return; 314 } 315 316 for (s = buf; *s && strchr("0123456789+-.eE", *s) != NULL; s++) 317 ; 318 if (*s) { 319 if (set_view(buf)) 320 error("Invalid/ambiguous view: %s", buf); 321 } else 322 cmd_delay(buf); 323 } 324 325 void 326 cmd_delay(const char *buf) 327 { 328 double del; 329 const char *errstr; 330 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 { 337 udelay = (useconds_t)(del * 1000000); 338 gotsig_alarm = 1; 339 naptime = del; 340 } 341 } 342 343 void 344 cmd_count(const char *buf) 345 { 346 const char *errstr; 347 348 maxprint = strtonum(buf, 1, lines - HEADER_LINES, &errstr); 349 if (errstr) 350 maxprint = lines - HEADER_LINES; 351 } 352 353 354 int 355 keyboard_callback(int ch) 356 { 357 switch (ch) { 358 case '?': 359 /* FALLTHROUGH */ 360 case 'h': 361 show_help(); 362 need_update = 1; 363 break; 364 case CTRL_G: 365 show_view(); 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); 373 break; 374 case ',': 375 separate_thousands = !separate_thousands; 376 gotsig_alarm = 1; 377 break; 378 case ':': 379 command_set(&cm_compat, NULL); 380 break; 381 default: 382 return 0; 383 }; 384 385 return 1; 386 } 387 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(); 403 initpool(); 404 initmalloc(); 405 initnfs(); 406 initcpu(); 407 inituvm(); 408 } 409 410 void 411 gethz(void) 412 { 413 struct clockinfo cinf; 414 size_t size = sizeof(cinf); 415 int mib[2]; 416 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; 423 } 424 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 467 int 468 main(int argc, char *argv[]) 469 { 470 char errbuf[_POSIX2_LINE_MAX]; 471 const char *errstr; 472 extern char *optarg; 473 extern int optind; 474 double delay = 5, del; 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); 485 if (ut == -1) { 486 warn("No utmp"); 487 } 488 489 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 490 491 gid = getgid(); 492 if (setresgid(gid, gid, gid) == -1) 493 err(1, "setresgid"); 494 495 while ((ch = getopt(argc, argv, "BNabd:hins:w:")) != -1) { 496 switch (ch) { 497 case 'a': 498 maxlines = -1; 499 break; 500 case 'B': 501 averageonly = 1; 502 if (countmax < 2) 503 countmax = 2; 504 /* FALLTHROUGH */ 505 case 'b': 506 rawmode = 1; 507 interactive = 0; 508 break; 509 case 'd': 510 countmax = strtonum(optarg, 1, INT_MAX, &errstr); 511 if (errstr) 512 errx(1, "-d %s: %s", optarg, errstr); 513 break; 514 case 'h': 515 humanreadable = 1; 516 break; 517 case 'i': 518 interactive = 1; 519 break; 520 case 'N': 521 nflag = 0; 522 break; 523 case 'n': 524 /* this is a noop, -n is the default */ 525 nflag = 1; 526 break; 527 case 's': 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); 533 break; 534 case 'w': 535 rawwidth = strtonum(optarg, 1, MAX_LINE_BUF-1, &errstr); 536 if (errstr) 537 errx(1, "-w %s: %s", optarg, errstr); 538 break; 539 default: 540 usage(); 541 /* NOTREACHED */ 542 } 543 } 544 545 if (kd == NULL) 546 warnx("kvm_openfiles: %s", errbuf); 547 548 argc -= optind; 549 argv += optind; 550 551 if (argc == 1) { 552 del = strtodnum(argv[0], 0, UINT32_MAX / 1000000, &errstr); 553 if (errstr != NULL) 554 viewstr = argv[0]; 555 else 556 delay = del; 557 } else if (argc == 2) { 558 viewstr = argv[0]; 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); 562 } 563 564 udelay = (useconds_t)(delay * 1000000.0); 565 if (udelay < 1) 566 udelay = 1; 567 568 naptime = (double)udelay / 1000000.0; 569 570 gethostname(hostname, sizeof (hostname)); 571 gethz(); 572 573 initialize(); 574 575 set_order(NULL); 576 if (viewstr && set_view(viewstr)) { 577 fprintf(stderr, "Unknown/ambiguous view name: %s\n", viewstr); 578 return 1; 579 } 580 581 if (check_termcap()) { 582 rawmode = 1; 583 interactive = 0; 584 } 585 586 setup_term(maxlines); 587 588 if (unveil("/", "r") == -1) 589 err(1, "unveil"); 590 if (unveil(NULL, NULL) == -1) 591 err(1, "unveil"); 592 593 if (rawmode && countmax == 0) 594 countmax = 1; 595 596 gotsig_alarm = 1; 597 598 engine_loop(countmax); 599 600 return 0; 601 } 602