xref: /freebsd/sbin/sysctl/sysctl.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
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
31 static 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
38 static char sccsid[] = "@(#)from: sysctl.c	8.1 (Berkeley) 6/6/93";
39 #endif
40 static const char rcsid[] =
41   "$FreeBSD$";
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 
63 static int	aflag, bflag, dflag, eflag, hflag, Nflag, nflag, oflag, xflag;
64 
65 static int	oidfmt(int *, int, char *, u_int *);
66 static void	parse(char *);
67 static int	show_var(int *, int);
68 static int	sysctl_all (int *oid, int len);
69 static int	name2oid(char *, int *);
70 
71 static void	set_T_dev_t (char *, void **, size_t *);
72 
73 static void
74 usage(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 
83 int
84 main(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  */
156 static void
157 parse(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 
304 static int
305 S_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 
318 static int
319 S_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 
334 static int
335 S_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 
357 static int
358 S_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 
393 static int
394 T_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 
412 static void
413 set_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 
442 static int
443 name2oid(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 
460 static int
461 oidfmt(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 
493 static int
494 show_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 	qoid[0] = 0;
505 	memcpy(qoid + 2, oid, nlen * sizeof(int));
506 
507 	qoid[1] = 1;
508 	j = sizeof(name);
509 	i = sysctl(qoid, nlen + 2, name, &j, 0, 0);
510 	if (i || !j)
511 		err(1, "sysctl name %d %d %d", i, j, errno);
512 
513 	if (Nflag) {
514 		printf("%s", name);
515 		return (0);
516 	}
517 
518 	if (eflag)
519 		sep = "=";
520 	else
521 		sep = ": ";
522 
523 	if (dflag) {	/* just print description */
524 		qoid[1] = 5;
525 		j = sizeof(buf);
526 		i = sysctl(qoid, nlen + 2, buf, &j, 0, 0);
527 		if (!nflag)
528 			printf("%s%s", name, sep);
529 		printf("%s", buf);
530 		return (0);
531 	}
532 	/* find an estimate of how much we need for this var */
533 	j = 0;
534 	i = sysctl(oid, nlen, 0, &j, 0, 0);
535 	j += j; /* we want to be sure :-) */
536 
537 	val = alloca(j + 1);
538 	len = j;
539 	i = sysctl(oid, nlen, val, &len, 0, 0);
540 	if (i || !len)
541 		return (1);
542 
543 	if (bflag) {
544 		fwrite(val, 1, len, stdout);
545 		return (0);
546 	}
547 	val[len] = '\0';
548 	fmt = buf;
549 	oidfmt(oid, nlen, fmt, &kind);
550 	p = val;
551 	switch (*fmt) {
552 	case 'A':
553 		if (!nflag)
554 			printf("%s%s", name, sep);
555 		printf("%.*s", len, p);
556 		return (0);
557 
558 	case 'I':
559 		if (!nflag)
560 			printf("%s%s", name, sep);
561 		fmt++;
562 		val = "";
563 		while (len >= sizeof(int)) {
564 			fputs(val, stdout);
565 			if(*fmt == 'U')
566 				printf(hflag ? "%'u" : "%u", *(unsigned int *)p);
567 			else if (*fmt == 'K') {
568 				if (*(int *)p < 0)
569 					printf("%d", *(int *)p);
570 				else
571 					printf("%d.%dC", (*(int *)p - 2732) / 10, (*(int *)p - 2732) % 10);
572 			} else
573 				printf(hflag ? "%'d" : "%d", *(int *)p);
574 			val = " ";
575 			len -= sizeof(int);
576 			p += sizeof(int);
577 		}
578 		return (0);
579 
580 	case 'L':
581 		if (!nflag)
582 			printf("%s%s", name, sep);
583 		fmt++;
584 		val = "";
585 		while (len >= sizeof(long)) {
586 			fputs(val, stdout);
587 			if(*fmt == 'U')
588 				printf(hflag ? "%'lu" : "%lu", *(unsigned long *)p);
589 			else if (*fmt == 'K') {
590 				if (*(long *)p < 0)
591 					printf("%ld", *(long *)p);
592 				else
593 					printf("%ld.%ldC", (*(long *)p - 2732) / 10, (*(long *)p - 2732) % 10);
594 			} else
595 				printf(hflag ? "%'ld" : "%ld", *(long *)p);
596 			val = " ";
597 			len -= sizeof(long);
598 			p += sizeof(long);
599 		}
600 		return (0);
601 
602 	case 'P':
603 		if (!nflag)
604 			printf("%s%s", name, sep);
605 		printf("%p", *(void **)p);
606 		return (0);
607 
608 	case 'T':
609 	case 'S':
610 		i = 0;
611 		if (strcmp(fmt, "S,clockinfo") == 0)
612 			func = S_clockinfo;
613 		else if (strcmp(fmt, "S,timeval") == 0)
614 			func = S_timeval;
615 		else if (strcmp(fmt, "S,loadavg") == 0)
616 			func = S_loadavg;
617 		else if (strcmp(fmt, "S,vmtotal") == 0)
618 			func = S_vmtotal;
619 		else if (strcmp(fmt, "T,dev_t") == 0)
620 			func = T_dev_t;
621 		else
622 			func = NULL;
623 		if (func) {
624 			if (!nflag)
625 				printf("%s%s", name, sep);
626 			return ((*func)(len, p));
627 		}
628 		/* FALLTHROUGH */
629 	default:
630 		if (!oflag && !xflag)
631 			return (1);
632 		if (!nflag)
633 			printf("%s%s", name, sep);
634 		printf("Format:%s Length:%d Dump:0x", fmt, len);
635 		while (len-- && (xflag || p < val + 16))
636 			printf("%02x", *p++);
637 		if (!xflag && len > 16)
638 			printf("...");
639 		return (0);
640 	}
641 	return (1);
642 }
643 
644 static int
645 sysctl_all (int *oid, int len)
646 {
647 	int name1[22], name2[22];
648 	int i, j;
649 	size_t l1, l2;
650 
651 	name1[0] = 0;
652 	name1[1] = 2;
653 	l1 = 2;
654 	if (len) {
655 		memcpy(name1+2, oid, len * sizeof(int));
656 		l1 += len;
657 	} else {
658 		name1[2] = 1;
659 		l1++;
660 	}
661 	for (;;) {
662 		l2 = sizeof(name2);
663 		j = sysctl(name1, l1, name2, &l2, 0, 0);
664 		if (j < 0) {
665 			if (errno == ENOENT)
666 				return 0;
667 			else
668 				err(1, "sysctl(getnext) %d %d", j, l2);
669 		}
670 
671 		l2 /= sizeof(int);
672 
673 		if (l2 < len)
674 			return 0;
675 
676 		for (i = 0; i < len; i++)
677 			if (name2[i] != oid[i])
678 				return 0;
679 
680 		i = show_var(name2, l2);
681 		if (!i && !bflag)
682 			putchar('\n');
683 
684 		memcpy(name1+2, name2, l2 * sizeof(int));
685 		l1 = 2 + l2;
686 	}
687 }
688