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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static const char copyright[] = 36 "@(#) Copyright (c) 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)from: sysctl.c 8.1 (Berkeley) 6/6/93"; 43 #endif 44 static const char rcsid[] = 45 "$FreeBSD$"; 46 #endif /* not lint */ 47 48 #ifdef __i386__ 49 #include <sys/reboot.h> /* used for bootdev parsing */ 50 #endif 51 #include <sys/param.h> 52 #include <sys/time.h> 53 #include <sys/resource.h> 54 #include <sys/stat.h> 55 #include <sys/sysctl.h> 56 #include <sys/vmmeter.h> 57 58 #include <ctype.h> 59 #include <err.h> 60 #include <errno.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 66 static int aflag, bflag, dflag, eflag, Nflag, nflag, oflag, xflag; 67 68 static int oidfmt(int *, int, char *, u_int *); 69 static void parse(char *); 70 static int show_var(int *, int); 71 static int sysctl_all (int *oid, int len); 72 static int name2oid(char *, int *); 73 74 static void set_T_dev_t (char *, void **, int *); 75 76 static void 77 usage(void) 78 { 79 80 (void)fprintf(stderr, "%s\n%s\n", 81 "usage: sysctl [-bdeNnox] variable[=value] ...", 82 " sysctl [-bdeNnox] -a"); 83 exit(1); 84 } 85 86 int 87 main(int argc, char **argv) 88 { 89 int ch; 90 setbuf(stdout,0); 91 setbuf(stderr,0); 92 93 while ((ch = getopt(argc, argv, "AabdeNnowxX")) != -1) { 94 switch (ch) { 95 case 'A': 96 /* compatibility */ 97 aflag = oflag = 1; 98 break; 99 case 'a': 100 aflag = 1; 101 break; 102 case 'b': 103 bflag = 1; 104 break; 105 case 'd': 106 dflag = 1; 107 break; 108 case 'e': 109 eflag = 1; 110 break; 111 case 'N': 112 Nflag = 1; 113 break; 114 case 'n': 115 nflag = 1; 116 break; 117 case 'o': 118 oflag = 1; 119 break; 120 case 'w': 121 /* compatibility */ 122 /* ignored */ 123 break; 124 case 'X': 125 /* compatibility */ 126 aflag = xflag = 1; 127 break; 128 case 'x': 129 xflag = 1; 130 break; 131 default: 132 usage(); 133 } 134 } 135 argc -= optind; 136 argv += optind; 137 138 if (Nflag && nflag) 139 usage(); 140 if (aflag && argc == 0) 141 exit(sysctl_all(0, 0)); 142 if (argc == 0) 143 usage(); 144 while (argc-- > 0) 145 parse(*argv++); 146 exit(0); 147 } 148 149 /* 150 * Parse a name into a MIB entry. 151 * Lookup and print out the MIB entry if it exists. 152 * Set a new value if requested. 153 */ 154 static void 155 parse(char *string) 156 { 157 int len, i, j; 158 void *newval = 0; 159 int intval; 160 unsigned int uintval; 161 long longval; 162 unsigned long ulongval; 163 size_t newsize = 0; 164 quad_t quadval; 165 int mib[CTL_MAXNAME]; 166 char *cp, *bufp, buf[BUFSIZ], *endptr, fmt[BUFSIZ]; 167 u_int kind; 168 169 bufp = buf; 170 snprintf(buf, BUFSIZ, "%s", string); 171 if ((cp = strchr(string, '=')) != NULL) { 172 *strchr(buf, '=') = '\0'; 173 *cp++ = '\0'; 174 while (isspace(*cp)) 175 cp++; 176 newval = cp; 177 newsize = strlen(cp); 178 } 179 len = name2oid(bufp, mib); 180 181 if (len < 0) 182 errx(1, "unknown oid '%s'", bufp); 183 184 if (oidfmt(mib, len, fmt, &kind)) 185 err(1, "couldn't find format of oid '%s'", bufp); 186 187 if (newval == NULL) { 188 if ((kind & CTLTYPE) == CTLTYPE_NODE) { 189 sysctl_all(mib, len); 190 } else { 191 i = show_var(mib, len); 192 if (!i && !bflag) 193 putchar('\n'); 194 } 195 } else { 196 if ((kind & CTLTYPE) == CTLTYPE_NODE) 197 errx(1, "oid '%s' isn't a leaf node", bufp); 198 199 if (!(kind&CTLFLAG_WR)) 200 errx(1, "oid '%s' is read only", bufp); 201 202 if ((kind & CTLTYPE) == CTLTYPE_INT || 203 (kind & CTLTYPE) == CTLTYPE_UINT || 204 (kind & CTLTYPE) == CTLTYPE_LONG || 205 (kind & CTLTYPE) == CTLTYPE_ULONG) { 206 if (strlen(newval) == 0) 207 errx(1, "empty numeric value"); 208 } 209 210 switch (kind & CTLTYPE) { 211 case CTLTYPE_INT: 212 intval = (int) strtol(newval, &endptr, 0); 213 if (endptr == newval || *endptr != '\0') 214 errx(1, "invalid integer '%s'", 215 newval); 216 newval = &intval; 217 newsize = sizeof(intval); 218 break; 219 case CTLTYPE_UINT: 220 uintval = (int) strtoul(newval, &endptr, 0); 221 if (endptr == newval || *endptr != '\0') 222 errx(1, "invalid unsigned integer '%s'", 223 newval); 224 newval = &uintval; 225 newsize = sizeof uintval; 226 break; 227 case CTLTYPE_LONG: 228 longval = strtol(newval, &endptr, 0); 229 if (endptr == newval || *endptr != '\0') 230 errx(1, "invalid long integer '%s'", 231 newval); 232 newval = &longval; 233 newsize = sizeof longval; 234 break; 235 case CTLTYPE_ULONG: 236 ulongval = strtoul(newval, &endptr, 0); 237 if (endptr == newval || *endptr != '\0') 238 errx(1, "invalid unsigned long integer" 239 " '%s'", newval); 240 newval = &ulongval; 241 newsize = sizeof ulongval; 242 break; 243 case CTLTYPE_STRING: 244 break; 245 case CTLTYPE_QUAD: 246 sscanf(newval, "%qd", &quadval); 247 newval = &quadval; 248 newsize = sizeof(quadval); 249 break; 250 case CTLTYPE_OPAQUE: 251 if (strcmp(fmt, "T,dev_t") == 0) { 252 set_T_dev_t ((char*)newval, &newval, &newsize); 253 break; 254 } 255 /* FALLTHROUGH */ 256 default: 257 errx(1, "oid '%s' is type %d," 258 " cannot set that", bufp, 259 kind & CTLTYPE); 260 } 261 262 i = show_var(mib, len); 263 if (sysctl(mib, len, 0, 0, newval, newsize) == -1) { 264 if (!i && !bflag) 265 putchar('\n'); 266 switch (errno) { 267 case EOPNOTSUPP: 268 errx(1, "%s: value is not available", 269 string); 270 case ENOTDIR: 271 errx(1, "%s: specification is incomplete", 272 string); 273 case ENOMEM: 274 errx(1, "%s: type is unknown to this program", 275 string); 276 default: 277 warn("%s", string); 278 return; 279 } 280 } 281 if (!bflag) 282 printf(" -> "); 283 i = nflag; 284 nflag = 1; 285 j = show_var(mib, len); 286 if (!j && !bflag) 287 putchar('\n'); 288 nflag = i; 289 } 290 } 291 292 /* These functions will dump out various interesting structures. */ 293 294 static int 295 S_clockinfo(int l2, void *p) 296 { 297 struct clockinfo *ci = (struct clockinfo*)p; 298 if (l2 != sizeof(*ci)) { 299 warnx("S_clockinfo %d != %d", l2, sizeof(*ci)); 300 return (0); 301 } 302 printf("{ hz = %d, tick = %d, profhz = %d, stathz = %d }", 303 ci->hz, ci->tick, ci->profhz, ci->stathz); 304 return (0); 305 } 306 307 static int 308 S_loadavg(int l2, void *p) 309 { 310 struct loadavg *tv = (struct loadavg*)p; 311 312 if (l2 != sizeof(*tv)) { 313 warnx("S_loadavg %d != %d", l2, sizeof(*tv)); 314 return (0); 315 } 316 printf("{ %.2f %.2f %.2f }", 317 (double)tv->ldavg[0]/(double)tv->fscale, 318 (double)tv->ldavg[1]/(double)tv->fscale, 319 (double)tv->ldavg[2]/(double)tv->fscale); 320 return (0); 321 } 322 323 static int 324 S_timeval(int l2, void *p) 325 { 326 struct timeval *tv = (struct timeval*)p; 327 time_t tv_sec; 328 char *p1, *p2; 329 330 if (l2 != sizeof(*tv)) { 331 warnx("S_timeval %d != %d", l2, sizeof(*tv)); 332 return (0); 333 } 334 printf("{ sec = %ld, usec = %ld } ", 335 tv->tv_sec, tv->tv_usec); 336 tv_sec = tv->tv_sec; 337 p1 = strdup(ctime(&tv_sec)); 338 for (p2=p1; *p2 ; p2++) 339 if (*p2 == '\n') 340 *p2 = '\0'; 341 fputs(p1, stdout); 342 return (0); 343 } 344 345 static int 346 S_vmtotal(int l2, void *p) 347 { 348 struct vmtotal *v = (struct vmtotal *)p; 349 int pageKilo = getpagesize() / 1024; 350 351 if (l2 != sizeof(*v)) { 352 warnx("S_vmtotal %d != %d", l2, sizeof(*v)); 353 return (0); 354 } 355 356 printf( 357 "\nSystem wide totals computed every five seconds:" 358 " (values in kilobytes)\n"); 359 printf("===============================================\n"); 360 printf( 361 "Processes:\t\t(RUNQ: %hu Disk Wait: %hu Page Wait: " 362 "%hu Sleep: %hu)\n", 363 v->t_rq, v->t_dw, v->t_pw, v->t_sl); 364 printf( 365 "Virtual Memory:\t\t(Total: %luK, Active %lldK)\n", 366 (unsigned long)v->t_vm / 1024, 367 (long long)v->t_avm * pageKilo); 368 printf("Real Memory:\t\t(Total: %lldK Active %lldK)\n", 369 (long long)v->t_rm * pageKilo, (long long)v->t_arm * pageKilo); 370 printf("Shared Virtual Memory:\t(Total: %lldK Active: %lldK)\n", 371 (long long)v->t_vmshr * pageKilo, 372 (long long)v->t_avmshr * pageKilo); 373 printf("Shared Real Memory:\t(Total: %lldK Active: %lldK)\n", 374 (long long)v->t_rmshr * pageKilo, 375 (long long)v->t_armshr * pageKilo); 376 printf("Free Memory Pages:\t%ldK\n", (long long)v->t_free * pageKilo); 377 378 return (0); 379 } 380 381 static int 382 T_dev_t(int l2, void *p) 383 { 384 dev_t *d = (dev_t *)p; 385 if (l2 != sizeof(*d)) { 386 warnx("T_dev_T %d != %d", l2, sizeof(*d)); 387 return (0); 388 } 389 if ((int)(*d) != -1) { 390 if (minor(*d) > 255 || minor(*d) < 0) 391 printf("{ major = %d, minor = 0x%x }", 392 major(*d), minor(*d)); 393 else 394 printf("{ major = %d, minor = %d }", 395 major(*d), minor(*d)); 396 } 397 return (0); 398 } 399 400 static void 401 set_T_dev_t (char *path, void **val, int *size) 402 { 403 static struct stat statb; 404 405 if (strcmp(path, "none") && strcmp(path, "off")) { 406 int rc = stat (path, &statb); 407 if (rc) { 408 err(1, "cannot stat %s", path); 409 } 410 411 if (!S_ISCHR(statb.st_mode)) { 412 errx(1, "must specify a device special file."); 413 } 414 } else { 415 statb.st_rdev = NODEV; 416 } 417 *val = (char*) &statb.st_rdev; 418 *size = sizeof statb.st_rdev; 419 } 420 421 /* 422 * These functions uses a presently undocumented interface to the kernel 423 * to walk the tree and get the type so it can print the value. 424 * This interface is under work and consideration, and should probably 425 * be killed with a big axe by the first person who can find the time. 426 * (be aware though, that the proper interface isn't as obvious as it 427 * may seem, there are various conflicting requirements. 428 */ 429 430 static int 431 name2oid(char *name, int *oidp) 432 { 433 int oid[2]; 434 int i; 435 size_t j; 436 437 oid[0] = 0; 438 oid[1] = 3; 439 440 j = CTL_MAXNAME * sizeof(int); 441 i = sysctl(oid, 2, oidp, &j, name, strlen(name)); 442 if (i < 0) 443 return i; 444 j /= sizeof(int); 445 return (j); 446 } 447 448 static int 449 oidfmt(int *oid, int len, char *fmt, u_int *kind) 450 { 451 int qoid[CTL_MAXNAME+2]; 452 u_char buf[BUFSIZ]; 453 int i; 454 size_t j; 455 456 qoid[0] = 0; 457 qoid[1] = 4; 458 memcpy(qoid + 2, oid, len * sizeof(int)); 459 460 j = sizeof(buf); 461 i = sysctl(qoid, len + 2, buf, &j, 0, 0); 462 if (i) 463 err(1, "sysctl fmt %d %d %d", i, j, errno); 464 465 if (kind) 466 *kind = *(u_int *)buf; 467 468 if (fmt) 469 strcpy(fmt, (char *)(buf + sizeof(u_int))); 470 return 0; 471 } 472 473 /* 474 * This formats and outputs the value of one variable 475 * 476 * Returns zero if anything was actually output. 477 * Returns one if didn't know what to do with this. 478 * Return minus one if we had errors. 479 */ 480 481 static int 482 show_var(int *oid, int nlen) 483 { 484 u_char buf[BUFSIZ], *val, *p; 485 char name[BUFSIZ], *fmt, *sep; 486 int qoid[CTL_MAXNAME+2]; 487 int i; 488 size_t j, len; 489 u_int kind; 490 int (*func)(int, void *); 491 492 qoid[0] = 0; 493 memcpy(qoid + 2, oid, nlen * sizeof(int)); 494 495 qoid[1] = 1; 496 j = sizeof(name); 497 i = sysctl(qoid, nlen + 2, name, &j, 0, 0); 498 if (i || !j) 499 err(1, "sysctl name %d %d %d", i, j, errno); 500 501 if (Nflag) { 502 printf("%s", name); 503 return (0); 504 } 505 506 if (eflag) 507 sep = "="; 508 else 509 sep = ": "; 510 511 if (dflag) { /* just print description */ 512 qoid[1] = 5; 513 j = sizeof(buf); 514 i = sysctl(qoid, nlen + 2, buf, &j, 0, 0); 515 if (!nflag) 516 printf("%s%s", name, sep); 517 printf("%s", buf); 518 return (0); 519 } 520 /* find an estimate of how much we need for this var */ 521 j = 0; 522 i = sysctl(oid, nlen, 0, &j, 0, 0); 523 j += j; /* we want to be sure :-) */ 524 525 val = alloca(j + 1); 526 len = j; 527 i = sysctl(oid, nlen, val, &len, 0, 0); 528 if (i || !len) 529 return (1); 530 531 if (bflag) { 532 fwrite(val, 1, len, stdout); 533 return (0); 534 } 535 val[len] = '\0'; 536 fmt = buf; 537 oidfmt(oid, nlen, fmt, &kind); 538 p = val; 539 switch (*fmt) { 540 case 'A': 541 if (!nflag) 542 printf("%s%s", name, sep); 543 printf("%.*s", len, p); 544 return (0); 545 546 case 'I': 547 if (!nflag) 548 printf("%s%s", name, sep); 549 fmt++; 550 val = ""; 551 while (len >= sizeof(int)) { 552 if(*fmt == 'U') 553 printf("%s%u", val, *(unsigned int *)p); 554 else 555 printf("%s%d", val, *(int *)p); 556 val = " "; 557 len -= sizeof(int); 558 p += sizeof(int); 559 } 560 return (0); 561 562 case 'L': 563 if (!nflag) 564 printf("%s%s", name, sep); 565 fmt++; 566 val = ""; 567 while (len >= sizeof(long)) { 568 if(*fmt == 'U') 569 printf("%s%lu", val, *(unsigned long *)p); 570 else 571 printf("%s%ld", val, *(long *)p); 572 val = " "; 573 len -= sizeof(long); 574 p += sizeof(long); 575 } 576 return (0); 577 578 case 'P': 579 if (!nflag) 580 printf("%s%s", name, sep); 581 printf("%p", *(void **)p); 582 return (0); 583 584 case 'T': 585 case 'S': 586 i = 0; 587 if (strcmp(fmt, "S,clockinfo") == 0) 588 func = S_clockinfo; 589 else if (strcmp(fmt, "S,timeval") == 0) 590 func = S_timeval; 591 else if (strcmp(fmt, "S,loadavg") == 0) 592 func = S_loadavg; 593 else if (strcmp(fmt, "S,vmtotal") == 0) 594 func = S_vmtotal; 595 else if (strcmp(fmt, "T,dev_t") == 0) 596 func = T_dev_t; 597 else 598 func = NULL; 599 if (func) { 600 if (!nflag) 601 printf("%s%s", name, sep); 602 return ((*func)(len, p)); 603 } 604 /* FALLTHROUGH */ 605 default: 606 if (!oflag && !xflag) 607 return (1); 608 if (!nflag) 609 printf("%s%s", name, sep); 610 printf("Format:%s Length:%d Dump:0x", fmt, len); 611 while (len-- && (xflag || p < val + 16)) 612 printf("%02x", *p++); 613 if (!xflag && len > 16) 614 printf("..."); 615 return (0); 616 } 617 return (1); 618 } 619 620 static int 621 sysctl_all (int *oid, int len) 622 { 623 int name1[22], name2[22]; 624 int i, j; 625 size_t l1, l2; 626 627 name1[0] = 0; 628 name1[1] = 2; 629 l1 = 2; 630 if (len) { 631 memcpy(name1+2, oid, len * sizeof(int)); 632 l1 += len; 633 } else { 634 name1[2] = 1; 635 l1++; 636 } 637 for (;;) { 638 l2 = sizeof(name2); 639 j = sysctl(name1, l1, name2, &l2, 0, 0); 640 if (j < 0) { 641 if (errno == ENOENT) 642 return 0; 643 else 644 err(1, "sysctl(getnext) %d %d", j, l2); 645 } 646 647 l2 /= sizeof(int); 648 649 if (l2 < len) 650 return 0; 651 652 for (i = 0; i < len; i++) 653 if (name2[i] != oid[i]) 654 return 0; 655 656 i = show_var(name2, l2); 657 if (!i && !bflag) 658 putchar('\n'); 659 660 memcpy(name1+2, name2, l2 * sizeof(int)); 661 l1 = 2 + l2; 662 } 663 } 664