]> git.saurik.com Git - apple/system_cmds.git/blob - sysctl.tproj/sysctl.c
system_cmds-550.6.tar.gz
[apple/system_cmds.git] / sysctl.tproj / sysctl.c
1 /*
2 * Copyright (c) 1999-2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * Copyright (c) 1993
25 * The Regents of the University of California. All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 4. Neither the name of the University nor the names of its contributors
36 * may be used to endorse or promote products derived from this software
37 * without specific prior written permission.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
40 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
43 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
45 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
47 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
48 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
49 * SUCH DAMAGE.
50 */
51
52 /*
53 Modified November 1, 2000, by Ryan Rempel, ryan.rempel@utoronto.ca
54
55 The Darwin sysctl mechanism is in a state of flux. Parts of the kernel use the old
56 style of BSD sysctl definition, and other parts use the new style. The sysctl (8)
57 command that shipped with Darwin 1.2 (OS X PB) did not allow you to access
58 all possible sysctl values. In particular, it did not permit access to sysctl values
59 created by kernel extensions--hence my particular interest. The freeBSD sysctl (8)
60 command compiled and ran under Darwin 1.2, and it did permit access to
61 sysctl values created by kernel extensions, as well as several others. However, it
62 did not permit access to many other values which the Darwin 1.2 sysctl could access.
63
64 What I have done is merge the Darwin 1.2 sysctl and the freeBSD sysctl. Essentially,
65 there are two points of merger. When showing all values (i.e. -a, -A, or -X), sysctl now
66 runs the Darwin 1.2 routine to show all values, and then the freeBSD routine. This does
67 result in some duplication. When getting or setting a particular value, sysctl now tries
68 the freeBSD way first. If it cannot find the value, then it tries the Darwin 1.2 way.
69
70 There are a few oddities which this creates (aside from some duplication with -a, -A,
71 and -X). The freeBSD version of sysctl now supports two extra options, -b and -X.
72 In this syctl, those options are supported where the value is retrieved by the freeBSD
73 routine, and have no effect where the value is retrieved by the Darwin 1.2 routine.
74 The freeBSD sysctl uses a ':' to separate the name and the value, whereas Darwin 1.2's
75 sysctl uses a '='. I have left this way, as it lets you know which routine was used,
76 should it matter.
77
78 I have also fixed several lines which gave warnings previously, one of which appears
79 to have been an actual bug (bufp was dereferenced when it shouldn't have been).
80 I have also incoporated my previous patch to permit setting kern.hostid as an unsigned
81 integer. In the freeBSD side of the code, I have incorporated a general fix for
82 setting values where the format is specified as unsigned integer.
83 */
84
85 #include <sys/cdefs.h>
86 #ifndef lint
87 __unused static char copyright[] =
88 "@(#) Copyright (c) 1993\n\
89 The Regents of the University of California. All rights reserved.\n";
90 #endif /* not lint */
91
92 #ifndef lint
93 __unused static char sccsid[] = "@(#)sysctl.c 8.5 (Berkeley) 5/9/95";
94 #endif /* not lint */
95
96 #include <sys/param.h>
97 #include <sys/gmon.h>
98 #include <sys/mount.h>
99 #include <sys/stat.h>
100 #include <sys/sysctl.h>
101 #include <sys/socket.h>
102 #ifdef __APPLE__
103 #include <mach/machine/vm_param.h>
104 #include <mach/machine/vm_types.h>
105 #include <mach/mach_types.h>
106 #else
107 #include <vm/vm_param.h>
108 #endif /* __APPLE__ */
109
110 #include <errno.h>
111 #include <ctype.h>
112 #include <unistd.h>
113 #include <stdio.h>
114 #include <stdlib.h>
115 #include <string.h>
116
117 #include <sys/types.h>
118 #include <sys/resource.h>
119 #include <err.h>
120
121 struct ctlname topname[] = CTL_NAMES;
122 struct ctlname kernname[] = CTL_KERN_NAMES;
123 struct ctlname vmname[] = CTL_VM_NAMES;
124 struct ctlname hwname[] = CTL_HW_NAMES;
125 struct ctlname username[] = CTL_USER_NAMES;
126 struct ctlname debugname[CTL_DEBUG_MAXID];
127 struct ctlname *vfsname;
128 #ifdef CTL_MACHDEP_NAMES
129 struct ctlname machdepname[] = CTL_MACHDEP_NAMES;
130 #endif
131 char names[BUFSIZ];
132 int lastused;
133
134 struct list {
135 struct ctlname *list;
136 int size;
137 };
138 struct list toplist = { topname, CTL_MAXID };
139 struct list secondlevel[] = {
140 { 0, 0 }, /* CTL_UNSPEC */
141 { kernname, KERN_MAXID }, /* CTL_KERN */
142 { vmname, VM_MAXID }, /* CTL_VM */
143 { 0, 0 }, /* CTL_VFS */
144 { 0, 0 }, /* CTL_NET */
145 { 0, CTL_DEBUG_MAXID }, /* CTL_DEBUG */
146 { hwname, HW_MAXID }, /* CTL_HW */
147 #ifdef CTL_MACHDEP_NAMES
148 { machdepname, CPU_MAXID }, /* CTL_MACHDEP */
149 #else
150 { 0, 0 }, /* CTL_MACHDEP */
151 #endif
152 { username, USER_MAXID }, /* CTL_USER_NAMES */
153 };
154
155 static int Aflag, aflag, bflag, hflag, nflag, wflag, Xflag;
156 static int foundSome = 0;
157 static int invalid_name_used = 0;
158
159 void listall(char *prefix, struct list *lp);
160 void old_parse(char *string, int flags);
161 void debuginit();
162 void vfsinit();
163 int findname(char *string, char *level, char **bufp, struct list *namelist);
164 void usage();
165
166 static void parse(char *string, int flags);
167 static int oidfmt(int *, int, char *, u_int *);
168 static int show_var(int *, int, int);
169 static int sysctl_all (int *oid, int len);
170 static int name2oid(char *, int *);
171
172 /*
173 * Variables requiring special processing.
174 */
175 #define CLOCK 0x00000001
176 #define BOOTTIME 0x00000002
177 #define CONSDEV 0x00000004
178
179 int
180 main(argc, argv)
181 int argc;
182 char *argv[];
183 {
184 // extern char *optarg; // unused
185 extern int optind;
186 int ch, lvl1;
187
188 while ((ch = getopt(argc, argv, "AabnwX")) != EOF) {
189 switch (ch) {
190 case 'A': Aflag = 1; break;
191 case 'a': aflag = 1; break;
192 case 'b': bflag = 1; break;
193 case 'h': hflag = 1; break;
194 case 'n': nflag = 1; break;
195 case 'w': wflag = 1; break;
196 case 'X': Xflag = Aflag = 1; break;
197 default: usage();
198 }
199 }
200 argc -= optind;
201 argv += optind;
202
203 if (argc == 0 && (Aflag || aflag)) {
204 debuginit();
205 vfsinit();
206 for (lvl1 = 1; lvl1 < CTL_MAXID; lvl1++)
207 listall(topname[lvl1].ctl_name, &secondlevel[lvl1]);
208 exit (sysctl_all(0, 0));
209 }
210 if (argc == 0)
211 usage();
212 for (; *argv != NULL; ++argv)
213 parse(*argv, 1);
214 exit(invalid_name_used ? 1 : 0);
215 }
216
217 /*
218 * List all variables known to the system.
219 */
220 void
221 listall(prefix, lp)
222 char *prefix;
223 struct list *lp;
224 {
225 int lvl2;
226 char *cp, name[BUFSIZ];
227
228 if (lp->list == 0)
229 return;
230 strcpy(name, prefix);
231 cp = &name[strlen(name)];
232 *cp++ = '.';
233 for (lvl2 = 0; lvl2 < lp->size; lvl2++) {
234 if (lp->list[lvl2].ctl_name == 0)
235 continue;
236 strcpy(cp, lp->list[lvl2].ctl_name);
237 old_parse(name, Aflag);
238 }
239 }
240
241 /*
242 * Parse a name into a MIB entry.
243 * Lookup and print out the MIB entry if it exists.
244 * Set a new value if requested.
245 */
246 void
247 old_parse(string, flags)
248 char *string;
249 int flags;
250 {
251 int indx, type, state, len;
252 size_t size;
253 int special = 0;
254 void *newval = 0;
255 int intval, newsize = 0;
256 unsigned int uintval;
257 int useUnsignedInt = 0;
258 quad_t quadval;
259 struct list *lp;
260 struct vfsconf vfc;
261 int mib[CTL_MAXNAME];
262 char *cp, *bufp, buf[BUFSIZ] /*, strval[BUFSIZ] */ ;
263
264 bufp = buf;
265 snprintf(buf, BUFSIZ, "%s", string);
266 if ((cp = strchr(string, '=')) != NULL) {
267 if (!wflag) {
268 fprintf(stderr, "Must specify -w to set variables\n");
269 exit(2);
270 }
271 *strchr(buf, '=') = '\0';
272 *cp++ = '\0';
273 while (isspace(*cp))
274 cp++;
275 newval = cp;
276 newsize = strlen(cp);
277 }
278 if ((indx = findname(string, "top", &bufp, &toplist)) == -1)
279 return;
280 mib[0] = indx;
281 if (indx == CTL_VFS)
282 vfsinit();
283 if (indx == CTL_DEBUG)
284 debuginit();
285 lp = &secondlevel[indx];
286 if (lp->list == 0) {
287 if (!foundSome) fprintf(stderr, "%s: class is not implemented\n",
288 topname[indx].ctl_name);
289 return;
290 }
291 if (bufp == NULL) {
292 listall(topname[indx].ctl_name, lp);
293 return;
294 }
295 if ((indx = findname(string, "second", &bufp, lp)) == -1)
296 return;
297 mib[1] = indx;
298 type = lp->list[indx].ctl_type;
299 len = 2;
300 switch (mib[0]) {
301
302 case CTL_KERN:
303 switch (mib[1]) {
304 case KERN_PROF:
305 mib[2] = GPROF_STATE;
306 size = sizeof state;
307 if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) {
308 if (flags == 0)
309 return;
310 if (!nflag)
311 fprintf(stdout, "%s: ", string);
312 fprintf(stderr,
313 "kernel is not compiled for profiling\n");
314 return;
315 }
316 if (!nflag)
317 fprintf(stdout, "%s: %s\n", string,
318 state == GMON_PROF_OFF ? "off" : "running");
319 return;
320 case KERN_VNODE:
321 case KERN_FILE:
322 if (flags == 0)
323 return;
324 fprintf(stderr,
325 "Use pstat to view %s information\n", string);
326 return;
327 case KERN_PROC:
328 if (flags == 0)
329 return;
330 fprintf(stderr,
331 "Use ps to view %s information\n", string);
332 return;
333 case KERN_CLOCKRATE:
334 special |= CLOCK;
335 break;
336 case KERN_BOOTTIME:
337 special |= BOOTTIME;
338 break;
339 case KERN_HOSTID:
340 useUnsignedInt = 1;
341 break;
342 }
343 break;
344
345 case CTL_HW:
346 useUnsignedInt = 1;
347 break;
348
349 case CTL_VM: break;
350 #if 0 /* XXX Handled by the new sysctl mechanism */
351 switch (mib[1]) {
352 case VM_LOADAVG: { /* XXX this is bogus */
353 double loads[3];
354
355 getloadavg(loads, 3);
356 if (!nflag)
357 fprintf(stdout, "%s: ", string);
358 fprintf(stdout, "%.2f %.2f %.2f\n",
359 loads[0], loads[1], loads[2]);
360 return;
361 }
362 case VM_SWAPUSAGE: {
363 struct xsw_usage xsu;
364 int saved_errno;
365
366 size = sizeof (xsu);
367 if (sysctl(mib, 2, &xsu, &size, NULL, 0) != 0) {
368 if (flags == 0)
369 return;
370 saved_errno = errno;
371 if (!nflag)
372 fprintf(stderr, "%s: ", string);
373 fprintf(stderr, "sysctl(VM_SWAPUSAGE): %s\n",
374 strerror(saved_errno));
375 return;
376 }
377
378 if (!nflag)
379 fprintf(stdout, "%s: ", string);
380 fprintf(stdout,
381 "total = %.2fM used = %.2fM free = %.2fM %s\n",
382 ((double) xsu.xsu_total) / (1024.0 * 1024.0),
383 ((double) xsu.xsu_used) / (1024.0 * 1024.0),
384 ((double) xsu.xsu_avail) / (1024.0 * 1024.0),
385 xsu.xsu_encrypted ? "(encrypted)" : "");
386 return;
387 }
388 }
389 if (flags == 0)
390 return;
391 fprintf(stderr,
392 "Use vmstat or systat to view %s information\n", string);
393 return;
394 #endif
395
396 case CTL_DEBUG:
397 mib[2] = CTL_DEBUG_VALUE;
398 len = 3;
399 break;
400
401 case CTL_MACHDEP:
402 #ifdef CPU_CONSDEV
403 if (mib[1] == CPU_CONSDEV)
404 special |= CONSDEV;
405 #endif
406 break;
407
408 case CTL_VFS:
409 mib[3] = mib[1];
410 mib[1] = VFS_GENERIC;
411 mib[2] = VFS_CONF;
412 len = 4;
413 size = sizeof vfc;
414 if (sysctl(mib, 4, &vfc, &size, (void *)0, (size_t)0) < 0) {
415 perror("vfs print");
416 return;
417 }
418 if (flags == 0 && vfc.vfc_refcount == 0)
419 return;
420 if (!nflag)
421 fprintf(stdout, "%s has %d mounted instance%s\n",
422 string, vfc.vfc_refcount,
423 vfc.vfc_refcount != 1 ? "s" : "");
424 else
425 fprintf(stdout, "%d\n", vfc.vfc_refcount);
426 return;
427
428 case CTL_USER:
429 break;
430
431 default:
432 fprintf(stderr, "Illegal top level value: %d\n", mib[0]);
433 return;
434
435 }
436 if (bufp) {
437 fprintf(stderr, "name %s in %s is unknown\n", bufp, string);
438 return;
439 }
440 if (newsize > 0) {
441 switch (type) {
442 case CTLTYPE_INT:
443 if (useUnsignedInt) {
444 uintval = strtoul(newval, NULL, 0);
445 if ((uintval == 0) && (errno == EINVAL)) {
446 fprintf(stderr, "invalid argument: %s\n",
447 (char *)newval);
448 return;
449 }
450 newval = &uintval;
451 newsize = sizeof uintval;
452 } else {
453 intval = strtol(newval, NULL, 0);
454 if ((intval == 0) && (errno == EINVAL)) {
455 fprintf(stderr, "invalid argument: %s\n",
456 (char *)newval);
457 return;
458 }
459 newval = &intval;
460 newsize = sizeof intval;
461 }
462 break;
463
464 case CTLTYPE_QUAD:
465 quadval = strtoq(newval, NULL, 0);
466 if ((quadval == 0) && (errno == EINVAL)) {
467 fprintf(stderr, "invalid argument: %s\n",
468 (char *)newval);
469 return;
470 }
471 newval = &quadval;
472 newsize = sizeof quadval;
473 break;
474 }
475 }
476 size = BUFSIZ;
477 if (sysctl(mib, len, buf, &size, newsize ? newval : 0, newsize) == -1) {
478 if (flags == 0)
479 return;
480 switch (errno) {
481 case ENOTSUP:
482 fprintf(stderr, "%s: value is not available\n", string);
483 return;
484 case ENOTDIR:
485 fprintf(stderr, "%s: specification is incomplete\n",
486 string);
487 return;
488 case ENOMEM:
489 fprintf(stderr, "%s: type is unknown to this program\n",
490 string);
491 return;
492 case ENOENT:
493 fprintf(stderr, "%s: no such MIB\n",
494 string);
495 return;
496 default:
497 perror(string);
498 return;
499 }
500 }
501 if (special & CLOCK) {
502 struct clockinfo *clkp = (struct clockinfo *)buf;
503
504 if (!nflag)
505 fprintf(stdout, "%s: ", string);
506 fprintf(stdout,
507 "hz = %d, tick = %d, profhz = %d, stathz = %d\n",
508 clkp->hz, clkp->tick, clkp->profhz, clkp->stathz);
509 return;
510 }
511 if (special & BOOTTIME) {
512 struct timeval *btp = (struct timeval *)buf;
513
514 if (!nflag)
515 fprintf(stdout, "%s = %s\n", string,
516 ctime((time_t *) &btp->tv_sec));
517 else
518 fprintf(stdout, "%ld\n", btp->tv_sec);
519 return;
520 }
521 if (special & CONSDEV) {
522 dev_t dev = *(dev_t *)buf;
523
524 if (!nflag)
525 fprintf(stdout, "%s = %s\n", string,
526 devname(dev, S_IFCHR));
527 else
528 fprintf(stdout, "0x%x\n", dev);
529 return;
530 }
531 switch (type) {
532 case CTLTYPE_INT:
533 if (newsize == 0) {
534 if (!nflag)
535 fprintf(stdout, "%s = ", string);
536 fprintf(stdout, useUnsignedInt ? "%u\n" : "%d\n", *(int *)buf);
537 } else {
538 if (!nflag)
539 fprintf(stdout, useUnsignedInt ? "%s: %u -> " : "%s: %d -> ",
540 string, *(int *)buf);
541 fprintf(stdout, useUnsignedInt ? "%u\n" : "%d\n", *(int *)newval);
542 }
543 return;
544
545 case CTLTYPE_STRING:
546 if (newsize == 0) {
547 if (!nflag)
548 fprintf(stdout, "%s = ", string);
549 fprintf(stdout, "%s\n", buf);
550 } else {
551 if (!nflag)
552 fprintf(stdout, "%s: %s -> ", string, buf);
553 fprintf(stdout, "%s\n", (char *) newval);
554 }
555 return;
556
557 case CTLTYPE_QUAD:
558 if (newsize == 0) {
559 if (!nflag)
560 fprintf(stdout, "%s = ", string);
561 fprintf(stdout, "%qd\n", *(quad_t *)buf);
562 } else {
563 if (!nflag)
564 fprintf(stdout, "%s: %qd -> ", string,
565 *(quad_t *)buf);
566 fprintf(stdout, "%qd\n", *(quad_t *)newval);
567 }
568 return;
569
570 case CTLTYPE_NODE:
571 case CTLTYPE_STRUCT:
572 return;
573
574 default:
575 fprintf(stderr, "%s: unknown type returned\n",
576 string);
577 return;
578 }
579 }
580
581 /*
582 * Initialize the set of debugging names
583 */
584 void debuginit()
585 {
586 int mib[3], loc, i;
587 size_t size;
588
589 if (secondlevel[CTL_DEBUG].list != 0)
590 return;
591 secondlevel[CTL_DEBUG].list = debugname;
592 mib[0] = CTL_DEBUG;
593 mib[2] = CTL_DEBUG_NAME;
594 for (loc = lastused, i = 0; i < CTL_DEBUG_MAXID; i++) {
595 mib[1] = i;
596 size = BUFSIZ - loc;
597 if (sysctl(mib, 3, &names[loc], &size, NULL, 0) == -1)
598 continue;
599 debugname[i].ctl_name = &names[loc];
600 debugname[i].ctl_type = CTLTYPE_INT;
601 loc += size;
602 }
603 lastused = loc;
604 }
605
606 /*
607 * Initialize the set of filesystem names
608 */
609 void vfsinit()
610 {
611 int mib[4], maxtypenum, cnt, loc, size;
612 struct vfsconf vfc;
613 size_t buflen;
614
615 if (secondlevel[CTL_VFS].list != 0)
616 return;
617 mib[0] = CTL_VFS;
618 mib[1] = VFS_GENERIC;
619 mib[2] = VFS_MAXTYPENUM;
620 buflen = 4;
621 if (sysctl(mib, 3, &maxtypenum, &buflen, (void *)0, (size_t)0) < 0)
622 return;
623 if ((vfsname = malloc(maxtypenum * sizeof(*vfsname))) == 0)
624 return;
625 memset(vfsname, 0, maxtypenum * sizeof(*vfsname));
626 mib[2] = VFS_CONF;
627 buflen = sizeof vfc;
628 for (loc = lastused, cnt = 0; cnt < maxtypenum; cnt++) {
629 mib[3] = cnt;
630 if (sysctl(mib, 4, &vfc, &buflen, (void *)0, (size_t)0) < 0) {
631 if (errno == ENOTSUP)
632 continue;
633 perror("vfsinit");
634 free(vfsname);
635 return;
636 }
637 strcat(&names[loc], vfc.vfc_name);
638 vfsname[cnt].ctl_name = &names[loc];
639 vfsname[cnt].ctl_type = CTLTYPE_INT;
640 size = strlen(vfc.vfc_name) + 1;
641 loc += size;
642 }
643 lastused = loc;
644 secondlevel[CTL_VFS].list = vfsname;
645 secondlevel[CTL_VFS].size = maxtypenum;
646 return;
647 }
648
649 /*
650 * Scan a list of names searching for a particular name.
651 */
652 int
653 findname(string, level, bufp, namelist)
654 char *string;
655 char *level;
656 char **bufp;
657 struct list *namelist;
658 {
659 char *name;
660 int i;
661
662 /* Make 'sysctl kern.' style behave the same as 'sysctl kern' 3360872*/
663 if (bufp[0][strlen(*bufp)-1] == '.')
664 bufp[0][strlen(*bufp)-1]='\0';
665 if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
666 if (!foundSome) {
667 fprintf(stderr, "%s: incomplete specification\n", string);
668 invalid_name_used = 1;
669 }
670 return (-1);
671 }
672 for (i = 0; i < namelist->size; i++)
673 if (namelist->list[i].ctl_name != NULL &&
674 strcmp(name, namelist->list[i].ctl_name) == 0)
675 break;
676 if (i == namelist->size) {
677 if (!foundSome) {
678 fprintf(stderr, "%s level name %s in %s is invalid\n",
679 level, name, string);
680 invalid_name_used = 1;
681 }
682 return (-1);
683 }
684 return (i);
685 }
686
687 void usage()
688 {
689
690 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
691 "usage: sysctl [-bn] variable ...",
692 " sysctl [-bn] -w variable=value ...",
693 " sysctl [-bn] -a",
694 " sysctl [-bn] -A",
695 " sysctl [-bn] -X");
696 exit(1);
697 }
698
699 /*
700 * Parse a name into a MIB entry.
701 * Lookup and print out the MIB entry if it exists.
702 * Set a new value if requested.
703 */
704 static void
705 parse(char *string, int flags)
706 {
707 int len, i, j;
708 void *newval = 0;
709 int intval, newsize = 0;
710 unsigned int uintval;
711 quad_t quadval;
712 int mib[CTL_MAXNAME];
713 char *cp, *bufp, buf[BUFSIZ], fmt[BUFSIZ];
714 u_int kind;
715
716 bufp = buf;
717 if (snprintf(buf, BUFSIZ, "%s", string) >= BUFSIZ)
718 errx(1, "MIB too long");
719 snprintf(buf, BUFSIZ, "%s", string);
720 if ((cp = strchr(string, '=')) != NULL) {
721 if (!wflag)
722 errx(2, "must specify -w to set variables");
723 *strchr(buf, '=') = '\0';
724 *cp++ = '\0';
725 while (isspace(*cp))
726 cp++;
727 newval = cp;
728 newsize = strlen(cp);
729 } else {
730 if (wflag)
731 usage();
732 }
733 len = name2oid(bufp, mib);
734
735 if (len < 0) {
736 if (cp != NULL) {
737 while (*cp != '\0') cp--;
738 *cp = '=';
739 }
740 old_parse (string, flags);
741 return;
742 }
743
744 /*
745 * An non-zero return here is an OID space containing parameters which
746 * needs to be ignored in the interests of backward compatibility with
747 * pre-newsysctl sysctls.
748 */
749 if (oidfmt(mib, len, fmt, &kind))
750 return;
751
752 if (!wflag) {
753 if ((kind & CTLTYPE) == CTLTYPE_NODE) {
754 sysctl_all(mib, len);
755 foundSome = 1;
756 old_parse (string, flags);
757 } else {
758 i = show_var(mib, len, 1);
759 if (!i && !bflag)
760 putchar('\n');
761 }
762 } else {
763 if ((kind & CTLTYPE) == CTLTYPE_NODE)
764 errx(1, "oid '%s' isn't a leaf node", bufp);
765
766 if (!(kind&CTLFLAG_WR))
767 errx(1, "oid '%s' is read only", bufp);
768
769 switch (kind & CTLTYPE) {
770 case CTLTYPE_INT:
771 if ((*fmt == 'I') && (*(fmt + 1) == 'U')) {
772 uintval = (unsigned int) strtoul (newval, NULL, 0);
773 if ((uintval == 0) &&
774 (errno == EINVAL)) {
775 errx(1, "invalid argument: %s",
776 (char *)newval);
777 return;
778 }
779 newval = &uintval;
780 newsize = sizeof uintval;
781 } else {
782 intval = (int) strtol(newval, NULL, 0);
783 if ((intval == 0) &&
784 (errno == EINVAL)) {
785 errx(1, "invalid argument: %s",
786 (char *)newval);
787 return;
788 }
789 newval = &intval;
790 newsize = sizeof intval;
791 }
792 break;
793 case CTLTYPE_STRING:
794 break;
795 case CTLTYPE_QUAD:
796 quadval = strtoq(newval, NULL, 0);
797 if ((quadval == 0) && (errno == EINVAL)) {
798 errx(1, "invalid argument %s", (char *)newval);
799 return;
800 }
801 newval = &quadval;
802 newsize = sizeof(quadval);
803 break;
804 default:
805 errx(1, "oid '%s' is type %d,"
806 " cannot set that", bufp,
807 kind & CTLTYPE);
808 }
809
810 i = show_var(mib, len, 1);
811 if (sysctl(mib, len, 0, 0, newval, newsize) == -1) {
812 if (!i && !bflag)
813 putchar('\n');
814 switch (errno) {
815 case ENOTSUP:
816 errx(1, "%s: value is not available",
817 string);
818 case ENOTDIR:
819 errx(1, "%s: specification is incomplete",
820 string);
821 case ENOMEM:
822 errx(1, "%s: type is unknown to this program",
823 string);
824 default:
825 warn("%s", string);
826 return;
827 }
828 }
829 if (!bflag)
830 printf(" -> ");
831 i = nflag;
832 nflag = 1;
833 j = show_var(mib, len, 1);
834 if (!j && !bflag)
835 putchar('\n');
836 nflag = i;
837 }
838 }
839
840 /* These functions will dump out various interesting structures. */
841
842 static int
843 S_clockinfo(int l2, void *p)
844 {
845 struct clockinfo *ci = (struct clockinfo*)p;
846
847 if (l2 != sizeof(*ci)) {
848 warnx("S_clockinfo %d != %ld", l2, sizeof(*ci));
849 return (1);
850 }
851 printf(hflag ? "{ hz = %'d, tick = %'d, tickadj = %'d, profhz = %'d, stathz = %'d }" :
852 "{ hz = %d, tick = %d, tickadj = %d, profhz = %d, stathz = %d }",
853 ci->hz, ci->tick, ci->tickadj, ci->profhz, ci->stathz);
854 return (0);
855 }
856
857 static int
858 S_loadavg(int l2, void *p)
859 {
860 struct loadavg *tv = (struct loadavg*)p;
861
862 if (l2 != sizeof(*tv)) {
863 warnx("S_loadavg %d != %ld", l2, sizeof(*tv));
864 return (1);
865 }
866 printf(hflag ? "{ %'.2f %'.2f %'.2f }" : "{ %.2f %.2f %.2f }",
867 (double)tv->ldavg[0]/(double)tv->fscale,
868 (double)tv->ldavg[1]/(double)tv->fscale,
869 (double)tv->ldavg[2]/(double)tv->fscale);
870 return (0);
871 }
872
873 static int
874 S_timeval(int l2, void *p)
875 {
876 struct timeval *tv = (struct timeval*)p;
877 time_t tv_sec;
878 char *p1, *p2;
879
880 if (l2 != sizeof(*tv)) {
881 warnx("S_timeval %d != %ld", l2, sizeof(*tv));
882 return (1);
883 }
884 printf(hflag ? "{ sec = %'jd, usec = %'ld } " :
885 "{ sec = %jd, usec = %ld } ",
886 (intmax_t)tv->tv_sec, (long)tv->tv_usec);
887 tv_sec = tv->tv_sec;
888 p1 = strdup(ctime(&tv_sec));
889 for (p2=p1; *p2 ; p2++)
890 if (*p2 == '\n')
891 *p2 = '\0';
892 fputs(p1, stdout);
893 free(p1);
894 return (0);
895 }
896
897 static int
898 S_xswusage(int l2, void *p)
899 {
900 struct xsw_usage *xsu = (struct xsw_usage *)p;
901
902 if (l2 != sizeof(*xsu)) {
903 warnx("S_xswusage %d != %ld", l2, sizeof(*xsu));
904 return (1);
905 }
906 fprintf(stdout,
907 "total = %.2fM used = %.2fM free = %.2fM %s",
908 ((double)xsu->xsu_total) / (1024.0 * 1024.0),
909 ((double)xsu->xsu_used) / (1024.0 * 1024.0),
910 ((double)xsu->xsu_avail) / (1024.0 * 1024.0),
911 xsu->xsu_encrypted ? "(encrypted)" : "");
912 return (0);
913 }
914
915 static int
916 T_dev_t(int l2, void *p)
917 {
918 dev_t *d = (dev_t *)p;
919
920 if (l2 != sizeof(*d)) {
921 warnx("T_dev_T %d != %ld", l2, sizeof(*d));
922 return (1);
923 }
924 if ((int)(*d) != -1) {
925 if (minor(*d) > 255 || minor(*d) < 0)
926 printf("{ major = %d, minor = 0x%x }",
927 major(*d), minor(*d));
928 else
929 printf("{ major = %d, minor = %d }",
930 major(*d), minor(*d));
931 }
932 return (0);
933 }
934
935 /*
936 * These functions uses a presently undocumented interface to the kernel
937 * to walk the tree and get the type so it can print the value.
938 * This interface is under work and consideration, and should probably
939 * be killed with a big axe by the first person who can find the time.
940 * (be aware though, that the proper interface isn't as obvious as it
941 * may seem, there are various conflicting requirements.
942 */
943
944 static int
945 name2oid(char *name, int *oidp)
946 {
947 int oid[2];
948 int i;
949 size_t j;
950
951 oid[0] = 0;
952 oid[1] = 3;
953
954 j = CTL_MAXNAME * sizeof (int);
955 i = sysctl(oid, 2, oidp, &j, name, strlen(name));
956 if (i < 0)
957 return i;
958 j /= sizeof (int);
959 return (j);
960 }
961
962 static int
963 oidfmt(int *oid, int len, char *fmt, u_int *kind)
964 {
965 int qoid[CTL_MAXNAME+2];
966 u_char buf[BUFSIZ];
967 int i;
968 size_t j;
969
970 qoid[0] = 0;
971 qoid[1] = 4;
972 memcpy(qoid + 2, oid, len * sizeof(int));
973
974 j = sizeof(buf);
975 i = sysctl(qoid, len + 2, buf, &j, 0, 0);
976 if (i) {
977 /*
978 * An ENOENT error return indicates that the OID in question
979 * is a node OID followed not by additional OID elements, but
980 * by integer parameters. We really do not want to support
981 * this type of thing going forward, but we alow it here for
982 * historical compatibility. Eventually, this will go away.
983 */
984 if (errno == ENOENT)
985 return ENOENT;
986 err(1, "sysctl fmt %d %ld %d", i, j, errno);
987 }
988
989 if (kind)
990 *kind = *(u_int *)buf;
991
992 if (fmt)
993 strcpy(fmt, (char *)(buf + sizeof(u_int)));
994 return (0);
995 }
996
997 /*
998 * This formats and outputs the value of one variable
999 *
1000 * Returns zero if anything was actually output.
1001 * Returns one if didn't know what to do with this.
1002 * Return minus one if we had errors.
1003 */
1004
1005 static int
1006 show_var(int *oid, int nlen, int show_masked)
1007 {
1008 u_char buf[BUFSIZ], *val, *mval, *p;
1009 char name[BUFSIZ], /* descr[BUFSIZ], */ *fmt;
1010 int qoid[CTL_MAXNAME+2];
1011 int i;
1012 int retval;
1013 size_t j, len;
1014 u_int kind;
1015 int (*func)(int, void *) = 0;
1016
1017 qoid[0] = 0;
1018 memcpy(qoid + 2, oid, nlen * sizeof(int));
1019
1020 qoid[1] = 1;
1021 j = sizeof name;
1022 i = sysctl(qoid, nlen + 2, name, &j, 0, 0);
1023 if (i || !j)
1024 err(1, "sysctl name %d %ld %d", i, j, errno);
1025
1026 /* find an estimate of how much we need for this var */
1027 j = 0;
1028 i = sysctl(oid, nlen, 0, &j, 0, 0);
1029 j += j; /* we want to be sure :-) */
1030
1031 val = mval = malloc(j);
1032 len = j;
1033 i = sysctl(oid, nlen, val, &len, 0, 0);
1034 if (i || !len) {
1035 retval = 1;
1036 goto RETURN;
1037 }
1038
1039 if (bflag) {
1040 fwrite(val, 1, len, stdout);
1041 retval = 0;
1042 goto RETURN;
1043 }
1044
1045 qoid[1] = 4;
1046 j = sizeof buf;
1047 i = sysctl(qoid, nlen + 2, buf, &j, 0, 0);
1048 /*
1049 * An ENOENT error return indicates that the OID in question
1050 * is a node OID followed not by additional OID elements, but
1051 * by integer parameters. We really do not want to support
1052 * this type of thing going forward, but we alow it here for
1053 * historical compatibility. Eventially, this will go away.
1054 */
1055 if (i && errno == ENOENT) {
1056 retval = 1;
1057 goto RETURN;
1058 }
1059
1060 if (i || !j)
1061 err(1, "sysctl fmt %d %ld %d", i, j, errno);
1062
1063 kind = *(u_int *)buf;
1064 if (!show_masked && (kind & CTLFLAG_MASKED)) {
1065 retval = 1;
1066 goto RETURN;
1067 }
1068
1069 fmt = (char *)(buf + sizeof(u_int));
1070
1071 p = val;
1072 switch (*fmt) {
1073 case '-':
1074 /* deprecated, do not print */
1075 retval = 0;
1076 goto RETURN;
1077
1078
1079 case 'A':
1080 if (!nflag)
1081 printf("%s: ", name);
1082 printf("%s", p);
1083 retval = 0;
1084 goto RETURN;
1085
1086 case 'I':
1087 if (!nflag)
1088 printf("%s: ", name);
1089 fmt++;
1090 val = (unsigned char *)"";
1091 while (len >= sizeof(int)) {
1092 if(*fmt == 'U')
1093 printf("%s%u", val, *(unsigned int *)p);
1094 else
1095 printf("%s%d", val, *(int *)p);
1096 val = (unsigned char *)" ";
1097 len -= sizeof (int);
1098 p += sizeof (int);
1099 }
1100 retval = 0;
1101 goto RETURN;
1102
1103 case 'L':
1104 if (!nflag)
1105 printf("%s: ", name);
1106 fmt++;
1107 val = (unsigned char *)"";
1108 while (len >= sizeof(long)) {
1109 if(*fmt == 'U')
1110 printf("%s%lu", val, *(unsigned long *)p);
1111 else
1112 printf("%s%ld", val, *(long *)p);
1113 val = (unsigned char *)" ";
1114 len -= sizeof (long);
1115 p += sizeof (long);
1116 }
1117 retval = 0;
1118 goto RETURN;
1119
1120 case 'P':
1121 if (!nflag)
1122 printf("%s: ", name);
1123 printf("%p", *(void **)p);
1124 retval = 0;
1125 goto RETURN;
1126
1127 case 'Q':
1128 if (!nflag)
1129 printf("%s: ", name);
1130 fmt++;
1131 val = (unsigned char *)"";
1132 while (len >= sizeof(long long)) {
1133 if(*fmt == 'U')
1134 printf("%s%llu", val, *(unsigned long long *)p);
1135 else
1136 printf("%s%lld", val, *(long long *)p);
1137 val = (unsigned char *)" ";
1138 len -= sizeof (long long);
1139 p += sizeof (long long);
1140 }
1141 retval = 0;
1142 goto RETURN;
1143
1144
1145 case 'T':
1146 case 'S':
1147 i = 0;
1148 if (!strcmp(fmt, "S,clockinfo")) func = S_clockinfo;
1149 else if (!strcmp(fmt, "S,timeval")) func = S_timeval;
1150 else if (!strcmp(fmt, "S,loadavg")) func = S_loadavg;
1151 else if (!strcmp(fmt, "S,xsw_usage")) func = S_xswusage;
1152 else if (!strcmp(fmt, "T,dev_t")) func = T_dev_t;
1153 if (func) {
1154 if (!nflag)
1155 printf("%s: ", name);
1156 retval = (*func)(len, p);
1157 goto RETURN;
1158 }
1159 /* FALL THROUGH */
1160 default:
1161 if (!Aflag) {
1162 retval = 1;
1163 goto RETURN;
1164 }
1165 if (!nflag)
1166 printf("%s: ", name);
1167 printf("Format:%s Length:%ld Dump:0x", fmt, len);
1168 while (len--) {
1169 printf("%02x", *p++);
1170 if (Xflag || p < val+16)
1171 continue;
1172 printf("...");
1173 break;
1174 }
1175 retval = 0;
1176 goto RETURN;
1177 }
1178
1179 retval = 1;
1180 RETURN:
1181 free(mval);
1182 return (retval);
1183 }
1184
1185 static int
1186 sysctl_all (int *oid, int len)
1187 {
1188 int name1[22], name2[22];
1189 int i, j;
1190 size_t l1, l2;
1191
1192 name1[0] = 0;
1193 name1[1] = 2;
1194 l1 = 2;
1195 if (len) {
1196 memcpy(name1+2, oid, len*sizeof (int));
1197 l1 += len;
1198 } else {
1199 name1[2] = 1;
1200 l1++;
1201 }
1202 while (1) {
1203 l2 = sizeof name2;
1204 j = sysctl(name1, l1, name2, &l2, 0, 0);
1205 if (j < 0) {
1206 if (errno == ENOENT)
1207 return 0;
1208 else
1209 err(1, "sysctl(getnext) %d %ld", j, l2);
1210 }
1211
1212 l2 /= sizeof (int);
1213
1214 if (l2 < len)
1215 return 0;
1216
1217 for (i = 0; i < len; i++)
1218 if (name2[i] != oid[i])
1219 return 0;
1220
1221 i = show_var(name2, l2, 0);
1222 if (!i && !bflag)
1223 putchar('\n');
1224
1225 memcpy(name1+2, name2, l2*sizeof (int));
1226 l1 = 2 + l2;
1227 }
1228 }