1 /* 2 * Copyright (c) 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #ifndef lint 31 static const char copyright[] = 32 "@(#) Copyright (c) 1993\n\ 33 The Regents of the University of California. All rights reserved.\n"; 34 #endif /* not lint */ 35 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)from: sysctl.c 8.1 (Berkeley) 6/6/93"; 39 #endif 40 static const char rcsid[] = 41 "$FreeBSD$"; 42 #endif /* not lint */ 43 44 #include <sys/param.h> 45 #include <sys/time.h> 46 #include <sys/resource.h> 47 #include <sys/stat.h> 48 #include <sys/sysctl.h> 49 #include <sys/vmmeter.h> 50 51 #include <ctype.h> 52 #include <err.h> 53 #include <errno.h> 54 #include <inttypes.h> 55 #include <locale.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 61 static int aflag, bflag, dflag, eflag, hflag, iflag; 62 static int Nflag, nflag, oflag, qflag, xflag, warncount; 63 64 static int oidfmt(int *, int, char *, u_int *); 65 static void parse(char *); 66 static int show_var(int *, int); 67 static int sysctl_all(int *oid, int len); 68 static int name2oid(char *, int *); 69 70 static void set_T_dev_t(char *, void **, size_t *); 71 static int set_IK(const char *, int *); 72 73 static void 74 usage(void) 75 { 76 77 (void)fprintf(stderr, "%s\n%s\n", 78 "usage: sysctl [-bdehiNnoqx] name[=value] ...", 79 " sysctl [-bdehNnoqx] -a"); 80 exit(1); 81 } 82 83 int 84 main(int argc, char **argv) 85 { 86 int ch; 87 88 setlocale(LC_NUMERIC, ""); 89 setbuf(stdout,0); 90 setbuf(stderr,0); 91 92 while ((ch = getopt(argc, argv, "AabdehiNnoqwxX")) != -1) { 93 switch (ch) { 94 case 'A': 95 /* compatibility */ 96 aflag = oflag = 1; 97 break; 98 case 'a': 99 aflag = 1; 100 break; 101 case 'b': 102 bflag = 1; 103 break; 104 case 'd': 105 dflag = 1; 106 break; 107 case 'e': 108 eflag = 1; 109 break; 110 case 'h': 111 hflag = 1; 112 break; 113 case 'i': 114 iflag = 1; 115 break; 116 case 'N': 117 Nflag = 1; 118 break; 119 case 'n': 120 nflag = 1; 121 break; 122 case 'o': 123 oflag = 1; 124 break; 125 case 'q': 126 qflag = 1; 127 break; 128 case 'w': 129 /* compatibility */ 130 /* ignored */ 131 break; 132 case 'X': 133 /* compatibility */ 134 aflag = xflag = 1; 135 break; 136 case 'x': 137 xflag = 1; 138 break; 139 default: 140 usage(); 141 } 142 } 143 argc -= optind; 144 argv += optind; 145 146 if (Nflag && nflag) 147 usage(); 148 if (aflag && argc == 0) 149 exit(sysctl_all(0, 0)); 150 if (argc == 0) 151 usage(); 152 153 warncount = 0; 154 while (argc-- > 0) 155 parse(*argv++); 156 exit(warncount); 157 } 158 159 /* 160 * Parse a name into a MIB entry. 161 * Lookup and print out the MIB entry if it exists. 162 * Set a new value if requested. 163 */ 164 static void 165 parse(char *string) 166 { 167 int len, i, j; 168 void *newval = 0; 169 int intval; 170 unsigned int uintval; 171 long longval; 172 unsigned long ulongval; 173 size_t newsize = 0; 174 quad_t quadval; 175 int mib[CTL_MAXNAME]; 176 char *cp, *bufp, buf[BUFSIZ], *endptr, fmt[BUFSIZ]; 177 u_int kind; 178 179 bufp = buf; 180 if (snprintf(buf, BUFSIZ, "%s", string) >= BUFSIZ) 181 errx(1, "oid too long: '%s'", string); 182 if ((cp = strchr(string, '=')) != NULL) { 183 *strchr(buf, '=') = '\0'; 184 *cp++ = '\0'; 185 while (isspace(*cp)) 186 cp++; 187 newval = cp; 188 newsize = strlen(cp); 189 } 190 len = name2oid(bufp, mib); 191 192 if (len < 0) { 193 if (iflag) 194 return; 195 if (qflag) 196 exit(1); 197 else 198 errx(1, "unknown oid '%s'", bufp); 199 } 200 201 if (oidfmt(mib, len, fmt, &kind)) 202 err(1, "couldn't find format of oid '%s'", bufp); 203 204 if (newval == NULL) { 205 if ((kind & CTLTYPE) == CTLTYPE_NODE) { 206 if (dflag) { 207 i = show_var(mib, len); 208 if (!i && !bflag) 209 putchar('\n'); 210 } 211 sysctl_all(mib, len); 212 } else { 213 i = show_var(mib, len); 214 if (!i && !bflag) 215 putchar('\n'); 216 } 217 } else { 218 if ((kind & CTLTYPE) == CTLTYPE_NODE) 219 errx(1, "oid '%s' isn't a leaf node", bufp); 220 221 if (!(kind & CTLFLAG_WR)) { 222 if (kind & CTLFLAG_TUN) { 223 warnx("oid '%s' is a read only tunable", bufp); 224 errx(1, "Tunable values are set in /boot/loader.conf"); 225 } else { 226 errx(1, "oid '%s' is read only", bufp); 227 } 228 } 229 230 if ((kind & CTLTYPE) == CTLTYPE_INT || 231 (kind & CTLTYPE) == CTLTYPE_UINT || 232 (kind & CTLTYPE) == CTLTYPE_LONG || 233 (kind & CTLTYPE) == CTLTYPE_ULONG || 234 (kind & CTLTYPE) == CTLTYPE_QUAD) { 235 if (strlen(newval) == 0) 236 errx(1, "empty numeric value"); 237 } 238 239 switch (kind & CTLTYPE) { 240 case CTLTYPE_INT: 241 if (strcmp(fmt, "IK") == 0) { 242 if (!set_IK(newval, &intval)) 243 errx(1, "invalid value '%s'", 244 (char *)newval); 245 } else { 246 intval = (int)strtol(newval, &endptr, 247 0); 248 if (endptr == newval || *endptr != '\0') 249 errx(1, "invalid integer '%s'", 250 (char *)newval); 251 } 252 newval = &intval; 253 newsize = sizeof(intval); 254 break; 255 case CTLTYPE_UINT: 256 uintval = (int) strtoul(newval, &endptr, 0); 257 if (endptr == newval || *endptr != '\0') 258 errx(1, "invalid unsigned integer '%s'", 259 (char *)newval); 260 newval = &uintval; 261 newsize = sizeof(uintval); 262 break; 263 case CTLTYPE_LONG: 264 longval = strtol(newval, &endptr, 0); 265 if (endptr == newval || *endptr != '\0') 266 errx(1, "invalid long integer '%s'", 267 (char *)newval); 268 newval = &longval; 269 newsize = sizeof(longval); 270 break; 271 case CTLTYPE_ULONG: 272 ulongval = strtoul(newval, &endptr, 0); 273 if (endptr == newval || *endptr != '\0') 274 errx(1, "invalid unsigned long integer" 275 " '%s'", (char *)newval); 276 newval = &ulongval; 277 newsize = sizeof(ulongval); 278 break; 279 case CTLTYPE_STRING: 280 break; 281 case CTLTYPE_QUAD: 282 quadval = strtoq(newval, &endptr, 0); 283 if (endptr == newval || *endptr != '\0') 284 errx(1, "invalid quad integer" 285 " '%s'", (char *)newval); 286 newval = &quadval; 287 newsize = sizeof(quadval); 288 break; 289 case CTLTYPE_OPAQUE: 290 if (strcmp(fmt, "T,dev_t") == 0) { 291 set_T_dev_t (newval, &newval, &newsize); 292 break; 293 } 294 /* FALLTHROUGH */ 295 default: 296 errx(1, "oid '%s' is type %d," 297 " cannot set that", bufp, 298 kind & CTLTYPE); 299 } 300 301 i = show_var(mib, len); 302 if (sysctl(mib, len, 0, 0, newval, newsize) == -1) { 303 if (!i && !bflag) 304 putchar('\n'); 305 switch (errno) { 306 case EOPNOTSUPP: 307 errx(1, "%s: value is not available", 308 string); 309 case ENOTDIR: 310 errx(1, "%s: specification is incomplete", 311 string); 312 case ENOMEM: 313 errx(1, "%s: type is unknown to this program", 314 string); 315 default: 316 warn("%s", string); 317 warncount++; 318 return; 319 } 320 } 321 if (!bflag) 322 printf(" -> "); 323 i = nflag; 324 nflag = 1; 325 j = show_var(mib, len); 326 if (!j && !bflag) 327 putchar('\n'); 328 nflag = i; 329 } 330 } 331 332 /* These functions will dump out various interesting structures. */ 333 334 static int 335 S_clockinfo(int l2, void *p) 336 { 337 struct clockinfo *ci = (struct clockinfo*)p; 338 339 if (l2 != sizeof(*ci)) { 340 warnx("S_clockinfo %d != %zu", l2, sizeof(*ci)); 341 return (1); 342 } 343 printf(hflag ? "{ hz = %'d, tick = %'d, profhz = %'d, stathz = %'d }" : 344 "{ hz = %d, tick = %d, profhz = %d, stathz = %d }", 345 ci->hz, ci->tick, ci->profhz, ci->stathz); 346 return (0); 347 } 348 349 static int 350 S_loadavg(int l2, void *p) 351 { 352 struct loadavg *tv = (struct loadavg*)p; 353 354 if (l2 != sizeof(*tv)) { 355 warnx("S_loadavg %d != %zu", l2, sizeof(*tv)); 356 return (1); 357 } 358 printf(hflag ? "{ %'.2f %'.2f %'.2f }" : "{ %.2f %.2f %.2f }", 359 (double)tv->ldavg[0]/(double)tv->fscale, 360 (double)tv->ldavg[1]/(double)tv->fscale, 361 (double)tv->ldavg[2]/(double)tv->fscale); 362 return (0); 363 } 364 365 static int 366 S_timeval(int l2, void *p) 367 { 368 struct timeval *tv = (struct timeval*)p; 369 time_t tv_sec; 370 char *p1, *p2; 371 372 if (l2 != sizeof(*tv)) { 373 warnx("S_timeval %d != %zu", l2, sizeof(*tv)); 374 return (1); 375 } 376 printf(hflag ? "{ sec = %'jd, usec = %'ld } " : 377 "{ sec = %jd, usec = %ld } ", 378 (intmax_t)tv->tv_sec, tv->tv_usec); 379 tv_sec = tv->tv_sec; 380 p1 = strdup(ctime(&tv_sec)); 381 for (p2=p1; *p2 ; p2++) 382 if (*p2 == '\n') 383 *p2 = '\0'; 384 fputs(p1, stdout); 385 free(p1); 386 return (0); 387 } 388 389 static int 390 S_vmtotal(int l2, void *p) 391 { 392 struct vmtotal *v = (struct vmtotal *)p; 393 int pageKilo = getpagesize() / 1024; 394 395 if (l2 != sizeof(*v)) { 396 warnx("S_vmtotal %d != %zu", l2, sizeof(*v)); 397 return (1); 398 } 399 400 printf( 401 "\nSystem wide totals computed every five seconds:" 402 " (values in kilobytes)\n"); 403 printf("===============================================\n"); 404 printf( 405 "Processes:\t\t(RUNQ: %hd Disk Wait: %hd Page Wait: " 406 "%hd Sleep: %hd)\n", 407 v->t_rq, v->t_dw, v->t_pw, v->t_sl); 408 printf( 409 "Virtual Memory:\t\t(Total: %dK, Active %dK)\n", 410 v->t_vm * pageKilo, v->t_avm * pageKilo); 411 printf("Real Memory:\t\t(Total: %dK Active %dK)\n", 412 v->t_rm * pageKilo, v->t_arm * pageKilo); 413 printf("Shared Virtual Memory:\t(Total: %dK Active: %dK)\n", 414 v->t_vmshr * pageKilo, v->t_avmshr * pageKilo); 415 printf("Shared Real Memory:\t(Total: %dK Active: %dK)\n", 416 v->t_rmshr * pageKilo, v->t_armshr * pageKilo); 417 printf("Free Memory Pages:\t%dK\n", v->t_free * pageKilo); 418 419 return (0); 420 } 421 422 static int 423 T_dev_t(int l2, void *p) 424 { 425 dev_t *d = (dev_t *)p; 426 427 if (l2 != sizeof(*d)) { 428 warnx("T_dev_T %d != %zu", l2, sizeof(*d)); 429 return (1); 430 } 431 printf("%s", devname(*d, S_IFCHR)); 432 return (0); 433 } 434 435 static void 436 set_T_dev_t(char *path, void **val, size_t *size) 437 { 438 static struct stat statb; 439 440 if (strcmp(path, "none") && strcmp(path, "off")) { 441 int rc = stat (path, &statb); 442 if (rc) { 443 err(1, "cannot stat %s", path); 444 } 445 446 if (!S_ISCHR(statb.st_mode)) { 447 errx(1, "must specify a device special file."); 448 } 449 } else { 450 statb.st_rdev = NODEV; 451 } 452 *val = (void *) &statb.st_rdev; 453 *size = sizeof(statb.st_rdev); 454 } 455 456 static int 457 set_IK(const char *str, int *val) 458 { 459 float temp; 460 int len, kelv; 461 const char *p; 462 char *endptr; 463 464 if ((len = strlen(str)) == 0) 465 return (0); 466 p = &str[len - 1]; 467 if (*p == 'C' || *p == 'F') { 468 temp = strtof(str, &endptr); 469 if (endptr == str || endptr != p) 470 return (0); 471 if (*p == 'F') 472 temp = (temp - 32) * 5 / 9; 473 kelv = temp * 10 + 2732; 474 } else { 475 kelv = (int)strtol(str, &endptr, 10); 476 if (endptr == str || *endptr != '\0') 477 return (0); 478 } 479 *val = kelv; 480 return (1); 481 } 482 483 /* 484 * These functions uses a presently undocumented interface to the kernel 485 * to walk the tree and get the type so it can print the value. 486 * This interface is under work and consideration, and should probably 487 * be killed with a big axe by the first person who can find the time. 488 * (be aware though, that the proper interface isn't as obvious as it 489 * may seem, there are various conflicting requirements. 490 */ 491 492 static int 493 name2oid(char *name, int *oidp) 494 { 495 int oid[2]; 496 int i; 497 size_t j; 498 499 oid[0] = 0; 500 oid[1] = 3; 501 502 j = CTL_MAXNAME * sizeof(int); 503 i = sysctl(oid, 2, oidp, &j, name, strlen(name)); 504 if (i < 0) 505 return (i); 506 j /= sizeof(int); 507 return (j); 508 } 509 510 static int 511 oidfmt(int *oid, int len, char *fmt, u_int *kind) 512 { 513 int qoid[CTL_MAXNAME+2]; 514 u_char buf[BUFSIZ]; 515 int i; 516 size_t j; 517 518 qoid[0] = 0; 519 qoid[1] = 4; 520 memcpy(qoid + 2, oid, len * sizeof(int)); 521 522 j = sizeof(buf); 523 i = sysctl(qoid, len + 2, buf, &j, 0, 0); 524 if (i) 525 err(1, "sysctl fmt %d %zu %d", i, j, errno); 526 527 if (kind) 528 *kind = *(u_int *)buf; 529 530 if (fmt) 531 strcpy(fmt, (char *)(buf + sizeof(u_int))); 532 return (0); 533 } 534 535 /* 536 * This formats and outputs the value of one variable 537 * 538 * Returns zero if anything was actually output. 539 * Returns one if didn't know what to do with this. 540 * Return minus one if we had errors. 541 */ 542 543 static int 544 show_var(int *oid, int nlen) 545 { 546 u_char buf[BUFSIZ], *val, *oval, *p; 547 char name[BUFSIZ], *fmt; 548 const char *sep, *sep1; 549 int qoid[CTL_MAXNAME+2]; 550 uintmax_t umv; 551 intmax_t mv; 552 int i, hexlen; 553 size_t intlen; 554 size_t j, len; 555 u_int kind; 556 int (*func)(int, void *); 557 558 /* Silence GCC. */ 559 umv = mv = intlen = 0; 560 561 bzero(buf, BUFSIZ); 562 bzero(name, BUFSIZ); 563 qoid[0] = 0; 564 memcpy(qoid + 2, oid, nlen * sizeof(int)); 565 566 qoid[1] = 1; 567 j = sizeof(name); 568 i = sysctl(qoid, nlen + 2, name, &j, 0, 0); 569 if (i || !j) 570 err(1, "sysctl name %d %zu %d", i, j, errno); 571 572 if (Nflag) { 573 printf("%s", name); 574 return (0); 575 } 576 577 if (eflag) 578 sep = "="; 579 else 580 sep = ": "; 581 582 if (dflag) { /* just print description */ 583 qoid[1] = 5; 584 j = sizeof(buf); 585 i = sysctl(qoid, nlen + 2, buf, &j, 0, 0); 586 if (!nflag) 587 printf("%s%s", name, sep); 588 printf("%s", buf); 589 return (0); 590 } 591 /* find an estimate of how much we need for this var */ 592 j = 0; 593 i = sysctl(oid, nlen, 0, &j, 0, 0); 594 j += j; /* we want to be sure :-) */ 595 596 val = oval = malloc(j + 1); 597 if (val == NULL) { 598 warnx("malloc failed"); 599 return (1); 600 } 601 len = j; 602 i = sysctl(oid, nlen, val, &len, 0, 0); 603 if (i || !len) { 604 free(oval); 605 return (1); 606 } 607 608 if (bflag) { 609 fwrite(val, 1, len, stdout); 610 free(oval); 611 return (0); 612 } 613 val[len] = '\0'; 614 fmt = buf; 615 oidfmt(oid, nlen, fmt, &kind); 616 p = val; 617 switch (*fmt) { 618 case 'A': 619 if (!nflag) 620 printf("%s%s", name, sep); 621 printf("%.*s", (int)len, p); 622 free(oval); 623 return (0); 624 625 case 'I': 626 case 'L': 627 case 'Q': 628 if (!nflag) 629 printf("%s%s", name, sep); 630 switch (*fmt) { 631 case 'I': intlen = sizeof(int); break; 632 case 'L': intlen = sizeof(long); break; 633 case 'Q': intlen = sizeof(quad_t); break; 634 } 635 hexlen = 2 + (intlen * CHAR_BIT + 3) / 4; 636 sep1 = ""; 637 while (len >= intlen) { 638 switch (*fmt) { 639 case 'I': 640 umv = *(u_int *)p; 641 mv = *(int *)p; 642 break; 643 case 'L': 644 umv = *(u_long *)p; 645 mv = *(long *)p; 646 break; 647 case 'Q': 648 umv = *(u_quad_t *)p; 649 mv = *(quad_t *)p; 650 break; 651 } 652 fputs(sep1, stdout); 653 if (fmt[1] == 'U') 654 printf(hflag ? "%'ju" : "%ju", umv); 655 else if (fmt[1] == 'X') 656 printf("%#0*jx", hexlen, umv); 657 else if (fmt[1] == 'K') { 658 if (mv < 0) 659 printf("%jd", mv); 660 else 661 printf("%.1fC", (mv - 2732.0) / 10); 662 } else 663 printf(hflag ? "%'jd" : "%jd", mv); 664 sep1 = " "; 665 len -= intlen; 666 p += intlen; 667 } 668 free(oval); 669 return (0); 670 671 case 'P': 672 if (!nflag) 673 printf("%s%s", name, sep); 674 printf("%p", *(void **)p); 675 free(oval); 676 return (0); 677 678 case 'T': 679 case 'S': 680 i = 0; 681 if (strcmp(fmt, "S,clockinfo") == 0) 682 func = S_clockinfo; 683 else if (strcmp(fmt, "S,timeval") == 0) 684 func = S_timeval; 685 else if (strcmp(fmt, "S,loadavg") == 0) 686 func = S_loadavg; 687 else if (strcmp(fmt, "S,vmtotal") == 0) 688 func = S_vmtotal; 689 else if (strcmp(fmt, "T,dev_t") == 0) 690 func = T_dev_t; 691 else 692 func = NULL; 693 if (func) { 694 if (!nflag) 695 printf("%s%s", name, sep); 696 i = (*func)(len, p); 697 free(oval); 698 return (i); 699 } 700 /* FALLTHROUGH */ 701 default: 702 if (!oflag && !xflag) { 703 free(oval); 704 return (1); 705 } 706 if (!nflag) 707 printf("%s%s", name, sep); 708 printf("Format:%s Length:%zu Dump:0x", fmt, len); 709 while (len-- && (xflag || p < val + 16)) 710 printf("%02x", *p++); 711 if (!xflag && len > 16) 712 printf("..."); 713 free(oval); 714 return (0); 715 } 716 free(oval); 717 return (1); 718 } 719 720 static int 721 sysctl_all(int *oid, int len) 722 { 723 int name1[22], name2[22]; 724 int i, j; 725 size_t l1, l2; 726 727 name1[0] = 0; 728 name1[1] = 2; 729 l1 = 2; 730 if (len) { 731 memcpy(name1+2, oid, len * sizeof(int)); 732 l1 += len; 733 } else { 734 name1[2] = 1; 735 l1++; 736 } 737 for (;;) { 738 l2 = sizeof(name2); 739 j = sysctl(name1, l1, name2, &l2, 0, 0); 740 if (j < 0) { 741 if (errno == ENOENT) 742 return (0); 743 else 744 err(1, "sysctl(getnext) %d %zu", j, l2); 745 } 746 747 l2 /= sizeof(int); 748 749 if (len < 0 || l2 < (unsigned int)len) 750 return (0); 751 752 for (i = 0; i < len; i++) 753 if (name2[i] != oid[i]) 754 return (0); 755 756 i = show_var(name2, l2); 757 if (!i && !bflag) 758 putchar('\n'); 759 760 memcpy(name1+2, name2, l2 * sizeof(int)); 761 l1 = 2 + l2; 762 } 763 } 764