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 (1); 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 (1); 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 (1); 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 (1); 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 (1); 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 int i, hexlen; 548 size_t intlen; 549 size_t j, len; 550 u_int kind; 551 int (*func)(int, void *); 552 553 bzero(buf, BUFSIZ); 554 bzero(name, BUFSIZ); 555 qoid[0] = 0; 556 memcpy(qoid + 2, oid, nlen * sizeof(int)); 557 558 qoid[1] = 1; 559 j = sizeof(name); 560 i = sysctl(qoid, nlen + 2, name, &j, 0, 0); 561 if (i || !j) 562 err(1, "sysctl name %d %d %d", i, j, errno); 563 564 if (Nflag) { 565 printf("%s", name); 566 return (0); 567 } 568 569 if (eflag) 570 sep = "="; 571 else 572 sep = ": "; 573 574 if (dflag) { /* just print description */ 575 qoid[1] = 5; 576 j = sizeof(buf); 577 i = sysctl(qoid, nlen + 2, buf, &j, 0, 0); 578 if (!nflag) 579 printf("%s%s", name, sep); 580 printf("%s", buf); 581 return (0); 582 } 583 /* find an estimate of how much we need for this var */ 584 j = 0; 585 i = sysctl(oid, nlen, 0, &j, 0, 0); 586 j += j; /* we want to be sure :-) */ 587 588 val = oval = malloc(j + 1); 589 if (val == NULL) { 590 warnx("malloc failed"); 591 return (1); 592 } 593 len = j; 594 i = sysctl(oid, nlen, val, &len, 0, 0); 595 if (i || !len) { 596 free(oval); 597 return (1); 598 } 599 600 if (bflag) { 601 fwrite(val, 1, len, stdout); 602 free(oval); 603 return (0); 604 } 605 val[len] = '\0'; 606 fmt = buf; 607 oidfmt(oid, nlen, fmt, &kind); 608 p = val; 609 switch (*fmt) { 610 case 'A': 611 if (!nflag) 612 printf("%s%s", name, sep); 613 printf("%.*s", len, p); 614 free(oval); 615 return (0); 616 617 case 'I': 618 case 'L': 619 case 'Q': 620 if (!nflag) 621 printf("%s%s", name, sep); 622 switch (*fmt) { 623 case 'I': intlen = sizeof(int); break; 624 case 'L': intlen = sizeof(long); break; 625 case 'Q': intlen = sizeof(quad_t); break; 626 } 627 hexlen = 2 + (intlen * CHAR_BIT + 3) / 4; 628 sep1 = ""; 629 while (len >= intlen) { 630 switch (*fmt) { 631 case 'I': 632 umv = *(u_int *)p; 633 mv = *(int *)p; 634 break; 635 case 'L': 636 umv = *(u_long *)p; 637 mv = *(long *)p; 638 break; 639 case 'Q': 640 umv = *(u_quad_t *)p; 641 mv = *(quad_t *)p; 642 break; 643 } 644 fputs(sep1, stdout); 645 if (fmt[1] == 'U') 646 printf(hflag ? "%'ju" : "%ju", umv); 647 else if (fmt[1] == 'X') 648 printf("%#0*jx", hexlen, umv); 649 else if (fmt[1] == 'K') { 650 if (mv < 0) 651 printf("%jd", mv); 652 else 653 printf("%.1fC", (mv - 2732.0) / 10); 654 } else 655 printf(hflag ? "%'jd" : "%jd", mv); 656 sep1 = " "; 657 len -= intlen; 658 p += intlen; 659 } 660 free(oval); 661 return (0); 662 663 case 'P': 664 if (!nflag) 665 printf("%s%s", name, sep); 666 printf("%p", *(void **)p); 667 free(oval); 668 return (0); 669 670 case 'T': 671 case 'S': 672 i = 0; 673 if (strcmp(fmt, "S,clockinfo") == 0) 674 func = S_clockinfo; 675 else if (strcmp(fmt, "S,timeval") == 0) 676 func = S_timeval; 677 else if (strcmp(fmt, "S,loadavg") == 0) 678 func = S_loadavg; 679 else if (strcmp(fmt, "S,vmtotal") == 0) 680 func = S_vmtotal; 681 else if (strcmp(fmt, "T,dev_t") == 0) 682 func = T_dev_t; 683 else 684 func = NULL; 685 if (func) { 686 if (!nflag) 687 printf("%s%s", name, sep); 688 i = (*func)(len, p); 689 free(oval); 690 return (i); 691 } 692 /* FALLTHROUGH */ 693 default: 694 if (!oflag && !xflag) { 695 free(oval); 696 return (1); 697 } 698 if (!nflag) 699 printf("%s%s", name, sep); 700 printf("Format:%s Length:%d Dump:0x", fmt, len); 701 while (len-- && (xflag || p < val + 16)) 702 printf("%02x", *p++); 703 if (!xflag && len > 16) 704 printf("..."); 705 free(oval); 706 return (0); 707 } 708 free(oval); 709 return (1); 710 } 711 712 static int 713 sysctl_all(int *oid, int len) 714 { 715 int name1[22], name2[22]; 716 int i, j; 717 size_t l1, l2; 718 719 name1[0] = 0; 720 name1[1] = 2; 721 l1 = 2; 722 if (len) { 723 memcpy(name1+2, oid, len * sizeof(int)); 724 l1 += len; 725 } else { 726 name1[2] = 1; 727 l1++; 728 } 729 for (;;) { 730 l2 = sizeof(name2); 731 j = sysctl(name1, l1, name2, &l2, 0, 0); 732 if (j < 0) { 733 if (errno == ENOENT) 734 return (0); 735 else 736 err(1, "sysctl(getnext) %d %d", j, l2); 737 } 738 739 l2 /= sizeof(int); 740 741 if (len < 0 || l2 < (unsigned int)len) 742 return (0); 743 744 for (i = 0; i < len; i++) 745 if (name2[i] != oid[i]) 746 return (0); 747 748 i = show_var(name2, l2); 749 if (!i && !bflag) 750 putchar('\n'); 751 752 memcpy(name1+2, name2, l2 * sizeof(int)); 753 l1 = 2 + l2; 754 } 755 } 756