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