1/* $OpenBSD: main.c,v 1.77 2022/12/04 18:01:57 cheloha 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 59double dellave; 60 61kvm_t *kd; 62char *nlistf = NULL; 63char *memf = NULL; 64double avenrun[3]; 65double naptime = 5.0; 66int verbose = 1; /* to report kvm read errs */ 67int nflag = 1; 68int ut, hz; 69char hostname[HOST_NAME_MAX+1]; 70WINDOW *wnd; 71int CMDLINE; 72char timebuf[26]; 73char uloadbuf[TIMEPOS]; 74 75 76int ucount(void); 77void usage(void); 78double strtodnum(const char *, double, double, const char **); 79 80/* command prompt */ 81 82void cmd_delay(const char *); 83void cmd_count(const char *); 84void cmd_compat(const char *); 85 86struct command cm_compat = {"Command", cmd_compat}; 87struct command cm_delay = {"Seconds to delay", cmd_delay}; 88struct command cm_count = {"Number of lines to display", cmd_count}; 89 90 91/* display functions */ 92 93int 94print_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 */ 142void 143error(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 155void 156nlisterr(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 174void 175die(void) 176{ 177 if (!rawmode) 178 endwin(); 179 exit(0); 180} 181 182 183int 184prefix(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 */ 196int 197ucount(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 214void 215usage(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 223void 224show_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 235void 236add_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 244void 245show_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 256void 257add_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 267void 268show_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 281void 282cmd_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 323void 324cmd_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 341void 342cmd_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 352int 353keyboard_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 384void 385initialize(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 406void 407gethz(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 hz = cinf.hz; 418} 419 420#define INVALID 1 421#define TOOSMALL 2 422#define TOOLARGE 3 423 424double 425strtodnum(const char *nptr, double minval, double maxval, const char **errstrp) 426{ 427 double d = 0; 428 int error = 0; 429 char *ep; 430 struct errval { 431 const char *errstr; 432 int err; 433 } ev[4] = { 434 { NULL, 0 }, 435 { "invalid", EINVAL }, 436 { "too small", ERANGE }, 437 { "too large", ERANGE }, 438 }; 439 440 ev[0].err = errno; 441 errno = 0; 442 if (minval > maxval) { 443 error = INVALID; 444 } else { 445 d = strtod(nptr, &ep); 446 if (nptr == ep || *ep != '\0') 447 error = INVALID; 448 else if ((d == -HUGE_VAL && errno == ERANGE) || d < minval) 449 error = TOOSMALL; 450 else if ((d == HUGE_VAL && errno == ERANGE) || d > maxval) 451 error = TOOLARGE; 452 } 453 if (errstrp != NULL) 454 *errstrp = ev[error].errstr; 455 errno = ev[error].err; 456 if (error) 457 d = 0; 458 459 return (d); 460} 461 462int 463main(int argc, char *argv[]) 464{ 465 char errbuf[_POSIX2_LINE_MAX]; 466 const char *errstr; 467 extern char *optarg; 468 extern int optind; 469 double delay = 5, del; 470 471 char *viewstr = NULL; 472 473 gid_t gid; 474 int countmax = 0; 475 int maxlines = 0; 476 477 int ch; 478 479 ut = open(_PATH_UTMP, O_RDONLY); 480 if (ut == -1) { 481 warn("No utmp"); 482 } 483 484 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 485 486 gid = getgid(); 487 if (setresgid(gid, gid, gid) == -1) 488 err(1, "setresgid"); 489 490 while ((ch = getopt(argc, argv, "BNabd:hins:w:")) != -1) { 491 switch (ch) { 492 case 'a': 493 maxlines = -1; 494 break; 495 case 'B': 496 averageonly = 1; 497 if (countmax < 2) 498 countmax = 2; 499 /* FALLTHROUGH */ 500 case 'b': 501 rawmode = 1; 502 interactive = 0; 503 break; 504 case 'd': 505 countmax = strtonum(optarg, 1, INT_MAX, &errstr); 506 if (errstr) 507 errx(1, "-d %s: %s", optarg, errstr); 508 break; 509 case 'h': 510 humanreadable = 1; 511 break; 512 case 'i': 513 interactive = 1; 514 break; 515 case 'N': 516 nflag = 0; 517 break; 518 case 'n': 519 /* this is a noop, -n is the default */ 520 nflag = 1; 521 break; 522 case 's': 523 delay = strtodnum(optarg, 0, UINT32_MAX / 1000000, 524 &errstr); 525 if (errstr != NULL) 526 errx(1, "-s \"%s\": delay value is %s", optarg, 527 errstr); 528 break; 529 case 'w': 530 rawwidth = strtonum(optarg, 1, MAX_LINE_BUF-1, &errstr); 531 if (errstr) 532 errx(1, "-w %s: %s", optarg, errstr); 533 break; 534 default: 535 usage(); 536 /* NOTREACHED */ 537 } 538 } 539 540 if (kd == NULL) 541 warnx("kvm_openfiles: %s", errbuf); 542 543 argc -= optind; 544 argv += optind; 545 546 if (argc == 1) { 547 del = strtodnum(argv[0], 0, UINT32_MAX / 1000000, &errstr); 548 if (errstr != NULL) 549 viewstr = argv[0]; 550 else 551 delay = del; 552 } else if (argc == 2) { 553 viewstr = argv[0]; 554 delay = strtodnum(argv[1], 0, UINT32_MAX / 1000000, &errstr); 555 if (errstr != NULL) 556 errx(1, "\"%s\": delay value is %s", argv[1], errstr); 557 } 558 559 refresh_delay(delay); 560 naptime = delay; 561 562 gethostname(hostname, sizeof (hostname)); 563 gethz(); 564 565 initialize(); 566 567 set_order(NULL); 568 if (viewstr && set_view(viewstr)) { 569 fprintf(stderr, "Unknown/ambiguous view name: %s\n", viewstr); 570 return 1; 571 } 572 573 if (check_termcap()) { 574 rawmode = 1; 575 interactive = 0; 576 } 577 578 setup_term(maxlines); 579 580 if (unveil("/", "r") == -1) 581 err(1, "unveil /"); 582 if (unveil(NULL, NULL) == -1) 583 err(1, "unveil"); 584 585 if (rawmode && countmax == 0) 586 countmax = 1; 587 588 gotsig_alarm = 1; 589 590 engine_loop(countmax); 591 592 return 0; 593} 594