1 /* $OpenBSD: main.c,v 1.72 2020/01/12 20:51:08 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 <netdb.h> 44 #include <signal.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <stdarg.h> 49 #include <unistd.h> 50 #include <utmp.h> 51 52 #include "engine.h" 53 #include "systat.h" 54 55 #define TIMEPOS (80 - 8 - 20 - 1) 56 57 double dellave; 58 59 kvm_t *kd; 60 char *nlistf = NULL; 61 char *memf = NULL; 62 double avenrun[3]; 63 double naptime = 5.0; 64 int verbose = 1; /* to report kvm read errs */ 65 int nflag = 1; 66 int ut, hz, stathz; 67 char hostname[HOST_NAME_MAX+1]; 68 WINDOW *wnd; 69 int CMDLINE; 70 char timebuf[26]; 71 char uloadbuf[TIMEPOS]; 72 73 74 int ucount(void); 75 void usage(void); 76 77 /* command prompt */ 78 79 void cmd_delay(const char *); 80 void cmd_count(const char *); 81 void cmd_compat(const char *); 82 83 struct command cm_compat = {"Command", cmd_compat}; 84 struct command cm_delay = {"Seconds to delay", cmd_delay}; 85 struct command cm_count = {"Number of lines to display", cmd_count}; 86 87 88 /* display functions */ 89 90 int 91 print_header(void) 92 { 93 time_t now; 94 int start = dispstart + 1, end = dispstart + maxprint; 95 char tmpbuf[TIMEPOS]; 96 char header[MAX_LINE_BUF]; 97 98 if (end > num_disp) 99 end = num_disp; 100 101 tb_start(); 102 103 if (!paused) { 104 char *ctim; 105 106 getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])); 107 108 snprintf(uloadbuf, sizeof(uloadbuf), 109 "%4d users Load %.2f %.2f %.2f", 110 ucount(), avenrun[0], avenrun[1], avenrun[2]); 111 112 time(&now); 113 ctim = ctime(&now); 114 ctim[11+8] = '\0'; 115 strlcpy(timebuf, ctim + 11, sizeof(timebuf)); 116 } 117 118 if (num_disp && (start > 1 || end != num_disp)) 119 snprintf(tmpbuf, sizeof(tmpbuf), 120 "%s (%u-%u of %u) %s", uloadbuf, start, end, num_disp, 121 paused ? "PAUSED" : ""); 122 else 123 snprintf(tmpbuf, sizeof(tmpbuf), 124 "%s %s", uloadbuf, 125 paused ? "PAUSED" : ""); 126 127 snprintf(header, sizeof(header), "%-*s %19.19s %s", TIMEPOS - 1, 128 tmpbuf, hostname, timebuf); 129 130 if (rawmode) 131 printf("\n\n%s\n", header); 132 else 133 mvprintw(0, 0, "%s", header); 134 135 return (1); 136 } 137 138 /* compatibility functions, rearrange later */ 139 void 140 error(const char *fmt, ...) 141 { 142 va_list ap; 143 char buf[MAX_LINE_BUF]; 144 145 va_start(ap, fmt); 146 vsnprintf(buf, sizeof buf, fmt, ap); 147 va_end(ap); 148 149 message_set(buf); 150 } 151 152 void 153 nlisterr(struct nlist namelist[]) 154 { 155 int i, n; 156 157 n = 0; 158 clear(); 159 mvprintw(2, 10, "systat: nlist: can't find following symbols:"); 160 for (i = 0; 161 namelist[i].n_name != NULL && *namelist[i].n_name != '\0'; i++) 162 if (namelist[i].n_value == 0) 163 mvprintw(2 + ++n, 10, "%s", namelist[i].n_name); 164 move(CMDLINE, 0); 165 clrtoeol(); 166 refresh(); 167 endwin(); 168 exit(1); 169 } 170 171 void 172 die(void) 173 { 174 if (!rawmode) 175 endwin(); 176 exit(0); 177 } 178 179 180 int 181 prefix(char *s1, char *s2) 182 { 183 184 while (*s1 == *s2) { 185 if (*s1 == '\0') 186 return (1); 187 s1++, s2++; 188 } 189 return (*s1 == '\0'); 190 } 191 192 /* calculate number of users on the system */ 193 int 194 ucount(void) 195 { 196 int nusers = 0; 197 struct utmp utmp; 198 199 if (ut < 0) 200 return (0); 201 lseek(ut, (off_t)0, SEEK_SET); 202 while (read(ut, &utmp, sizeof(utmp))) 203 if (utmp.ut_name[0] != '\0') 204 nusers++; 205 206 return (nusers); 207 } 208 209 /* main program functions */ 210 211 void 212 usage(void) 213 { 214 extern char *__progname; 215 fprintf(stderr, "usage: %s [-aBbhiNn] [-d count] " 216 "[-s delay] [-w width] [view] [delay]\n", __progname); 217 exit(1); 218 } 219 220 void 221 show_view(void) 222 { 223 if (rawmode) 224 return; 225 226 tb_start(); 227 tbprintf("%s %g", curr_view->name, naptime); 228 tb_end(); 229 message_set(tmp_buf); 230 } 231 232 void 233 add_view_tb(field_view *v) 234 { 235 if (curr_view == v) 236 tbprintf("[%s] ", v->name); 237 else 238 tbprintf("%s ", v->name); 239 } 240 241 void 242 show_help(void) 243 { 244 if (rawmode) 245 return; 246 247 tb_start(); 248 foreach_view(add_view_tb); 249 tb_end(); 250 message_set(tmp_buf); 251 } 252 253 void 254 add_order_tb(order_type *o) 255 { 256 if (curr_view->mgr->order_curr == o) 257 tbprintf("[%s%s(%c)] ", o->name, 258 o->func != NULL && sortdir == -1 ? "^" : "", 259 (char) o->hotkey); 260 else 261 tbprintf("%s(%c) ", o->name, (char) o->hotkey); 262 } 263 264 void 265 show_order(void) 266 { 267 if (rawmode) 268 return; 269 270 tb_start(); 271 if (foreach_order(add_order_tb) == -1) { 272 tbprintf("No orders available"); 273 } 274 tb_end(); 275 message_set(tmp_buf); 276 } 277 278 void 279 cmd_compat(const char *buf) 280 { 281 const char *s; 282 283 if (strcasecmp(buf, "help") == 0) { 284 show_help(); 285 need_update = 1; 286 return; 287 } 288 if (strcasecmp(buf, "quit") == 0 || strcasecmp(buf, "q") == 0) { 289 gotsig_close = 1; 290 return; 291 } 292 if (strcasecmp(buf, "stop") == 0) { 293 paused = 1; 294 gotsig_alarm = 1; 295 return; 296 } 297 if (strncasecmp(buf, "start", 5) == 0) { 298 paused = 0; 299 gotsig_alarm = 1; 300 cmd_delay(buf + 5); 301 return; 302 } 303 if (strncasecmp(buf, "order", 5) == 0) { 304 show_order(); 305 need_update = 1; 306 return; 307 } 308 if (strncasecmp(buf, "human", 5) == 0) { 309 humanreadable = !humanreadable; 310 return; 311 } 312 313 for (s = buf; *s && strchr("0123456789+-.eE", *s) != NULL; s++) 314 ; 315 if (*s) { 316 if (set_view(buf)) 317 error("Invalid/ambiguous view: %s", buf); 318 } else 319 cmd_delay(buf); 320 } 321 322 void 323 cmd_delay(const char *buf) 324 { 325 double del; 326 del = atof(buf); 327 328 if (del > 0) { 329 udelay = (useconds_t)(del * 1000000); 330 gotsig_alarm = 1; 331 naptime = del; 332 } 333 } 334 335 void 336 cmd_count(const char *buf) 337 { 338 const char *errstr; 339 340 maxprint = strtonum(buf, 1, lines - HEADER_LINES, &errstr); 341 if (errstr) 342 maxprint = lines - HEADER_LINES; 343 } 344 345 346 int 347 keyboard_callback(int ch) 348 { 349 switch (ch) { 350 case '?': 351 /* FALLTHROUGH */ 352 case 'h': 353 show_help(); 354 need_update = 1; 355 break; 356 case CTRL_G: 357 show_view(); 358 need_update = 1; 359 break; 360 case 'l': 361 command_set(&cm_count, NULL); 362 break; 363 case 's': 364 command_set(&cm_delay, NULL); 365 break; 366 case ',': 367 separate_thousands = !separate_thousands; 368 gotsig_alarm = 1; 369 break; 370 case ':': 371 command_set(&cm_compat, NULL); 372 break; 373 default: 374 return 0; 375 }; 376 377 return 1; 378 } 379 380 void 381 initialize(void) 382 { 383 engine_initialize(); 384 385 initvmstat(); 386 initpigs(); 387 initifstat(); 388 initiostat(); 389 initsensors(); 390 initmembufs(); 391 initnetstat(); 392 initswap(); 393 initpftop(); 394 initpf(); 395 initpool(); 396 initmalloc(); 397 initnfs(); 398 initcpu(); 399 inituvm(); 400 } 401 402 void 403 gethz(void) 404 { 405 struct clockinfo cinf; 406 size_t size = sizeof(cinf); 407 int mib[2]; 408 409 mib[0] = CTL_KERN; 410 mib[1] = KERN_CLOCKRATE; 411 if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1) 412 return; 413 stathz = cinf.stathz; 414 hz = cinf.hz; 415 } 416 417 int 418 main(int argc, char *argv[]) 419 { 420 char errbuf[_POSIX2_LINE_MAX]; 421 const char *errstr; 422 extern char *optarg; 423 extern int optind; 424 double delay = 5; 425 426 char *viewstr = NULL; 427 428 gid_t gid; 429 int countmax = 0; 430 int maxlines = 0; 431 432 int ch; 433 434 ut = open(_PATH_UTMP, O_RDONLY); 435 if (ut == -1) { 436 warn("No utmp"); 437 } 438 439 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 440 441 gid = getgid(); 442 if (setresgid(gid, gid, gid) == -1) 443 err(1, "setresgid"); 444 445 while ((ch = getopt(argc, argv, "BNabd:hins:w:")) != -1) { 446 switch (ch) { 447 case 'a': 448 maxlines = -1; 449 break; 450 case 'B': 451 averageonly = 1; 452 if (countmax < 2) 453 countmax = 2; 454 /* FALLTHROUGH */ 455 case 'b': 456 rawmode = 1; 457 interactive = 0; 458 break; 459 case 'd': 460 countmax = strtonum(optarg, 1, INT_MAX, &errstr); 461 if (errstr) 462 errx(1, "-d %s: %s", optarg, errstr); 463 break; 464 case 'h': 465 humanreadable = 1; 466 break; 467 case 'i': 468 interactive = 1; 469 break; 470 case 'N': 471 nflag = 0; 472 break; 473 case 'n': 474 /* this is a noop, -n is the default */ 475 nflag = 1; 476 break; 477 case 's': 478 delay = atof(optarg); 479 if (delay <= 0) 480 delay = 5; 481 break; 482 case 'w': 483 rawwidth = strtonum(optarg, 1, MAX_LINE_BUF-1, &errstr); 484 if (errstr) 485 errx(1, "-w %s: %s", optarg, errstr); 486 break; 487 default: 488 usage(); 489 /* NOTREACHED */ 490 } 491 } 492 493 if (kd == NULL) 494 warnx("kvm_openfiles: %s", errbuf); 495 496 argc -= optind; 497 argv += optind; 498 499 if (argc == 1) { 500 double del = atof(argv[0]); 501 if (del == 0) 502 viewstr = argv[0]; 503 else 504 delay = del; 505 } else if (argc == 2) { 506 viewstr = argv[0]; 507 delay = atof(argv[1]); 508 if (delay <= 0) 509 delay = 5; 510 } 511 512 udelay = (useconds_t)(delay * 1000000.0); 513 if (udelay < 1) 514 udelay = 1; 515 516 naptime = (double)udelay / 1000000.0; 517 518 gethostname(hostname, sizeof (hostname)); 519 gethz(); 520 521 initialize(); 522 523 set_order(NULL); 524 if (viewstr && set_view(viewstr)) { 525 fprintf(stderr, "Unknown/ambiguous view name: %s\n", viewstr); 526 return 1; 527 } 528 529 if (check_termcap()) { 530 rawmode = 1; 531 interactive = 0; 532 } 533 534 setup_term(maxlines); 535 536 if (unveil("/", "r") == -1) 537 err(1, "unveil"); 538 if (unveil(NULL, NULL) == -1) 539 err(1, "unveil"); 540 541 if (rawmode && countmax == 0) 542 countmax = 1; 543 544 gotsig_alarm = 1; 545 546 engine_loop(countmax); 547 548 return 0; 549 } 550