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, 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 [-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 150 warncount = 0; 151 while (argc-- > 0) 152 parse(*argv++); 153 exit(warncount); 154 } 155 156 /* 157 * Parse a name into a MIB entry. 158 * Lookup and print out the MIB entry if it exists. 159 * Set a new value if requested. 160 */ 161 static void 162 parse(char *string) 163 { 164 int len, i, j; 165 void *newval = 0; 166 int intval; 167 unsigned int uintval; 168 long longval; 169 unsigned long ulongval; 170 size_t newsize = 0; 171 quad_t quadval; 172 int mib[CTL_MAXNAME]; 173 char *cp, *bufp, buf[BUFSIZ], *endptr, fmt[BUFSIZ]; 174 u_int kind; 175 176 bufp = buf; 177 if (snprintf(buf, BUFSIZ, "%s", string) >= BUFSIZ) 178 errx(1, "oid too long: '%s'", string); 179 if ((cp = strchr(string, '=')) != NULL) { 180 *strchr(buf, '=') = '\0'; 181 *cp++ = '\0'; 182 while (isspace(*cp)) 183 cp++; 184 newval = cp; 185 newsize = strlen(cp); 186 } 187 len = name2oid(bufp, mib); 188 189 if (len < 0) { 190 if (qflag) 191 exit(1); 192 else 193 errx(1, "unknown oid '%s'", bufp); 194 } 195 196 if (oidfmt(mib, len, fmt, &kind)) 197 err(1, "couldn't find format of oid '%s'", bufp); 198 199 if (newval == NULL) { 200 if ((kind & CTLTYPE) == CTLTYPE_NODE) { 201 if (dflag) { 202 i = show_var(mib, len); 203 if (!i && !bflag) 204 putchar('\n'); 205 } 206 sysctl_all(mib, len); 207 } else { 208 i = show_var(mib, len); 209 if (!i && !bflag) 210 putchar('\n'); 211 } 212 } else { 213 if ((kind & CTLTYPE) == CTLTYPE_NODE) 214 errx(1, "oid '%s' isn't a leaf node", bufp); 215 216 if (!(kind & CTLFLAG_WR)) { 217 if (kind & CTLFLAG_TUN) { 218 warnx("oid '%s' is a read only tunable", bufp); 219 errx(1, "Tunable values are set in /boot/loader.conf"); 220 } else { 221 errx(1, "oid '%s' is read only", bufp); 222 } 223 } 224 225 if ((kind & CTLTYPE) == CTLTYPE_INT || 226 (kind & CTLTYPE) == CTLTYPE_UINT || 227 (kind & CTLTYPE) == CTLTYPE_LONG || 228 (kind & CTLTYPE) == CTLTYPE_ULONG || 229 (kind & CTLTYPE) == CTLTYPE_QUAD) { 230 if (strlen(newval) == 0) 231 errx(1, "empty numeric value"); 232 } 233 234 switch (kind & CTLTYPE) { 235 case CTLTYPE_INT: 236 if (strcmp(fmt, "IK") == 0) { 237 if (!set_IK(newval, &intval)) 238 errx(1, "invalid value '%s'", 239 (char *)newval); 240 } else { 241 intval = (int)strtol(newval, &endptr, 242 0); 243 if (endptr == newval || *endptr != '\0') 244 errx(1, "invalid integer '%s'", 245 (char *)newval); 246 } 247 newval = &intval; 248 newsize = sizeof(intval); 249 break; 250 case CTLTYPE_UINT: 251 uintval = (int) strtoul(newval, &endptr, 0); 252 if (endptr == newval || *endptr != '\0') 253 errx(1, "invalid unsigned integer '%s'", 254 (char *)newval); 255 newval = &uintval; 256 newsize = sizeof(uintval); 257 break; 258 case CTLTYPE_LONG: 259 longval = strtol(newval, &endptr, 0); 260 if (endptr == newval || *endptr != '\0') 261 errx(1, "invalid long integer '%s'", 262 (char *)newval); 263 newval = &longval; 264 newsize = sizeof(longval); 265 break; 266 case CTLTYPE_ULONG: 267 ulongval = strtoul(newval, &endptr, 0); 268 if (endptr == newval || *endptr != '\0') 269 errx(1, "invalid unsigned long integer" 270 " '%s'", (char *)newval); 271 newval = &ulongval; 272 newsize = sizeof(ulongval); 273 break; 274 case CTLTYPE_STRING: 275 break; 276 case CTLTYPE_QUAD: 277 sscanf(newval, "%qd", &quadval); 278 newval = &quadval; 279 newsize = sizeof(quadval); 280 break; 281 case CTLTYPE_OPAQUE: 282 if (strcmp(fmt, "T,dev_t") == 0) { 283 set_T_dev_t (newval, &newval, &newsize); 284 break; 285 } 286 /* FALLTHROUGH */ 287 default: 288 errx(1, "oid '%s' is type %d," 289 " cannot set that", bufp, 290 kind & CTLTYPE); 291 } 292 293 i = show_var(mib, len); 294 if (sysctl(mib, len, 0, 0, newval, newsize) == -1) { 295 if (!i && !bflag) 296 putchar('\n'); 297 switch (errno) { 298 case EOPNOTSUPP: 299 errx(1, "%s: value is not available", 300 string); 301 case ENOTDIR: 302 errx(1, "%s: specification is incomplete", 303 string); 304 case ENOMEM: 305 errx(1, "%s: type is unknown to this program", 306 string); 307 default: 308 warn("%s", string); 309 warncount++; 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 326 static int 327 S_clockinfo(int l2, void *p) 328 { 329 struct clockinfo *ci = (struct clockinfo*)p; 330 331 if (l2 != sizeof(*ci)) { 332 warnx("S_clockinfo %d != %d", l2, sizeof(*ci)); 333 return (1); 334 } 335 printf(hflag ? "{ hz = %'d, tick = %'d, profhz = %'d, stathz = %'d }" : 336 "{ hz = %d, tick = %d, profhz = %d, stathz = %d }", 337 ci->hz, ci->tick, ci->profhz, ci->stathz); 338 return (0); 339 } 340 341 static int 342 S_loadavg(int l2, void *p) 343 { 344 struct loadavg *tv = (struct loadavg*)p; 345 346 if (l2 != sizeof(*tv)) { 347 warnx("S_loadavg %d != %d", l2, sizeof(*tv)); 348 return (1); 349 } 350 printf(hflag ? "{ %'.2f %'.2f %'.2f }" : "{ %.2f %.2f %.2f }", 351 (double)tv->ldavg[0]/(double)tv->fscale, 352 (double)tv->ldavg[1]/(double)tv->fscale, 353 (double)tv->ldavg[2]/(double)tv->fscale); 354 return (0); 355 } 356 357 static int 358 S_timeval(int l2, void *p) 359 { 360 struct timeval *tv = (struct timeval*)p; 361 time_t tv_sec; 362 char *p1, *p2; 363 364 if (l2 != sizeof(*tv)) { 365 warnx("S_timeval %d != %d", l2, sizeof(*tv)); 366 return (1); 367 } 368 printf(hflag ? "{ sec = %'jd, usec = %'ld } " : 369 "{ sec = %jd, usec = %ld } ", 370 (intmax_t)tv->tv_sec, tv->tv_usec); 371 tv_sec = tv->tv_sec; 372 p1 = strdup(ctime(&tv_sec)); 373 for (p2=p1; *p2 ; p2++) 374 if (*p2 == '\n') 375 *p2 = '\0'; 376 fputs(p1, stdout); 377 return (0); 378 } 379 380 static int 381 S_vmtotal(int l2, void *p) 382 { 383 struct vmtotal *v = (struct vmtotal *)p; 384 int pageKilo = getpagesize() / 1024; 385 386 if (l2 != sizeof(*v)) { 387 warnx("S_vmtotal %d != %d", l2, sizeof(*v)); 388 return (1); 389 } 390 391 printf( 392 "\nSystem wide totals computed every five seconds:" 393 " (values in kilobytes)\n"); 394 printf("===============================================\n"); 395 printf( 396 "Processes:\t\t(RUNQ: %hd Disk Wait: %hd Page Wait: " 397 "%hd Sleep: %hd)\n", 398 v->t_rq, v->t_dw, v->t_pw, v->t_sl); 399 printf( 400 "Virtual Memory:\t\t(Total: %dK, Active %dK)\n", 401 v->t_vm * pageKilo, v->t_avm * pageKilo); 402 printf("Real Memory:\t\t(Total: %dK Active %dK)\n", 403 v->t_rm * pageKilo, v->t_arm * pageKilo); 404 printf("Shared Virtual Memory:\t(Total: %dK Active: %dK)\n", 405 v->t_vmshr * pageKilo, v->t_avmshr * pageKilo); 406 printf("Shared Real Memory:\t(Total: %dK Active: %dK)\n", 407 v->t_rmshr * pageKilo, v->t_armshr * pageKilo); 408 printf("Free Memory Pages:\t%dK\n", v->t_free * pageKilo); 409 410 return (0); 411 } 412 413 static int 414 T_dev_t(int l2, void *p) 415 { 416 dev_t *d = (dev_t *)p; 417 418 if (l2 != sizeof(*d)) { 419 warnx("T_dev_T %d != %d", l2, sizeof(*d)); 420 return (1); 421 } 422 if ((int)(*d) != -1) { 423 if (minor(*d) > 255 || minor(*d) < 0) 424 printf("{ major = %d, minor = 0x%x }", 425 major(*d), minor(*d)); 426 else 427 printf("{ major = %d, minor = %d }", 428 major(*d), minor(*d)); 429 } 430 return (0); 431 } 432 433 static void 434 set_T_dev_t(char *path, void **val, size_t *size) 435 { 436 static struct stat statb; 437 438 if (strcmp(path, "none") && strcmp(path, "off")) { 439 int rc = stat (path, &statb); 440 if (rc) { 441 err(1, "cannot stat %s", path); 442 } 443 444 if (!S_ISCHR(statb.st_mode)) { 445 errx(1, "must specify a device special file."); 446 } 447 } else { 448 statb.st_rdev = NODEV; 449 } 450 *val = (void *) &statb.st_rdev; 451 *size = sizeof(statb.st_rdev); 452 } 453 454 static int 455 set_IK(char *str, int *val) 456 { 457 float temp; 458 int len, kelv; 459 char *p, *endptr; 460 461 if ((len = strlen(str)) == 0) 462 return (0); 463 p = &str[len - 1]; 464 if (*p == 'C' || *p == 'F') { 465 *p = '\0'; 466 temp = strtof(str, &endptr); 467 if (endptr == str || *endptr != '\0') 468 return (0); 469 if (*p == 'F') 470 temp = (temp - 32) * 5 / 9; 471 kelv = temp * 10 + 2732; 472 } else { 473 kelv = (int)strtol(str, &endptr, 10); 474 if (endptr == str || *endptr != '\0') 475 return (0); 476 } 477 *val = kelv; 478 return (1); 479 } 480 481 /* 482 * These functions uses a presently undocumented interface to the kernel 483 * to walk the tree and get the type so it can print the value. 484 * This interface is under work and consideration, and should probably 485 * be killed with a big axe by the first person who can find the time. 486 * (be aware though, that the proper interface isn't as obvious as it 487 * may seem, there are various conflicting requirements. 488 */ 489 490 static int 491 name2oid(char *name, int *oidp) 492 { 493 int oid[2]; 494 int i; 495 size_t j; 496 497 oid[0] = 0; 498 oid[1] = 3; 499 500 j = CTL_MAXNAME * sizeof(int); 501 i = sysctl(oid, 2, oidp, &j, name, strlen(name)); 502 if (i < 0) 503 return (i); 504 j /= sizeof(int); 505 return (j); 506 } 507 508 static int 509 oidfmt(int *oid, int len, char *fmt, u_int *kind) 510 { 511 int qoid[CTL_MAXNAME+2]; 512 u_char buf[BUFSIZ]; 513 int i; 514 size_t j; 515 516 qoid[0] = 0; 517 qoid[1] = 4; 518 memcpy(qoid + 2, oid, len * sizeof(int)); 519 520 j = sizeof(buf); 521 i = sysctl(qoid, len + 2, buf, &j, 0, 0); 522 if (i) 523 err(1, "sysctl fmt %d %d %d", i, j, errno); 524 525 if (kind) 526 *kind = *(u_int *)buf; 527 528 if (fmt) 529 strcpy(fmt, (char *)(buf + sizeof(u_int))); 530 return (0); 531 } 532 533 /* 534 * This formats and outputs the value of one variable 535 * 536 * Returns zero if anything was actually output. 537 * Returns one if didn't know what to do with this. 538 * Return minus one if we had errors. 539 */ 540 541 static int 542 show_var(int *oid, int nlen) 543 { 544 u_char buf[BUFSIZ], *val, *oval, *p; 545 char name[BUFSIZ], *fmt; 546 const char *sep, *sep1; 547 int qoid[CTL_MAXNAME+2]; 548 uintmax_t umv; 549 intmax_t mv; 550 int i, hexlen; 551 size_t intlen; 552 size_t j, len; 553 u_int kind; 554 int (*func)(int, void *); 555 556 bzero(buf, BUFSIZ); 557 bzero(name, BUFSIZ); 558 qoid[0] = 0; 559 memcpy(qoid + 2, oid, nlen * sizeof(int)); 560 561 qoid[1] = 1; 562 j = sizeof(name); 563 i = sysctl(qoid, nlen + 2, name, &j, 0, 0); 564 if (i || !j) 565 err(1, "sysctl name %d %d %d", i, j, errno); 566 567 if (Nflag) { 568 printf("%s", name); 569 return (0); 570 } 571 572 if (eflag) 573 sep = "="; 574 else 575 sep = ": "; 576 577 if (dflag) { /* just print description */ 578 qoid[1] = 5; 579 j = sizeof(buf); 580 i = sysctl(qoid, nlen + 2, buf, &j, 0, 0); 581 if (!nflag) 582 printf("%s%s", name, sep); 583 printf("%s", buf); 584 return (0); 585 } 586 /* find an estimate of how much we need for this var */ 587 j = 0; 588 i = sysctl(oid, nlen, 0, &j, 0, 0); 589 j += j; /* we want to be sure :-) */ 590 591 val = oval = malloc(j + 1); 592 if (val == NULL) { 593 warnx("malloc failed"); 594 return (1); 595 } 596 len = j; 597 i = sysctl(oid, nlen, val, &len, 0, 0); 598 if (i || !len) { 599 free(oval); 600 return (1); 601 } 602 603 if (bflag) { 604 fwrite(val, 1, len, stdout); 605 free(oval); 606 return (0); 607 } 608 val[len] = '\0'; 609 fmt = buf; 610 oidfmt(oid, nlen, fmt, &kind); 611 p = val; 612 switch (*fmt) { 613 case 'A': 614 if (!nflag) 615 printf("%s%s", name, sep); 616 printf("%.*s", len, p); 617 free(oval); 618 return (0); 619 620 case 'I': 621 case 'L': 622 case 'Q': 623 if (!nflag) 624 printf("%s%s", name, sep); 625 switch (*fmt) { 626 case 'I': intlen = sizeof(int); break; 627 case 'L': intlen = sizeof(long); break; 628 case 'Q': intlen = sizeof(quad_t); break; 629 } 630 hexlen = 2 + (intlen * CHAR_BIT + 3) / 4; 631 sep1 = ""; 632 while (len >= intlen) { 633 switch (*fmt) { 634 case 'I': 635 umv = *(u_int *)p; 636 mv = *(int *)p; 637 break; 638 case 'L': 639 umv = *(u_long *)p; 640 mv = *(long *)p; 641 break; 642 case 'Q': 643 umv = *(u_quad_t *)p; 644 mv = *(quad_t *)p; 645 break; 646 } 647 fputs(sep1, stdout); 648 if (fmt[1] == 'U') 649 printf(hflag ? "%'ju" : "%ju", umv); 650 else if (fmt[1] == 'X') 651 printf("%#0*jx", hexlen, umv); 652 else if (fmt[1] == 'K') { 653 if (mv < 0) 654 printf("%jd", mv); 655 else 656 printf("%.1fC", (mv - 2732.0) / 10); 657 } else 658 printf(hflag ? "%'jd" : "%jd", mv); 659 sep1 = " "; 660 len -= intlen; 661 p += intlen; 662 } 663 free(oval); 664 return (0); 665 666 case 'P': 667 if (!nflag) 668 printf("%s%s", name, sep); 669 printf("%p", *(void **)p); 670 free(oval); 671 return (0); 672 673 case 'T': 674 case 'S': 675 i = 0; 676 if (strcmp(fmt, "S,clockinfo") == 0) 677 func = S_clockinfo; 678 else if (strcmp(fmt, "S,timeval") == 0) 679 func = S_timeval; 680 else if (strcmp(fmt, "S,loadavg") == 0) 681 func = S_loadavg; 682 else if (strcmp(fmt, "S,vmtotal") == 0) 683 func = S_vmtotal; 684 else if (strcmp(fmt, "T,dev_t") == 0) 685 func = T_dev_t; 686 else 687 func = NULL; 688 if (func) { 689 if (!nflag) 690 printf("%s%s", name, sep); 691 i = (*func)(len, p); 692 free(oval); 693 return (i); 694 } 695 /* FALLTHROUGH */ 696 default: 697 if (!oflag && !xflag) { 698 free(oval); 699 return (1); 700 } 701 if (!nflag) 702 printf("%s%s", name, sep); 703 printf("Format:%s Length:%d Dump:0x", fmt, len); 704 while (len-- && (xflag || p < val + 16)) 705 printf("%02x", *p++); 706 if (!xflag && len > 16) 707 printf("..."); 708 free(oval); 709 return (0); 710 } 711 free(oval); 712 return (1); 713 } 714 715 static int 716 sysctl_all(int *oid, int len) 717 { 718 int name1[22], name2[22]; 719 int i, j; 720 size_t l1, l2; 721 722 name1[0] = 0; 723 name1[1] = 2; 724 l1 = 2; 725 if (len) { 726 memcpy(name1+2, oid, len * sizeof(int)); 727 l1 += len; 728 } else { 729 name1[2] = 1; 730 l1++; 731 } 732 for (;;) { 733 l2 = sizeof(name2); 734 j = sysctl(name1, l1, name2, &l2, 0, 0); 735 if (j < 0) { 736 if (errno == ENOENT) 737 return (0); 738 else 739 err(1, "sysctl(getnext) %d %d", j, l2); 740 } 741 742 l2 /= sizeof(int); 743 744 if (len < 0 || l2 < (unsigned int)len) 745 return (0); 746 747 for (i = 0; i < len; i++) 748 if (name2[i] != oid[i]) 749 return (0); 750 751 i = show_var(name2, l2); 752 if (!i && !bflag) 753 putchar('\n'); 754 755 memcpy(name1+2, name2, l2 * sizeof(int)); 756 l1 = 2 + l2; 757 } 758 } 759