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 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; 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 hz = cinf.hz; 418 } 419 420 #define INVALID 1 421 #define TOOSMALL 2 422 #define TOOLARGE 3 423 424 double 425 strtodnum(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 462 int 463 main(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