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