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