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, Nflag, nflag, oflag; 62 static int qflag, xflag; 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 [-bdehNnoqx] 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, "AabdehNnoqwxX")) != -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 'N': 114 Nflag = 1; 115 break; 116 case 'n': 117 nflag = 1; 118 break; 119 case 'o': 120 oflag = 1; 121 break; 122 case 'q': 123 qflag = 1; 124 break; 125 case 'w': 126 /* compatibility */ 127 /* ignored */ 128 break; 129 case 'X': 130 /* compatibility */ 131 aflag = xflag = 1; 132 break; 133 case 'x': 134 xflag = 1; 135 break; 136 default: 137 usage(); 138 } 139 } 140 argc -= optind; 141 argv += optind; 142 143 if (Nflag && nflag) 144 usage(); 145 if (aflag && argc == 0) 146 exit(sysctl_all(0, 0)); 147 if (argc == 0) 148 usage(); 149 while (argc-- > 0) 150 parse(*argv++); 151 exit(0); 152 } 153 154 /* 155 * Parse a name into a MIB entry. 156 * Lookup and print out the MIB entry if it exists. 157 * Set a new value if requested. 158 */ 159 static void 160 parse(char *string) 161 { 162 int len, i, j; 163 void *newval = 0; 164 int intval; 165 unsigned int uintval; 166 long longval; 167 unsigned long ulongval; 168 size_t newsize = 0; 169 quad_t quadval; 170 int mib[CTL_MAXNAME]; 171 char *cp, *bufp, buf[BUFSIZ], *endptr, fmt[BUFSIZ]; 172 u_int kind; 173 174 bufp = buf; 175 if (snprintf(buf, BUFSIZ, "%s", string) >= BUFSIZ) 176 errx(1, "oid too long: '%s'", string); 177 if ((cp = strchr(string, '=')) != NULL) { 178 *strchr(buf, '=') = '\0'; 179 *cp++ = '\0'; 180 while (isspace(*cp)) 181 cp++; 182 newval = cp; 183 newsize = strlen(cp); 184 } 185 len = name2oid(bufp, mib); 186 187 if (len < 0) { 188 if (qflag) 189 exit(1); 190 else 191 errx(1, "unknown oid '%s'", bufp); 192 } 193 194 if (oidfmt(mib, len, fmt, &kind)) 195 err(1, "couldn't find format of oid '%s'", bufp); 196 197 if (newval == NULL) { 198 if ((kind & CTLTYPE) == CTLTYPE_NODE) { 199 if (dflag) { 200 i = show_var(mib, len); 201 if (!i && !bflag) 202 putchar('\n'); 203 } 204 sysctl_all(mib, len); 205 } else { 206 i = show_var(mib, len); 207 if (!i && !bflag) 208 putchar('\n'); 209 } 210 } else { 211 if ((kind & CTLTYPE) == CTLTYPE_NODE) 212 errx(1, "oid '%s' isn't a leaf node", bufp); 213 214 if (!(kind & CTLFLAG_WR)) { 215 if (kind & CTLFLAG_TUN) { 216 warnx("oid '%s' is a read only tunable", bufp); 217 errx(1, "Tunable values are set in /boot/loader.conf"); 218 } else { 219 errx(1, "oid '%s' is read only", bufp); 220 } 221 } 222 223 if ((kind & CTLTYPE) == CTLTYPE_INT || 224 (kind & CTLTYPE) == CTLTYPE_UINT || 225 (kind & CTLTYPE) == CTLTYPE_LONG || 226 (kind & CTLTYPE) == CTLTYPE_ULONG || 227 (kind & CTLTYPE) == CTLTYPE_QUAD) { 228 if (strlen(newval) == 0) 229 errx(1, "empty numeric value"); 230 } 231 232 switch (kind & CTLTYPE) { 233 case CTLTYPE_INT: 234 if (strcmp(fmt, "IK") == 0) { 235 if (!set_IK(newval, &intval)) 236 errx(1, "invalid value '%s'", 237 (char *)newval); 238 } else { 239 intval = (int)strtol(newval, &endptr, 240 0); 241 if (endptr == newval || *endptr != '\0') 242 errx(1, "invalid integer '%s'", 243 (char *)newval); 244 } 245 newval = &intval; 246 newsize = sizeof(intval); 247 break; 248 case CTLTYPE_UINT: 249 uintval = (int) strtoul(newval, &endptr, 0); 250 if (endptr == newval || *endptr != '\0') 251 errx(1, "invalid unsigned integer '%s'", 252 (char *)newval); 253 newval = &uintval; 254 newsize = sizeof(uintval); 255 break; 256 case CTLTYPE_LONG: 257 longval = strtol(newval, &endptr, 0); 258 if (endptr == newval || *endptr != '\0') 259 errx(1, "invalid long integer '%s'", 260 (char *)newval); 261 newval = &longval; 262 newsize = sizeof(longval); 263 break; 264 case CTLTYPE_ULONG: 265 ulongval = strtoul(newval, &endptr, 0); 266 if (endptr == newval || *endptr != '\0') 267 errx(1, "invalid unsigned long integer" 268 " '%s'", (char *)newval); 269 newval = &ulongval; 270 newsize = sizeof(ulongval); 271 break; 272 case CTLTYPE_STRING: 273 break; 274 case CTLTYPE_QUAD: 275 sscanf(newval, "%qd", &quadval); 276 newval = &quadval; 277 newsize = sizeof(quadval); 278 break; 279 case CTLTYPE_OPAQUE: 280 if (strcmp(fmt, "T,dev_t") == 0) { 281 set_T_dev_t (newval, &newval, &newsize); 282 break; 283 } 284 /* FALLTHROUGH */ 285 default: 286 errx(1, "oid '%s' is type %d," 287 " cannot set that", bufp, 288 kind & CTLTYPE); 289 } 290 291 i = show_var(mib, len); 292 if (sysctl(mib, len, 0, 0, newval, newsize) == -1) { 293 if (!i && !bflag) 294 putchar('\n'); 295 switch (errno) { 296 case EOPNOTSUPP: 297 errx(1, "%s: value is not available", 298 string); 299 case ENOTDIR: 300 errx(1, "%s: specification is incomplete", 301 string); 302 case ENOMEM: 303 errx(1, "%s: type is unknown to this program", 304 string); 305 default: 306 warn("%s", string); 307 return; 308 } 309 } 310 if (!bflag) 311 printf(" -> "); 312 i = nflag; 313 nflag = 1; 314 j = show_var(mib, len); 315 if (!j && !bflag) 316 putchar('\n'); 317 nflag = i; 318 } 319 } 320 321 /* These functions will dump out various interesting structures. */ 322 323 static int 324 S_clockinfo(int l2, void *p) 325 { 326 struct clockinfo *ci = (struct clockinfo*)p; 327 328 if (l2 != sizeof(*ci)) { 329 warnx("S_clockinfo %d != %d", l2, sizeof(*ci)); 330 return (0); 331 } 332 printf(hflag ? "{ hz = %'d, tick = %'d, profhz = %'d, stathz = %'d }" : 333 "{ hz = %d, tick = %d, profhz = %d, stathz = %d }", 334 ci->hz, ci->tick, ci->profhz, ci->stathz); 335 return (0); 336 } 337 338 static int 339 S_loadavg(int l2, void *p) 340 { 341 struct loadavg *tv = (struct loadavg*)p; 342 343 if (l2 != sizeof(*tv)) { 344 warnx("S_loadavg %d != %d", l2, sizeof(*tv)); 345 return (0); 346 } 347 printf(hflag ? "{ %'.2f %'.2f %'.2f }" : "{ %.2f %.2f %.2f }", 348 (double)tv->ldavg[0]/(double)tv->fscale, 349 (double)tv->ldavg[1]/(double)tv->fscale, 350 (double)tv->ldavg[2]/(double)tv->fscale); 351 return (0); 352 } 353 354 static int 355 S_timeval(int l2, void *p) 356 { 357 struct timeval *tv = (struct timeval*)p; 358 time_t tv_sec; 359 char *p1, *p2; 360 361 if (l2 != sizeof(*tv)) { 362 warnx("S_timeval %d != %d", l2, sizeof(*tv)); 363 return (0); 364 } 365 printf(hflag ? "{ sec = %'ld, usec = %'ld } " : 366 "{ sec = %ld, usec = %ld } ", 367 tv->tv_sec, tv->tv_usec); 368 tv_sec = tv->tv_sec; 369 p1 = strdup(ctime(&tv_sec)); 370 for (p2=p1; *p2 ; p2++) 371 if (*p2 == '\n') 372 *p2 = '\0'; 373 fputs(p1, stdout); 374 return (0); 375 } 376 377 static int 378 S_vmtotal(int l2, void *p) 379 { 380 struct vmtotal *v = (struct vmtotal *)p; 381 int pageKilo = getpagesize() / 1024; 382 383 if (l2 != sizeof(*v)) { 384 warnx("S_vmtotal %d != %d", l2, sizeof(*v)); 385 return (0); 386 } 387 388 printf( 389 "\nSystem wide totals computed every five seconds:" 390 " (values in kilobytes)\n"); 391 printf("===============================================\n"); 392 printf( 393 "Processes:\t\t(RUNQ: %hd Disk Wait: %hd Page Wait: " 394 "%hd Sleep: %hd)\n", 395 v->t_rq, v->t_dw, v->t_pw, v->t_sl); 396 printf( 397 "Virtual Memory:\t\t(Total: %dK, Active %dK)\n", 398 v->t_vm * pageKilo, v->t_avm * pageKilo); 399 printf("Real Memory:\t\t(Total: %dK Active %dK)\n", 400 v->t_rm * pageKilo, v->t_arm * pageKilo); 401 printf("Shared Virtual Memory:\t(Total: %dK Active: %dK)\n", 402 v->t_vmshr * pageKilo, v->t_avmshr * pageKilo); 403 printf("Shared Real Memory:\t(Total: %dK Active: %dK)\n", 404 v->t_rmshr * pageKilo, v->t_armshr * pageKilo); 405 printf("Free Memory Pages:\t%dK\n", v->t_free * pageKilo); 406 407 return (0); 408 } 409 410 static int 411 T_dev_t(int l2, void *p) 412 { 413 dev_t *d = (dev_t *)p; 414 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 = (void *) &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; 543 const char *sep, *sep1; 544 int qoid[CTL_MAXNAME+2]; 545 uintmax_t umv; 546 intmax_t mv; 547 u_quad_t uqv; 548 quad_t qv; 549 u_long ulv; 550 long lv; 551 u_int uiv; 552 int i, flen, iv; 553 size_t intlen; 554 size_t j, len; 555 u_int kind; 556 int (*func)(int, void *); 557 558 bzero(buf, BUFSIZ); 559 bzero(name, BUFSIZ); 560 qoid[0] = 0; 561 memcpy(qoid + 2, oid, nlen * sizeof(int)); 562 563 qoid[1] = 1; 564 j = sizeof(name); 565 i = sysctl(qoid, nlen + 2, name, &j, 0, 0); 566 if (i || !j) 567 err(1, "sysctl name %d %d %d", i, j, errno); 568 569 if (Nflag) { 570 printf("%s", name); 571 return (0); 572 } 573 574 if (eflag) 575 sep = "="; 576 else 577 sep = ": "; 578 579 if (dflag) { /* just print description */ 580 qoid[1] = 5; 581 j = sizeof(buf); 582 i = sysctl(qoid, nlen + 2, buf, &j, 0, 0); 583 if (!nflag) 584 printf("%s%s", name, sep); 585 printf("%s", buf); 586 return (0); 587 } 588 /* find an estimate of how much we need for this var */ 589 j = 0; 590 i = sysctl(oid, nlen, 0, &j, 0, 0); 591 j += j; /* we want to be sure :-) */ 592 593 val = oval = malloc(j + 1); 594 if (val == NULL) { 595 warnx("malloc failed"); 596 return (-1); 597 } 598 len = j; 599 i = sysctl(oid, nlen, val, &len, 0, 0); 600 if (i || !len) { 601 free(oval); 602 return (1); 603 } 604 605 if (bflag) { 606 fwrite(val, 1, len, stdout); 607 free(oval); 608 return (0); 609 } 610 val[len] = '\0'; 611 fmt = buf; 612 oidfmt(oid, nlen, fmt, &kind); 613 p = val; 614 switch (*fmt) { 615 case 'A': 616 if (!nflag) 617 printf("%s%s", name, sep); 618 printf("%.*s", len, p); 619 free(oval); 620 return (0); 621 622 case 'I': 623 case 'L': 624 case 'Q': 625 if (!nflag) 626 printf("%s%s", name, sep); 627 switch (*fmt) { 628 case 'I': intlen = sizeof(int); flen = 10; break; 629 case 'L': intlen = sizeof(long); flen = 18; break; 630 case 'Q': intlen = sizeof(quad_t); flen = 18; break; 631 } 632 sep1 = ""; 633 while (len >= intlen) { 634 switch (*fmt) { 635 case 'I': 636 memcpy(&uiv, p, intlen); umv = uiv; 637 memcpy(&iv, p, intlen); mv = iv; 638 break; 639 case 'L': 640 memcpy(&ulv, p, intlen); umv = ulv; 641 memcpy(&lv, p, intlen); mv = lv; 642 break; 643 case 'Q': 644 memcpy(&uqv, p, intlen); umv = uqv; 645 memcpy(&qv, p, intlen); mv = qv; 646 break; 647 } 648 fputs(sep1, stdout); 649 if (fmt[1] == 'U') 650 printf(hflag ? "%'ju" : "%ju", umv); 651 else if (fmt[1] == 'X') 652 printf("%#0*jx", flen, umv); 653 else if (fmt[1] == 'K') { 654 if (*(int *)p < 0) 655 printf("%jd", mv); 656 else 657 printf("%.1fC", (mv - 2732.0) / 10); 658 } else 659 printf(hflag ? "%'jd" : "%jd", mv); 660 sep1 = " "; 661 len -= intlen; 662 p += intlen; 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 (len < 0 || l2 < (unsigned int)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