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