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 * @(#) Copyright (c) 1993 The Regents of the University of California. All rights reserved. 34 * @(#)from: sysctl.c 8.1 (Berkeley) 6/6/93 35 * $FreeBSD: src/sbin/sysctl/sysctl.c,v 1.25.2.11 2003/05/01 22:48:08 trhodes Exp $ 36 * $DragonFly: src/sbin/sysctl/sysctl.c,v 1.7 2005/01/10 20:27:51 cpressey Exp $ 37 */ 38 39 #ifdef __i386__ 40 #include <sys/diskslice.h> /* used for bootdev parsing */ 41 #include <sys/reboot.h> /* used for bootdev parsing */ 42 #endif 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/sysctl.h> 46 #include <sys/resource.h> 47 #include <sys/param.h> 48 49 #include <machine/inttypes.h> 50 51 #include <ctype.h> 52 #include <err.h> 53 #include <errno.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 static int aflag, bflag, dflag, eflag, Nflag, nflag, oflag, xflag; 60 61 static int oidfmt(int *, size_t, char *, u_int *); 62 static void parse(const char *); 63 static int show_var(int *, size_t); 64 static int sysctl_all(int *, size_t); 65 static void set_T_dev_t(const char *, void **, int *); 66 67 static void 68 usage(void) 69 { 70 71 fprintf(stderr, "%s\n%s\n", 72 "usage: sysctl [-bdeNnox] variable[=value] ...", 73 " sysctl [-bdeNnox] -a"); 74 exit(1); 75 } 76 77 int 78 main(int argc, char **argv) 79 { 80 int ch; 81 setbuf(stdout,0); 82 setbuf(stderr,0); 83 84 while ((ch = getopt(argc, argv, "AabdeNnowxX")) != -1) { 85 switch (ch) { 86 case 'A': 87 /* compatibility */ 88 aflag = oflag = 1; 89 break; 90 case 'a': 91 aflag = 1; 92 break; 93 case 'b': 94 bflag = 1; 95 break; 96 case 'd': 97 dflag = 1; 98 break; 99 case 'e': 100 eflag = 1; 101 break; 102 case 'N': 103 Nflag = 1; 104 break; 105 case 'n': 106 nflag = 1; 107 break; 108 case 'o': 109 oflag = 1; 110 break; 111 case 'w': 112 /* compatibility */ 113 /* ignored */ 114 break; 115 case 'X': 116 /* compatibility */ 117 aflag = xflag = 1; 118 break; 119 case 'x': 120 xflag = 1; 121 break; 122 default: 123 usage(); 124 } 125 } 126 argc -= optind; 127 argv += optind; 128 129 if (Nflag && nflag) 130 usage(); 131 if (aflag && argc == 0) 132 exit(sysctl_all(0, 0)); 133 if (argc == 0) 134 usage(); 135 while (argc-- > 0) 136 parse(*argv++); 137 exit(0); 138 } 139 140 /* 141 * Parse a name into a MIB entry. 142 * Lookup and print out the MIB entry if it exists. 143 * Set a new value if requested. 144 */ 145 static void 146 parse(const char *string) 147 { 148 size_t len; 149 int i, j; 150 void *newval = 0; 151 int intval; 152 unsigned int uintval; 153 long longval; 154 unsigned long ulongval; 155 size_t newsize = 0; 156 quad_t quadval; 157 int mib[CTL_MAXNAME]; 158 char *cp, *bufp, buf[BUFSIZ], fmt[BUFSIZ]; 159 u_int kind; 160 161 bufp = buf; 162 snprintf(buf, BUFSIZ, "%s", string); 163 if ((cp = strchr(string, '=')) != NULL) { 164 *strchr(buf, '=') = '\0'; 165 *cp++ = '\0'; 166 while (isspace(*cp)) 167 cp++; 168 newval = cp; 169 newsize = strlen(cp); 170 } 171 172 len = CTL_MAXNAME; 173 if (sysctlnametomib(bufp, mib, &len) < 0) { 174 if (errno == ENOENT) { 175 errx(1, "unknown oid '%s'", bufp); 176 } else { 177 err(1, "sysctlnametomib(\"%s\")", bufp); 178 } 179 } 180 181 if (oidfmt(mib, len, fmt, &kind)) 182 err(1, "couldn't find format of oid '%s'", bufp); 183 184 if (newval == NULL) { 185 if ((kind & CTLTYPE) == CTLTYPE_NODE) { 186 sysctl_all(mib, len); 187 } else { 188 i = show_var(mib, len); 189 if (!i && !bflag) 190 putchar('\n'); 191 } 192 } else { 193 if ((kind & CTLTYPE) == CTLTYPE_NODE) 194 errx(1, "oid '%s' isn't a leaf node", bufp); 195 196 if (!(kind&CTLFLAG_WR)) 197 errx(1, "oid '%s' is read only", bufp); 198 199 switch (kind & CTLTYPE) { 200 case CTLTYPE_INT: 201 intval = (int) strtol(newval, NULL, 0); 202 newval = &intval; 203 newsize = sizeof(intval); 204 break; 205 case CTLTYPE_UINT: 206 uintval = (int) strtoul(newval, NULL, 0); 207 newval = &uintval; 208 newsize = sizeof uintval; 209 break; 210 case CTLTYPE_LONG: 211 longval = strtol(newval, NULL, 0); 212 newval = &longval; 213 newsize = sizeof longval; 214 break; 215 case CTLTYPE_ULONG: 216 ulongval = strtoul(newval, NULL, 0); 217 newval = &ulongval; 218 newsize = sizeof ulongval; 219 break; 220 case CTLTYPE_STRING: 221 break; 222 case CTLTYPE_QUAD: 223 sscanf(newval, "%qd", &quadval); 224 newval = &quadval; 225 newsize = sizeof(quadval); 226 break; 227 case CTLTYPE_OPAQUE: 228 if (strcmp(fmt, "T,dev_t") == 0) { 229 set_T_dev_t ((char*)newval, &newval, &newsize); 230 break; 231 } 232 /* FALLTHROUGH */ 233 default: 234 errx(1, "oid '%s' is type %d," 235 " cannot set that", bufp, 236 kind & CTLTYPE); 237 } 238 239 i = show_var(mib, len); 240 if (sysctl(mib, len, 0, 0, newval, newsize) == -1) { 241 if (!i && !bflag) 242 putchar('\n'); 243 switch (errno) { 244 case EOPNOTSUPP: 245 errx(1, "%s: value is not available", 246 string); 247 case ENOTDIR: 248 errx(1, "%s: specification is incomplete", 249 string); 250 case ENOMEM: 251 errx(1, "%s: type is unknown to this program", 252 string); 253 default: 254 warn("%s", string); 255 return; 256 } 257 } 258 if (!bflag) 259 printf(" -> "); 260 i = nflag; 261 nflag = 1; 262 j = show_var(mib, len); 263 if (!j && !bflag) 264 putchar('\n'); 265 nflag = i; 266 } 267 } 268 269 /* These functions will dump out various interesting structures. */ 270 271 static int 272 S_clockinfo(int l2, void *p) 273 { 274 struct clockinfo *ci = (struct clockinfo*)p; 275 if (l2 != sizeof(*ci)) 276 err(1, "S_clockinfo %d != %d", l2, sizeof(*ci)); 277 printf("{ hz = %d, tick = %d, tickadj = %d, profhz = %d, stathz = %d }", 278 ci->hz, ci->tick, ci->tickadj, ci->profhz, ci->stathz); 279 return (0); 280 } 281 282 static int 283 S_loadavg(int l2, void *p) 284 { 285 struct loadavg *tv = (struct loadavg*)p; 286 287 if (l2 != sizeof(*tv)) 288 err(1, "S_loadavg %d != %d", l2, sizeof(*tv)); 289 290 printf("{ %.2f %.2f %.2f }", 291 (double)tv->ldavg[0]/(double)tv->fscale, 292 (double)tv->ldavg[1]/(double)tv->fscale, 293 (double)tv->ldavg[2]/(double)tv->fscale); 294 return (0); 295 } 296 297 static int 298 S_timeval(int l2, void *p) 299 { 300 struct timeval *tv = (struct timeval*)p; 301 time_t tv_sec; 302 char *p1, *p2; 303 304 if (l2 != sizeof(*tv)) 305 err(1, "S_timeval %d != %d", l2, sizeof(*tv)); 306 printf("{ sec = %ld, usec = %ld } ", 307 tv->tv_sec, tv->tv_usec); 308 tv_sec = tv->tv_sec; 309 p1 = strdup(ctime(&tv_sec)); 310 for (p2=p1; *p2 ; p2++) 311 if (*p2 == '\n') 312 *p2 = '\0'; 313 fputs(p1, stdout); 314 return (0); 315 } 316 317 static int 318 T_dev_t(int l2, void *p) 319 { 320 dev_t *d = (dev_t *)p; 321 if (l2 != sizeof(*d)) 322 err(1, "T_dev_T %d != %d", l2, sizeof(*d)); 323 if ((int)(*d) != -1) { 324 if (minor(*d) > 255 || minor(*d) < 0) 325 printf("{ major = %d, minor = 0x%x }", 326 major(*d), minor(*d)); 327 else 328 printf("{ major = %d, minor = %d }", 329 major(*d), minor(*d)); 330 } 331 return (0); 332 } 333 334 static void 335 set_T_dev_t(const char *path, void **val, int *size) 336 { 337 static struct stat statb; 338 339 if (strcmp(path, "none") && strcmp(path, "off")) { 340 int rc = stat (path, &statb); 341 if (rc) { 342 err(1, "cannot stat %s", path); 343 } 344 345 if (!S_ISCHR(statb.st_mode)) { 346 errx(1, "must specify a device special file."); 347 } 348 } else { 349 statb.st_rdev = NODEV; 350 } 351 *val = (char*) &statb.st_rdev; 352 *size = sizeof statb.st_rdev; 353 } 354 355 /* 356 * These functions uses a presently undocumented interface to the kernel 357 * to walk the tree and get the type so it can print the value. 358 * This interface is under work and consideration, and should probably 359 * be killed with a big axe by the first person who can find the time. 360 * (be aware though, that the proper interface isn't as obvious as it 361 * may seem, there are various conflicting requirements. 362 */ 363 364 static int 365 oidfmt(int *oid, size_t len, char *fmt, u_int *kind) 366 { 367 int qoid[CTL_MAXNAME+2]; 368 u_char buf[BUFSIZ]; 369 int i; 370 size_t j; 371 372 qoid[0] = 0; 373 qoid[1] = 4; 374 memcpy(qoid + 2, oid, len * sizeof(int)); 375 376 j = sizeof(buf); 377 i = sysctl(qoid, len + 2, buf, &j, 0, 0); 378 if (i) 379 err(1, "sysctl fmt %d %d %d", i, j, errno); 380 381 if (kind) 382 *kind = *(u_int *)buf; 383 384 if (fmt) 385 strcpy(fmt, (char *)(buf + sizeof(u_int))); 386 return 0; 387 } 388 389 #ifdef __i386__ 390 /* 391 * Code to map a bootdev major number into a suitable device name. 392 * Major numbers are mapped into names as in boot2.c 393 */ 394 struct _foo { 395 int majdev; 396 const char *name; 397 } maj2name[] = { 398 { 30, "ad" }, 399 { 0, "wd" }, 400 { 1, "wfd" }, 401 { 2, "fd" }, 402 { 4, "da" }, 403 { -1, NULL } /* terminator */ 404 }; 405 406 static int 407 machdep_bootdev(u_long value) 408 { 409 int majdev, unit, slice, part; 410 struct _foo *p; 411 412 if ((value & B_MAGICMASK) != B_DEVMAGIC) { 413 printf("invalid (0x%08lx)", value); 414 return 0; 415 } 416 majdev = B_TYPE(value); 417 unit = B_UNIT(value); 418 slice = B_SLICE(value); 419 part = B_PARTITION(value); 420 if (majdev == 2) { /* floppy, as known to the boot block... */ 421 printf("/dev/fd%d", unit); 422 return 0; 423 } 424 for (p = maj2name; p->name != NULL && p->majdev != majdev ; p++) ; 425 if (p->name != NULL) { /* found */ 426 if (slice == WHOLE_DISK_SLICE) 427 printf("/dev/%s%d%c", p->name, unit, part); 428 else 429 printf("/dev/%s%ds%d%c", 430 p->name, unit, slice - BASE_SLICE + 1, part + 'a'); 431 } else 432 printf("unknown (major %d unit %d slice %d part %d)", 433 majdev, unit, slice, part); 434 return 0; 435 } 436 #endif 437 438 /* 439 * This formats and outputs the value of one variable 440 * 441 * Returns zero if anything was actually output. 442 * Returns one if didn't know what to do with this. 443 * Return minus one if we had errors. 444 */ 445 446 static int 447 show_var(int *oid, size_t nlen) 448 { 449 u_char buf[BUFSIZ], *val, *p; 450 char name[BUFSIZ], *fmt; 451 const char *sep, *spacer; 452 int qoid[CTL_MAXNAME+2]; 453 int i; 454 size_t j, len; 455 u_int kind; 456 int (*func)(int, void *); 457 458 qoid[0] = 0; 459 memcpy(qoid + 2, oid, nlen * sizeof(int)); 460 461 qoid[1] = 1; 462 j = sizeof(name); 463 i = sysctl(qoid, nlen + 2, name, &j, 0, 0); 464 if (i || !j) 465 err(1, "sysctl name %d %d %d", i, j, errno); 466 467 if (Nflag) { 468 printf("%s", name); 469 return (0); 470 } 471 472 if (eflag) 473 sep = "="; 474 else 475 sep = ": "; 476 477 if (dflag) { /* just print description */ 478 qoid[1] = 5; 479 j = sizeof(buf); 480 i = sysctl(qoid, nlen + 2, buf, &j, 0, 0); 481 if (!nflag) 482 printf("%s%s", name, sep); 483 printf("%s", buf); 484 return(0); 485 } 486 /* find an estimate of how much we need for this var */ 487 j = 0; 488 i = sysctl(oid, nlen, 0, &j, 0, 0); 489 j += j; /* we want to be sure :-) */ 490 491 val = alloca(j + 1); 492 len = j; 493 i = sysctl(oid, nlen, val, &len, 0, 0); 494 if (i || !len) 495 return (1); 496 497 if (bflag) { 498 fwrite(val, 1, len, stdout); 499 return (0); 500 } 501 502 val[len] = '\0'; 503 fmt = buf; 504 oidfmt(oid, nlen, fmt, &kind); 505 p = val; 506 switch (*fmt) { 507 case 'A': 508 if (!nflag) 509 printf("%s%s", name, sep); 510 fwrite(p, len, 1, stdout); 511 return (0); 512 513 case 'I': 514 if (!nflag) 515 printf("%s%s", name, sep); 516 fmt++; 517 spacer = ""; 518 while (len >= sizeof(int)) { 519 if(*fmt == 'U') 520 printf("%s%u", spacer, *(unsigned int *)p); 521 else 522 printf("%s%d", spacer, *(int *)p); 523 spacer = " "; 524 len -= sizeof(int); 525 p += sizeof(int); 526 } 527 return (0); 528 529 case 'L': 530 if (!nflag) 531 printf("%s%s", name, sep); 532 fmt++; 533 #ifdef __i386__ 534 if (!strcmp(name, "machdep.guessed_bootdev")) 535 return machdep_bootdev(*(unsigned long *)p); 536 #endif 537 spacer = ""; 538 while (len >= sizeof(long)) { 539 if(*fmt == 'U') 540 printf("%s%lu", spacer, *(unsigned long *)p); 541 else 542 printf("%s%ld", spacer, *(long *)p); 543 spacer = " "; 544 len -= sizeof(long); 545 p += sizeof(long); 546 } 547 return (0); 548 549 case 'P': 550 if (!nflag) 551 printf("%s%s", name, sep); 552 printf("%p", *(void **)p); 553 return (0); 554 555 case 'Q': 556 if (!nflag) 557 printf("%s%s", name, sep); 558 fmt++; 559 spacer = ""; 560 while (len >= sizeof(int64_t)) { 561 if(*fmt == 'U') 562 printf("%s%"PRIu64, spacer, *(uint64_t *)p); 563 else 564 printf("%s%"PRId64, spacer, *(int64_t *)p); 565 spacer = " "; 566 len -= sizeof(int64_t); 567 p += sizeof(int64_t); 568 } 569 return (0); 570 571 case 'T': 572 case 'S': 573 if (!oflag && !xflag) { 574 i = 0; 575 if (strcmp(fmt, "S,clockinfo") == 0) 576 func = S_clockinfo; 577 else if (strcmp(fmt, "S,timeval") == 0) 578 func = S_timeval; 579 else if (strcmp(fmt, "S,loadavg") == 0) 580 func = S_loadavg; 581 else if (strcmp(fmt, "T,dev_t") == 0) 582 func = T_dev_t; 583 else 584 func = NULL; 585 if (func) { 586 if (!nflag) 587 printf("%s%s", name, sep); 588 return ((*func)(len, p)); 589 } 590 } 591 /* FALL THROUGH */ 592 default: 593 if (!oflag && !xflag) 594 return (1); 595 if (!nflag) 596 printf("%s%s", name, sep); 597 printf("Format:%s Length:%d Dump:0x", fmt, len); 598 while (len-- && (xflag || p < val + 16)) 599 printf("%02x", *p++); 600 if (!xflag && len > 16) 601 printf("..."); 602 return (0); 603 } 604 return (1); 605 } 606 607 static int 608 sysctl_all(int *oid, size_t len) 609 { 610 int name1[22], name2[22]; 611 int retval; 612 size_t i, l1, l2; 613 614 name1[0] = 0; 615 name1[1] = 2; 616 l1 = 2; 617 if (len) { 618 memcpy(name1+2, oid, len * sizeof(int)); 619 l1 += len; 620 } else { 621 name1[2] = 1; 622 l1++; 623 } 624 for (;;) { 625 l2 = sizeof(name2); 626 retval = sysctl(name1, l1, name2, &l2, 0, 0); 627 if (retval < 0) { 628 if (errno == ENOENT) 629 return 0; 630 else 631 err(1, "sysctl(getnext) %d %d", retval, l2); 632 } 633 634 l2 /= sizeof(int); 635 636 if (l2 < len) 637 return 0; 638 639 for (i = 0; i < len; i++) 640 if (name2[i] != oid[i]) 641 return 0; 642 643 retval = show_var(name2, l2); 644 if (retval == 0 && !bflag) 645 putchar('\n'); 646 647 memcpy(name1+2, name2, l2 * sizeof(int)); 648 l1 = 2 + l2; 649 } 650 } 651