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