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