sysctl.c revision 170287
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 31static 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 38static char sccsid[] = "@(#)from: sysctl.c 8.1 (Berkeley) 6/6/93"; 39#endif 40static const char rcsid[] = 41 "$FreeBSD: head/sbin/sysctl/sysctl.c 170287 2007-06-04 18:02:23Z dwmalone $"; 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 <inttypes.h> 58#include <locale.h> 59#include <stdio.h> 60#include <stdlib.h> 61#include <string.h> 62#include <unistd.h> 63 64static int aflag, bflag, dflag, eflag, hflag, Nflag, nflag, oflag; 65static int qflag, xflag; 66 67static int oidfmt(int *, int, char *, u_int *); 68static void parse(char *); 69static int show_var(int *, int); 70static int sysctl_all (int *oid, int len); 71static int name2oid(char *, int *); 72 73static void set_T_dev_t (char *, void **, size_t *); 74static int set_IK(char *, int *); 75 76static void 77usage(void) 78{ 79 80 (void)fprintf(stderr, "%s\n%s\n", 81 "usage: sysctl [-bdehNnoqx] name[=value] ...", 82 " sysctl [-bdehNnoqx] -a"); 83 exit(1); 84} 85 86int 87main(int argc, char **argv) 88{ 89 int ch; 90 91 setlocale(LC_NUMERIC, ""); 92 setbuf(stdout,0); 93 setbuf(stderr,0); 94 95 while ((ch = getopt(argc, argv, "AabdehNnoqwxX")) != -1) { 96 switch (ch) { 97 case 'A': 98 /* compatibility */ 99 aflag = oflag = 1; 100 break; 101 case 'a': 102 aflag = 1; 103 break; 104 case 'b': 105 bflag = 1; 106 break; 107 case 'd': 108 dflag = 1; 109 break; 110 case 'e': 111 eflag = 1; 112 break; 113 case 'h': 114 hflag = 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 while (argc-- > 0) 153 parse(*argv++); 154 exit(0); 155} 156 157/* 158 * Parse a name into a MIB entry. 159 * Lookup and print out the MIB entry if it exists. 160 * Set a new value if requested. 161 */ 162static void 163parse(char *string) 164{ 165 int len, i, j; 166 void *newval = 0; 167 int intval; 168 unsigned int uintval; 169 long longval; 170 unsigned long ulongval; 171 size_t newsize = 0; 172 quad_t quadval; 173 int mib[CTL_MAXNAME]; 174 char *cp, *bufp, buf[BUFSIZ], *endptr, fmt[BUFSIZ]; 175 u_int kind; 176 177 bufp = buf; 178 if (snprintf(buf, BUFSIZ, "%s", string) >= BUFSIZ) 179 errx(1, "oid too long: '%s'", string); 180 if ((cp = strchr(string, '=')) != NULL) { 181 *strchr(buf, '=') = '\0'; 182 *cp++ = '\0'; 183 while (isspace(*cp)) 184 cp++; 185 newval = cp; 186 newsize = strlen(cp); 187 } 188 len = name2oid(bufp, mib); 189 190 if (len < 0) { 191 if (qflag) 192 exit(1); 193 else 194 errx(1, "unknown oid '%s'", bufp); 195 } 196 197 if (oidfmt(mib, len, fmt, &kind)) 198 err(1, "couldn't find format of oid '%s'", bufp); 199 200 if (newval == NULL) { 201 if ((kind & CTLTYPE) == CTLTYPE_NODE) { 202 if (dflag) { 203 i = show_var(mib, len); 204 if (!i && !bflag) 205 putchar('\n'); 206 } 207 sysctl_all(mib, len); 208 } else { 209 i = show_var(mib, len); 210 if (!i && !bflag) 211 putchar('\n'); 212 } 213 } else { 214 if ((kind & CTLTYPE) == CTLTYPE_NODE) 215 errx(1, "oid '%s' isn't a leaf node", bufp); 216 217 if (!(kind & CTLFLAG_WR)) { 218 if (kind & CTLFLAG_TUN) { 219 warnx("oid '%s' is a read only tunable", bufp); 220 errx(1, "Tunable values are set in /boot/loader.conf"); 221 } else { 222 errx(1, "oid '%s' is read only", bufp); 223 } 224 } 225 226 if ((kind & CTLTYPE) == CTLTYPE_INT || 227 (kind & CTLTYPE) == CTLTYPE_UINT || 228 (kind & CTLTYPE) == CTLTYPE_LONG || 229 (kind & CTLTYPE) == CTLTYPE_ULONG || 230 (kind & CTLTYPE) == CTLTYPE_QUAD) { 231 if (strlen(newval) == 0) 232 errx(1, "empty numeric value"); 233 } 234 235 switch (kind & CTLTYPE) { 236 case CTLTYPE_INT: 237 if (strcmp(fmt, "IK") == 0) { 238 if (!set_IK((char*)newval, &intval)) 239 errx(1, "invalid value '%s'", 240 newval); 241 } else { 242 intval = (int)strtol(newval, &endptr, 243 0); 244 if (endptr == newval || *endptr != '\0') 245 errx(1, "invalid integer '%s'", 246 newval); 247 } 248 newval = &intval; 249 newsize = sizeof(intval); 250 break; 251 case CTLTYPE_UINT: 252 uintval = (int) strtoul(newval, &endptr, 0); 253 if (endptr == newval || *endptr != '\0') 254 errx(1, "invalid unsigned integer '%s'", 255 newval); 256 newval = &uintval; 257 newsize = sizeof uintval; 258 break; 259 case CTLTYPE_LONG: 260 longval = strtol(newval, &endptr, 0); 261 if (endptr == newval || *endptr != '\0') 262 errx(1, "invalid long integer '%s'", 263 newval); 264 newval = &longval; 265 newsize = sizeof longval; 266 break; 267 case CTLTYPE_ULONG: 268 ulongval = strtoul(newval, &endptr, 0); 269 if (endptr == newval || *endptr != '\0') 270 errx(1, "invalid unsigned long integer" 271 " '%s'", newval); 272 newval = &ulongval; 273 newsize = sizeof ulongval; 274 break; 275 case CTLTYPE_STRING: 276 break; 277 case CTLTYPE_QUAD: 278 sscanf(newval, "%qd", &quadval); 279 newval = &quadval; 280 newsize = sizeof(quadval); 281 break; 282 case CTLTYPE_OPAQUE: 283 if (strcmp(fmt, "T,dev_t") == 0) { 284 set_T_dev_t ((char*)newval, &newval, &newsize); 285 break; 286 } 287 /* FALLTHROUGH */ 288 default: 289 errx(1, "oid '%s' is type %d," 290 " cannot set that", bufp, 291 kind & CTLTYPE); 292 } 293 294 i = show_var(mib, len); 295 if (sysctl(mib, len, 0, 0, newval, newsize) == -1) { 296 if (!i && !bflag) 297 putchar('\n'); 298 switch (errno) { 299 case EOPNOTSUPP: 300 errx(1, "%s: value is not available", 301 string); 302 case ENOTDIR: 303 errx(1, "%s: specification is incomplete", 304 string); 305 case ENOMEM: 306 errx(1, "%s: type is unknown to this program", 307 string); 308 default: 309 warn("%s", string); 310 return; 311 } 312 } 313 if (!bflag) 314 printf(" -> "); 315 i = nflag; 316 nflag = 1; 317 j = show_var(mib, len); 318 if (!j && !bflag) 319 putchar('\n'); 320 nflag = i; 321 } 322} 323 324/* These functions will dump out various interesting structures. */ 325 326static int 327S_clockinfo(int l2, void *p) 328{ 329 struct clockinfo *ci = (struct clockinfo*)p; 330 if (l2 != sizeof(*ci)) { 331 warnx("S_clockinfo %d != %d", l2, sizeof(*ci)); 332 return (0); 333 } 334 printf(hflag ? "{ hz = %'d, tick = %'d, profhz = %'d, stathz = %'d }" : 335 "{ hz = %d, tick = %d, profhz = %d, stathz = %d }", 336 ci->hz, ci->tick, ci->profhz, ci->stathz); 337 return (0); 338} 339 340static int 341S_loadavg(int l2, void *p) 342{ 343 struct loadavg *tv = (struct loadavg*)p; 344 345 if (l2 != sizeof(*tv)) { 346 warnx("S_loadavg %d != %d", l2, sizeof(*tv)); 347 return (0); 348 } 349 printf(hflag ? "{ %'.2f %'.2f %'.2f }" : "{ %.2f %.2f %.2f }", 350 (double)tv->ldavg[0]/(double)tv->fscale, 351 (double)tv->ldavg[1]/(double)tv->fscale, 352 (double)tv->ldavg[2]/(double)tv->fscale); 353 return (0); 354} 355 356static int 357S_timeval(int l2, void *p) 358{ 359 struct timeval *tv = (struct timeval*)p; 360 time_t tv_sec; 361 char *p1, *p2; 362 363 if (l2 != sizeof(*tv)) { 364 warnx("S_timeval %d != %d", l2, sizeof(*tv)); 365 return (0); 366 } 367 printf(hflag ? "{ sec = %'ld, usec = %'ld } " : 368 "{ sec = %ld, usec = %ld } ", 369 tv->tv_sec, tv->tv_usec); 370 tv_sec = tv->tv_sec; 371 p1 = strdup(ctime(&tv_sec)); 372 for (p2=p1; *p2 ; p2++) 373 if (*p2 == '\n') 374 *p2 = '\0'; 375 fputs(p1, stdout); 376 return (0); 377} 378 379static int 380S_vmtotal(int l2, void *p) 381{ 382 struct vmtotal *v = (struct vmtotal *)p; 383 int pageKilo = getpagesize() / 1024; 384 385 if (l2 != sizeof(*v)) { 386 warnx("S_vmtotal %d != %d", l2, sizeof(*v)); 387 return (0); 388 } 389 390 printf( 391 "\nSystem wide totals computed every five seconds:" 392 " (values in kilobytes)\n"); 393 printf("===============================================\n"); 394 printf( 395 "Processes:\t\t(RUNQ: %hd Disk Wait: %hd Page Wait: " 396 "%hd Sleep: %hd)\n", 397 v->t_rq, v->t_dw, v->t_pw, v->t_sl); 398 printf( 399 "Virtual Memory:\t\t(Total: %dK, Active %dK)\n", 400 v->t_vm * pageKilo, v->t_avm * pageKilo); 401 printf("Real Memory:\t\t(Total: %dK Active %dK)\n", 402 v->t_rm * pageKilo, v->t_arm * pageKilo); 403 printf("Shared Virtual Memory:\t(Total: %dK Active: %dK)\n", 404 v->t_vmshr * pageKilo, v->t_avmshr * pageKilo); 405 printf("Shared Real Memory:\t(Total: %dK Active: %dK)\n", 406 v->t_rmshr * pageKilo, v->t_armshr * pageKilo); 407 printf("Free Memory Pages:\t%dK\n", v->t_free * pageKilo); 408 409 return (0); 410} 411 412static int 413T_dev_t(int l2, void *p) 414{ 415 dev_t *d = (dev_t *)p; 416 if (l2 != sizeof(*d)) { 417 warnx("T_dev_T %d != %d", l2, sizeof(*d)); 418 return (0); 419 } 420 if ((int)(*d) != -1) { 421 if (minor(*d) > 255 || minor(*d) < 0) 422 printf("{ major = %d, minor = 0x%x }", 423 major(*d), minor(*d)); 424 else 425 printf("{ major = %d, minor = %d }", 426 major(*d), minor(*d)); 427 } 428 return (0); 429} 430 431static void 432set_T_dev_t (char *path, void **val, size_t *size) 433{ 434 static struct stat statb; 435 436 if (strcmp(path, "none") && strcmp(path, "off")) { 437 int rc = stat (path, &statb); 438 if (rc) { 439 err(1, "cannot stat %s", path); 440 } 441 442 if (!S_ISCHR(statb.st_mode)) { 443 errx(1, "must specify a device special file."); 444 } 445 } else { 446 statb.st_rdev = NODEV; 447 } 448 *val = (char*) &statb.st_rdev; 449 *size = sizeof statb.st_rdev; 450} 451 452static int 453set_IK(char *str, int *val) 454{ 455 float temp; 456 int len, kelv; 457 char *p, *endptr; 458 459 if ((len = strlen(str)) == 0) 460 return (0); 461 p = &str[len - 1]; 462 if (*p == 'C' || *p == 'F') { 463 *p = '\0'; 464 temp = strtof(str, &endptr); 465 if (endptr == str || *endptr != '\0') 466 return (0); 467 if (*p == 'F') 468 temp = (temp - 32) * 5 / 9; 469 kelv = temp * 10 + 2732; 470 } else { 471 kelv = (int)strtol(str, &endptr, 10); 472 if (endptr == str || *endptr != '\0') 473 return (0); 474 } 475 *val = kelv; 476 return (1); 477} 478 479/* 480 * These functions uses a presently undocumented interface to the kernel 481 * to walk the tree and get the type so it can print the value. 482 * This interface is under work and consideration, and should probably 483 * be killed with a big axe by the first person who can find the time. 484 * (be aware though, that the proper interface isn't as obvious as it 485 * may seem, there are various conflicting requirements. 486 */ 487 488static int 489name2oid(char *name, int *oidp) 490{ 491 int oid[2]; 492 int i; 493 size_t j; 494 495 oid[0] = 0; 496 oid[1] = 3; 497 498 j = CTL_MAXNAME * sizeof(int); 499 i = sysctl(oid, 2, oidp, &j, name, strlen(name)); 500 if (i < 0) 501 return i; 502 j /= sizeof(int); 503 return (j); 504} 505 506static int 507oidfmt(int *oid, int len, char *fmt, u_int *kind) 508{ 509 int qoid[CTL_MAXNAME+2]; 510 u_char buf[BUFSIZ]; 511 int i; 512 size_t j; 513 514 qoid[0] = 0; 515 qoid[1] = 4; 516 memcpy(qoid + 2, oid, len * sizeof(int)); 517 518 j = sizeof(buf); 519 i = sysctl(qoid, len + 2, buf, &j, 0, 0); 520 if (i) 521 err(1, "sysctl fmt %d %d %d", i, j, errno); 522 523 if (kind) 524 *kind = *(u_int *)buf; 525 526 if (fmt) 527 strcpy(fmt, (char *)(buf + sizeof(u_int))); 528 return 0; 529} 530 531/* 532 * This formats and outputs the value of one variable 533 * 534 * Returns zero if anything was actually output. 535 * Returns one if didn't know what to do with this. 536 * Return minus one if we had errors. 537 */ 538 539static int 540show_var(int *oid, int nlen) 541{ 542 u_char buf[BUFSIZ], *val, *oval, *p; 543 char name[BUFSIZ], *fmt, *sep; 544 int qoid[CTL_MAXNAME+2]; 545 int i, flen, iv; 546 unsigned int uiv; 547 long lv; 548 unsigned long ulv; 549 quad_t qv; 550 u_quad_t uqv; 551 size_t intlen; 552 intmax_t v; 553 uintmax_t uv; 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 val = ""; 633 while (len >= intlen) { 634 switch (*fmt) { 635 case 'I': 636 memcpy(&uiv, p, intlen); uv = uiv; 637 memcpy(&iv, p, intlen); v = iv; 638 break; 639 case 'L': 640 memcpy(&ulv, p, intlen); uv = ulv; 641 memcpy(&lv, p, intlen); v = lv; 642 break; 643 case 'Q': 644 memcpy(&uqv, p, intlen); uv = uqv; 645 memcpy(&qv, p, intlen); v = qv; 646 break; 647 } 648 fputs(val, stdout); 649 if (fmt[1] == 'U') 650 printf(hflag ? "%'ju" : "%ju", uv); 651 else if (fmt[1] == 'X') 652 printf(hflag ? "%'#0*jx" : "%#0*jx", flen, uv); 653 else if (fmt[1] == 'K') { 654 if (*(int *)p < 0) 655 printf("%jd", v); 656 else 657 printf("%.1fC", (v - 2732.0) / 10); 658 } else 659 printf(hflag ? "%'d" : "%d", v); 660 val = " "; 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 716static int 717sysctl_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