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(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 sscanf(newval, "%qd", &quadval); 283 newval = &quadval; 284 newsize = sizeof(quadval); 285 break; 286 case CTLTYPE_OPAQUE: 287 if (strcmp(fmt, "T,dev_t") == 0) { 288 set_T_dev_t (newval, &newval, &newsize); 289 break; 290 } 291 /* FALLTHROUGH */ 292 default: 293 errx(1, "oid '%s' is type %d," 294 " cannot set that", bufp, 295 kind & CTLTYPE); 296 } 297 298 i = show_var(mib, len); 299 if (sysctl(mib, len, 0, 0, newval, newsize) == -1) { 300 if (!i && !bflag) 301 putchar('\n'); 302 switch (errno) { 303 case EOPNOTSUPP: 304 errx(1, "%s: value is not available", 305 string); 306 case ENOTDIR: 307 errx(1, "%s: specification is incomplete", 308 string); 309 case ENOMEM: 310 errx(1, "%s: type is unknown to this program", 311 string); 312 default: 313 warn("%s", string); 314 warncount++; 315 return; 316 } 317 } 318 if (!bflag) 319 printf(" -> "); 320 i = nflag; 321 nflag = 1; 322 j = show_var(mib, len); 323 if (!j && !bflag) 324 putchar('\n'); 325 nflag = i; 326 } 327 } 328 329 /* These functions will dump out various interesting structures. */ 330 331 static int 332 S_clockinfo(int l2, void *p) 333 { 334 struct clockinfo *ci = (struct clockinfo*)p; 335 336 if (l2 != sizeof(*ci)) { 337 warnx("S_clockinfo %d != %d", l2, sizeof(*ci)); 338 return (1); 339 } 340 printf(hflag ? "{ hz = %'d, tick = %'d, profhz = %'d, stathz = %'d }" : 341 "{ hz = %d, tick = %d, profhz = %d, stathz = %d }", 342 ci->hz, ci->tick, ci->profhz, ci->stathz); 343 return (0); 344 } 345 346 static int 347 S_loadavg(int l2, void *p) 348 { 349 struct loadavg *tv = (struct loadavg*)p; 350 351 if (l2 != sizeof(*tv)) { 352 warnx("S_loadavg %d != %d", l2, sizeof(*tv)); 353 return (1); 354 } 355 printf(hflag ? "{ %'.2f %'.2f %'.2f }" : "{ %.2f %.2f %.2f }", 356 (double)tv->ldavg[0]/(double)tv->fscale, 357 (double)tv->ldavg[1]/(double)tv->fscale, 358 (double)tv->ldavg[2]/(double)tv->fscale); 359 return (0); 360 } 361 362 static int 363 S_timeval(int l2, void *p) 364 { 365 struct timeval *tv = (struct timeval*)p; 366 time_t tv_sec; 367 char *p1, *p2; 368 369 if (l2 != sizeof(*tv)) { 370 warnx("S_timeval %d != %d", l2, sizeof(*tv)); 371 return (1); 372 } 373 printf(hflag ? "{ sec = %'jd, usec = %'ld } " : 374 "{ sec = %jd, usec = %ld } ", 375 (intmax_t)tv->tv_sec, tv->tv_usec); 376 tv_sec = tv->tv_sec; 377 p1 = strdup(ctime(&tv_sec)); 378 for (p2=p1; *p2 ; p2++) 379 if (*p2 == '\n') 380 *p2 = '\0'; 381 fputs(p1, stdout); 382 free(p1); 383 return (0); 384 } 385 386 static int 387 S_vmtotal(int l2, void *p) 388 { 389 struct vmtotal *v = (struct vmtotal *)p; 390 int pageKilo = getpagesize() / 1024; 391 392 if (l2 != sizeof(*v)) { 393 warnx("S_vmtotal %d != %d", l2, sizeof(*v)); 394 return (1); 395 } 396 397 printf( 398 "\nSystem wide totals computed every five seconds:" 399 " (values in kilobytes)\n"); 400 printf("===============================================\n"); 401 printf( 402 "Processes:\t\t(RUNQ: %hd Disk Wait: %hd Page Wait: " 403 "%hd Sleep: %hd)\n", 404 v->t_rq, v->t_dw, v->t_pw, v->t_sl); 405 printf( 406 "Virtual Memory:\t\t(Total: %dK, Active %dK)\n", 407 v->t_vm * pageKilo, v->t_avm * pageKilo); 408 printf("Real Memory:\t\t(Total: %dK Active %dK)\n", 409 v->t_rm * pageKilo, v->t_arm * pageKilo); 410 printf("Shared Virtual Memory:\t(Total: %dK Active: %dK)\n", 411 v->t_vmshr * pageKilo, v->t_avmshr * pageKilo); 412 printf("Shared Real Memory:\t(Total: %dK Active: %dK)\n", 413 v->t_rmshr * pageKilo, v->t_armshr * pageKilo); 414 printf("Free Memory Pages:\t%dK\n", v->t_free * pageKilo); 415 416 return (0); 417 } 418 419 static int 420 T_dev_t(int l2, void *p) 421 { 422 dev_t *d = (dev_t *)p; 423 424 if (l2 != sizeof(*d)) { 425 warnx("T_dev_T %d != %d", l2, sizeof(*d)); 426 return (1); 427 } 428 if ((int)(*d) != -1) { 429 if (minor(*d) > 255 || minor(*d) < 0) 430 printf("{ major = %d, minor = 0x%x }", 431 major(*d), minor(*d)); 432 else 433 printf("{ major = %d, minor = %d }", 434 major(*d), minor(*d)); 435 } 436 return (0); 437 } 438 439 static void 440 set_T_dev_t(char *path, void **val, size_t *size) 441 { 442 static struct stat statb; 443 444 if (strcmp(path, "none") && strcmp(path, "off")) { 445 int rc = stat (path, &statb); 446 if (rc) { 447 err(1, "cannot stat %s", path); 448 } 449 450 if (!S_ISCHR(statb.st_mode)) { 451 errx(1, "must specify a device special file."); 452 } 453 } else { 454 statb.st_rdev = NODEV; 455 } 456 *val = (void *) &statb.st_rdev; 457 *size = sizeof(statb.st_rdev); 458 } 459 460 static int 461 set_IK(char *str, int *val) 462 { 463 float temp; 464 int len, kelv; 465 char *p, *endptr; 466 467 if ((len = strlen(str)) == 0) 468 return (0); 469 p = &str[len - 1]; 470 if (*p == 'C' || *p == 'F') { 471 *p = '\0'; 472 temp = strtof(str, &endptr); 473 if (endptr == str || *endptr != '\0') 474 return (0); 475 if (*p == 'F') 476 temp = (temp - 32) * 5 / 9; 477 kelv = temp * 10 + 2732; 478 } else { 479 kelv = (int)strtol(str, &endptr, 10); 480 if (endptr == str || *endptr != '\0') 481 return (0); 482 } 483 *val = kelv; 484 return (1); 485 } 486 487 /* 488 * These functions uses a presently undocumented interface to the kernel 489 * to walk the tree and get the type so it can print the value. 490 * This interface is under work and consideration, and should probably 491 * be killed with a big axe by the first person who can find the time. 492 * (be aware though, that the proper interface isn't as obvious as it 493 * may seem, there are various conflicting requirements. 494 */ 495 496 static int 497 name2oid(char *name, int *oidp) 498 { 499 int oid[2]; 500 int i; 501 size_t j; 502 503 oid[0] = 0; 504 oid[1] = 3; 505 506 j = CTL_MAXNAME * sizeof(int); 507 i = sysctl(oid, 2, oidp, &j, name, strlen(name)); 508 if (i < 0) 509 return (i); 510 j /= sizeof(int); 511 return (j); 512 } 513 514 static int 515 oidfmt(int *oid, int len, char *fmt, u_int *kind) 516 { 517 int qoid[CTL_MAXNAME+2]; 518 u_char buf[BUFSIZ]; 519 int i; 520 size_t j; 521 522 qoid[0] = 0; 523 qoid[1] = 4; 524 memcpy(qoid + 2, oid, len * sizeof(int)); 525 526 j = sizeof(buf); 527 i = sysctl(qoid, len + 2, buf, &j, 0, 0); 528 if (i) 529 err(1, "sysctl fmt %d %d %d", i, j, errno); 530 531 if (kind) 532 *kind = *(u_int *)buf; 533 534 if (fmt) 535 strcpy(fmt, (char *)(buf + sizeof(u_int))); 536 return (0); 537 } 538 539 /* 540 * This formats and outputs the value of one variable 541 * 542 * Returns zero if anything was actually output. 543 * Returns one if didn't know what to do with this. 544 * Return minus one if we had errors. 545 */ 546 547 static int 548 show_var(int *oid, int nlen) 549 { 550 u_char buf[BUFSIZ], *val, *oval, *p; 551 char name[BUFSIZ], *fmt; 552 const char *sep, *sep1; 553 int qoid[CTL_MAXNAME+2]; 554 uintmax_t umv; 555 intmax_t mv; 556 int i, hexlen; 557 size_t intlen; 558 size_t j, len; 559 u_int kind; 560 int (*func)(int, void *); 561 562 bzero(buf, BUFSIZ); 563 bzero(name, BUFSIZ); 564 qoid[0] = 0; 565 memcpy(qoid + 2, oid, nlen * sizeof(int)); 566 567 qoid[1] = 1; 568 j = sizeof(name); 569 i = sysctl(qoid, nlen + 2, name, &j, 0, 0); 570 if (i || !j) 571 err(1, "sysctl name %d %d %d", i, j, errno); 572 573 if (Nflag) { 574 printf("%s", name); 575 return (0); 576 } 577 578 if (eflag) 579 sep = "="; 580 else 581 sep = ": "; 582 583 if (dflag) { /* just print description */ 584 qoid[1] = 5; 585 j = sizeof(buf); 586 i = sysctl(qoid, nlen + 2, buf, &j, 0, 0); 587 if (!nflag) 588 printf("%s%s", name, sep); 589 printf("%s", buf); 590 return (0); 591 } 592 /* find an estimate of how much we need for this var */ 593 j = 0; 594 i = sysctl(oid, nlen, 0, &j, 0, 0); 595 j += j; /* we want to be sure :-) */ 596 597 val = oval = malloc(j + 1); 598 if (val == NULL) { 599 warnx("malloc failed"); 600 return (1); 601 } 602 len = j; 603 i = sysctl(oid, nlen, val, &len, 0, 0); 604 if (i || !len) { 605 free(oval); 606 return (1); 607 } 608 609 if (bflag) { 610 fwrite(val, 1, len, stdout); 611 free(oval); 612 return (0); 613 } 614 val[len] = '\0'; 615 fmt = buf; 616 oidfmt(oid, nlen, fmt, &kind); 617 p = val; 618 switch (*fmt) { 619 case 'A': 620 if (!nflag) 621 printf("%s%s", name, sep); 622 printf("%.*s", len, p); 623 free(oval); 624 return (0); 625 626 case 'I': 627 case 'L': 628 case 'Q': 629 if (!nflag) 630 printf("%s%s", name, sep); 631 switch (*fmt) { 632 case 'I': intlen = sizeof(int); break; 633 case 'L': intlen = sizeof(long); break; 634 case 'Q': intlen = sizeof(quad_t); break; 635 } 636 hexlen = 2 + (intlen * CHAR_BIT + 3) / 4; 637 sep1 = ""; 638 while (len >= intlen) { 639 switch (*fmt) { 640 case 'I': 641 umv = *(u_int *)p; 642 mv = *(int *)p; 643 break; 644 case 'L': 645 umv = *(u_long *)p; 646 mv = *(long *)p; 647 break; 648 case 'Q': 649 umv = *(u_quad_t *)p; 650 mv = *(quad_t *)p; 651 break; 652 } 653 fputs(sep1, stdout); 654 if (fmt[1] == 'U') 655 printf(hflag ? "%'ju" : "%ju", umv); 656 else if (fmt[1] == 'X') 657 printf("%#0*jx", hexlen, umv); 658 else if (fmt[1] == 'K') { 659 if (mv < 0) 660 printf("%jd", mv); 661 else 662 printf("%.1fC", (mv - 2732.0) / 10); 663 } else 664 printf(hflag ? "%'jd" : "%jd", mv); 665 sep1 = " "; 666 len -= intlen; 667 p += intlen; 668 } 669 free(oval); 670 return (0); 671 672 case 'P': 673 if (!nflag) 674 printf("%s%s", name, sep); 675 printf("%p", *(void **)p); 676 free(oval); 677 return (0); 678 679 case 'T': 680 case 'S': 681 i = 0; 682 if (strcmp(fmt, "S,clockinfo") == 0) 683 func = S_clockinfo; 684 else if (strcmp(fmt, "S,timeval") == 0) 685 func = S_timeval; 686 else if (strcmp(fmt, "S,loadavg") == 0) 687 func = S_loadavg; 688 else if (strcmp(fmt, "S,vmtotal") == 0) 689 func = S_vmtotal; 690 else if (strcmp(fmt, "T,dev_t") == 0) 691 func = T_dev_t; 692 else 693 func = NULL; 694 if (func) { 695 if (!nflag) 696 printf("%s%s", name, sep); 697 i = (*func)(len, p); 698 free(oval); 699 return (i); 700 } 701 /* FALLTHROUGH */ 702 default: 703 if (!oflag && !xflag) { 704 free(oval); 705 return (1); 706 } 707 if (!nflag) 708 printf("%s%s", name, sep); 709 printf("Format:%s Length:%d Dump:0x", fmt, len); 710 while (len-- && (xflag || p < val + 16)) 711 printf("%02x", *p++); 712 if (!xflag && len > 16) 713 printf("..."); 714 free(oval); 715 return (0); 716 } 717 free(oval); 718 return (1); 719 } 720 721 static int 722 sysctl_all(int *oid, int len) 723 { 724 int name1[22], name2[22]; 725 int i, j; 726 size_t l1, l2; 727 728 name1[0] = 0; 729 name1[1] = 2; 730 l1 = 2; 731 if (len) { 732 memcpy(name1+2, oid, len * sizeof(int)); 733 l1 += len; 734 } else { 735 name1[2] = 1; 736 l1++; 737 } 738 for (;;) { 739 l2 = sizeof(name2); 740 j = sysctl(name1, l1, name2, &l2, 0, 0); 741 if (j < 0) { 742 if (errno == ENOENT) 743 return (0); 744 else 745 err(1, "sysctl(getnext) %d %d", j, l2); 746 } 747 748 l2 /= sizeof(int); 749 750 if (len < 0 || l2 < (unsigned int)len) 751 return (0); 752 753 for (i = 0; i < len; i++) 754 if (name2[i] != oid[i]) 755 return (0); 756 757 i = show_var(name2, l2); 758 if (!i && !bflag) 759 putchar('\n'); 760 761 memcpy(name1+2, name2, l2 * sizeof(int)); 762 l1 = 2 + l2; 763 } 764 } 765