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
121struct ctlname topname[] = CTL_NAMES;
122struct ctlname kernname[] = CTL_KERN_NAMES;
123struct ctlname vmname[] = CTL_VM_NAMES;
124struct ctlname hwname[] = CTL_HW_NAMES;
125struct ctlname username[] = CTL_USER_NAMES;
126struct ctlname debugname[CTL_DEBUG_MAXID];
127struct ctlname *vfsname;
128#ifdef CTL_MACHDEP_NAMES
129struct ctlname machdepname[] = CTL_MACHDEP_NAMES;
130#endif
131char names[BUFSIZ];
132int lastused;
133
134struct list {
135	struct	ctlname *list;
136	int	size;
137};
138struct list toplist = { topname, CTL_MAXID };
139struct 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
155static int	Aflag, aflag, bflag, hflag, nflag, wflag, Xflag;
156static int	foundSome = 0;
157static int	invalid_name_used = 0;
158
159void listall(char *prefix, struct list *lp);
160void old_parse(char *string, int flags);
161void debuginit();
162void vfsinit();
163int  findname(char *string, char *level, char **bufp, struct list *namelist);
164void usage();
165
166static void 	parse(char *string, int flags);
167static int	oidfmt(int *, int, char *, u_int *);
168static int	show_var(int *, int, int);
169static int	sysctl_all (int *oid, int len);
170static 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
179int
180main(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 */
220void
221listall(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 */
246void
247old_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 */
584void 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 */
609void 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 */
652int
653findname(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
687void 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 */
704static void
705parse(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
842static int
843S_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
857static int
858S_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
873static int
874S_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
897static int
898S_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
915static int
916T_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
944static int
945name2oid(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
962static int
963oidfmt(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
1005static int
1006show_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
1185static int
1186sysctl_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}
1229