1 /* $OpenBSD: main.c,v 1.76 2021/07/12 15:09:20 beck 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 message_toggle(MESSAGE_HELP); 288 return; 289 } 290 if (strcasecmp(buf, "quit") == 0 || strcasecmp(buf, "q") == 0) { 291 gotsig_close = 1; 292 return; 293 } 294 if (strcasecmp(buf, "stop") == 0) { 295 paused = 1; 296 gotsig_alarm = 1; 297 return; 298 } 299 if (strncasecmp(buf, "start", 5) == 0) { 300 paused = 0; 301 gotsig_alarm = 1; 302 cmd_delay(buf + 5); 303 return; 304 } 305 if (strncasecmp(buf, "order", 5) == 0) { 306 message_toggle(MESSAGE_ORDER); 307 return; 308 } 309 if (strncasecmp(buf, "human", 5) == 0) { 310 humanreadable = !humanreadable; 311 return; 312 } 313 314 for (s = buf; *s && strchr("0123456789+-.eE", *s) != NULL; s++) 315 ; 316 if (*s) { 317 if (set_view(buf)) 318 error("Invalid/ambiguous view: %s", buf); 319 } else 320 cmd_delay(buf); 321 } 322 323 void 324 cmd_delay(const char *buf) 325 { 326 double del; 327 const char *errstr; 328 329 if (buf[0] == '\0') 330 return; 331 del = strtodnum(buf, 0, UINT32_MAX / 1000000, &errstr); 332 if (errstr != NULL) 333 error("s: \"%s\": delay value is %s", buf, errstr); 334 else { 335 refresh_delay(del); 336 gotsig_alarm = 1; 337 naptime = del; 338 } 339 } 340 341 void 342 cmd_count(const char *buf) 343 { 344 const char *errstr; 345 346 maxprint = strtonum(buf, 1, lines - HEADER_LINES, &errstr); 347 if (errstr) 348 maxprint = lines - HEADER_LINES; 349 } 350 351 352 int 353 keyboard_callback(int ch) 354 { 355 switch (ch) { 356 case '?': 357 /* FALLTHROUGH */ 358 case 'h': 359 message_toggle(MESSAGE_HELP); 360 break; 361 case CTRL_G: 362 message_toggle(MESSAGE_VIEW); 363 break; 364 case 'l': 365 command_set(&cm_count, NULL); 366 break; 367 case 's': 368 command_set(&cm_delay, NULL); 369 break; 370 case ',': 371 separate_thousands = !separate_thousands; 372 gotsig_alarm = 1; 373 break; 374 case ':': 375 command_set(&cm_compat, NULL); 376 break; 377 default: 378 return 0; 379 }; 380 381 return 1; 382 } 383 384 void 385 initialize(void) 386 { 387 engine_initialize(); 388 389 initvmstat(); 390 initpigs(); 391 initifstat(); 392 initiostat(); 393 initsensors(); 394 initmembufs(); 395 initnetstat(); 396 initswap(); 397 initpftop(); 398 initpf(); 399 initpool(); 400 initmalloc(); 401 initnfs(); 402 initcpu(); 403 inituvm(); 404 } 405 406 void 407 gethz(void) 408 { 409 struct clockinfo cinf; 410 size_t size = sizeof(cinf); 411 int mib[2]; 412 413 mib[0] = CTL_KERN; 414 mib[1] = KERN_CLOCKRATE; 415 if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1) 416 return; 417 stathz = cinf.stathz; 418 hz = cinf.hz; 419 } 420 421 #define INVALID 1 422 #define TOOSMALL 2 423 #define TOOLARGE 3 424 425 double 426 strtodnum(const char *nptr, double minval, double maxval, const char **errstrp) 427 { 428 double d = 0; 429 int error = 0; 430 char *ep; 431 struct errval { 432 const char *errstr; 433 int err; 434 } ev[4] = { 435 { NULL, 0 }, 436 { "invalid", EINVAL }, 437 { "too small", ERANGE }, 438 { "too large", ERANGE }, 439 }; 440 441 ev[0].err = errno; 442 errno = 0; 443 if (minval > maxval) { 444 error = INVALID; 445 } else { 446 d = strtod(nptr, &ep); 447 if (nptr == ep || *ep != '\0') 448 error = INVALID; 449 else if ((d == -HUGE_VAL && errno == ERANGE) || d < minval) 450 error = TOOSMALL; 451 else if ((d == HUGE_VAL && errno == ERANGE) || d > maxval) 452 error = TOOLARGE; 453 } 454 if (errstrp != NULL) 455 *errstrp = ev[error].errstr; 456 errno = ev[error].err; 457 if (error) 458 d = 0; 459 460 return (d); 461 } 462 463 int 464 main(int argc, char *argv[]) 465 { 466 char errbuf[_POSIX2_LINE_MAX]; 467 const char *errstr; 468 extern char *optarg; 469 extern int optind; 470 double delay = 5, del; 471 472 char *viewstr = NULL; 473 474 gid_t gid; 475 int countmax = 0; 476 int maxlines = 0; 477 478 int ch; 479 480 ut = open(_PATH_UTMP, O_RDONLY); 481 if (ut == -1) { 482 warn("No utmp"); 483 } 484 485 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 486 487 gid = getgid(); 488 if (setresgid(gid, gid, gid) == -1) 489 err(1, "setresgid"); 490 491 while ((ch = getopt(argc, argv, "BNabd:hins:w:")) != -1) { 492 switch (ch) { 493 case 'a': 494 maxlines = -1; 495 break; 496 case 'B': 497 averageonly = 1; 498 if (countmax < 2) 499 countmax = 2; 500 /* FALLTHROUGH */ 501 case 'b': 502 rawmode = 1; 503 interactive = 0; 504 break; 505 case 'd': 506 countmax = strtonum(optarg, 1, INT_MAX, &errstr); 507 if (errstr) 508 errx(1, "-d %s: %s", optarg, errstr); 509 break; 510 case 'h': 511 humanreadable = 1; 512 break; 513 case 'i': 514 interactive = 1; 515 break; 516 case 'N': 517 nflag = 0; 518 break; 519 case 'n': 520 /* this is a noop, -n is the default */ 521 nflag = 1; 522 break; 523 case 's': 524 delay = strtodnum(optarg, 0, UINT32_MAX / 1000000, 525 &errstr); 526 if (errstr != NULL) 527 errx(1, "-s \"%s\": delay value is %s", optarg, 528 errstr); 529 break; 530 case 'w': 531 rawwidth = strtonum(optarg, 1, MAX_LINE_BUF-1, &errstr); 532 if (errstr) 533 errx(1, "-w %s: %s", optarg, errstr); 534 break; 535 default: 536 usage(); 537 /* NOTREACHED */ 538 } 539 } 540 541 if (kd == NULL) 542 warnx("kvm_openfiles: %s", errbuf); 543 544 argc -= optind; 545 argv += optind; 546 547 if (argc == 1) { 548 del = strtodnum(argv[0], 0, UINT32_MAX / 1000000, &errstr); 549 if (errstr != NULL) 550 viewstr = argv[0]; 551 else 552 delay = del; 553 } else if (argc == 2) { 554 viewstr = argv[0]; 555 delay = strtodnum(argv[1], 0, UINT32_MAX / 1000000, &errstr); 556 if (errstr != NULL) 557 errx(1, "\"%s\": delay value is %s", argv[1], errstr); 558 } 559 560 refresh_delay(delay); 561 naptime = delay; 562 563 gethostname(hostname, sizeof (hostname)); 564 gethz(); 565 566 initialize(); 567 568 set_order(NULL); 569 if (viewstr && set_view(viewstr)) { 570 fprintf(stderr, "Unknown/ambiguous view name: %s\n", viewstr); 571 return 1; 572 } 573 574 if (check_termcap()) { 575 rawmode = 1; 576 interactive = 0; 577 } 578 579 setup_term(maxlines); 580 581 if (unveil("/", "r") == -1) 582 err(1, "unveil /"); 583 if (unveil(NULL, NULL) == -1) 584 err(1, "unveil"); 585 586 if (rawmode && countmax == 0) 587 countmax = 1; 588 589 gotsig_alarm = 1; 590 591 engine_loop(countmax); 592 593 return 0; 594 } 595