xref: /freebsd/sbin/sysctl/sysctl.c (revision afe61c15161c324a7af299a9b8457aba5afc92db)
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 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 static char sccsid[] = "@(#)sysctl.c	8.1 (Berkeley) 6/6/93";
42 #endif /* not lint */
43 
44 #include <sys/param.h>
45 #include <sys/gmon.h>
46 #include <sys/stat.h>
47 #include <sys/sysctl.h>
48 #include <sys/socket.h>
49 #include <vm/vm_param.h>
50 #include <machine/cpu.h>
51 
52 #include <netinet/in.h>
53 #include <netinet/in_systm.h>
54 #include <netinet/ip.h>
55 #include <netinet/ip_icmp.h>
56 #include <netinet/icmp_var.h>
57 #include <netinet/ip_var.h>
58 #include <netinet/udp.h>
59 #include <netinet/udp_var.h>
60 
61 #include <errno.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 
66 struct ctlname topname[] = CTL_NAMES;
67 struct ctlname kernname[] = CTL_KERN_NAMES;
68 struct ctlname vmname[] = CTL_VM_NAMES;
69 struct ctlname netname[] = CTL_NET_NAMES;
70 struct ctlname hwname[] = CTL_HW_NAMES;
71 struct ctlname username[] = CTL_USER_NAMES;
72 struct ctlname debugname[CTL_DEBUG_MAXID];
73 #ifdef CTL_MACHDEP_NAMES
74 struct ctlname machdepname[] = CTL_MACHDEP_NAMES;
75 #endif
76 char names[BUFSIZ];
77 
78 struct list {
79 	struct	ctlname *list;
80 	int	size;
81 };
82 struct list toplist = { topname, CTL_MAXID };
83 struct list secondlevel[] = {
84 	{ 0, 0 },			/* CTL_UNSPEC */
85 	{ kernname, KERN_MAXID },	/* CTL_KERN */
86 	{ vmname, VM_MAXID },		/* CTL_VM */
87 	{ 0, 0 },			/* CTL_FS */
88 	{ netname, NET_MAXID },		/* CTL_NET */
89 	{ 0, CTL_DEBUG_MAXID },		/* CTL_DEBUG */
90 	{ hwname, HW_MAXID },		/* CTL_HW */
91 #ifdef CTL_MACHDEP_NAMES
92 	{ machdepname, CPU_MAXID },	/* CTL_MACHDEP */
93 #else
94 	{ 0, 0 },			/* CTL_MACHDEP */
95 #endif
96 	{ username, USER_MAXID },	/* CTL_USER_NAMES */
97 };
98 
99 int	Aflag, aflag, nflag, wflag;
100 
101 /*
102  * Variables requiring special processing.
103  */
104 #define	CLOCK		0x00000001
105 #define	BOOTTIME	0x00000002
106 #define	CONSDEV		0x00000004
107 
108 int
109 main(argc, argv)
110 	int argc;
111 	char *argv[];
112 {
113 	extern char *optarg;
114 	extern int optind;
115 	int ch, lvl1;
116 
117 	while ((ch = getopt(argc, argv, "Aanw")) != EOF) {
118 		switch (ch) {
119 
120 		case 'A':
121 			Aflag = 1;
122 			break;
123 
124 		case 'a':
125 			aflag = 1;
126 			break;
127 
128 		case 'n':
129 			nflag = 1;
130 			break;
131 
132 		case 'w':
133 			wflag = 1;
134 			break;
135 
136 		default:
137 			usage();
138 		}
139 	}
140 	argc -= optind;
141 	argv += optind;
142 
143 	if (Aflag || aflag) {
144 		debuginit();
145 		for (lvl1 = 1; lvl1 < CTL_MAXID; lvl1++)
146 			listall(topname[lvl1].ctl_name, &secondlevel[lvl1]);
147 		exit(0);
148 	}
149 	if (argc == 0)
150 		usage();
151 	while (argc-- > 0)
152 		parse(*argv, 1);
153 	exit(0);
154 }
155 
156 /*
157  * List all variables known to the system.
158  */
159 listall(prefix, lp)
160 	char *prefix;
161 	struct list *lp;
162 {
163 	int lvl2;
164 	char *cp, name[BUFSIZ];
165 
166 	if (lp->list == 0)
167 		return;
168 	strcpy(name, prefix);
169 	cp = &name[strlen(name)];
170 	*cp++ = '.';
171 	for (lvl2 = 0; lvl2 < lp->size; lvl2++) {
172 		if (lp->list[lvl2].ctl_name == 0)
173 			continue;
174 		strcpy(cp, lp->list[lvl2].ctl_name);
175 		parse(name, Aflag);
176 	}
177 }
178 
179 /*
180  * Parse a name into a MIB entry.
181  * Lookup and print out the MIB entry if it exists.
182  * Set a new value if requested.
183  */
184 parse(string, flags)
185 	char *string;
186 	int flags;
187 {
188 	int indx, type, state, size, len;
189 	int special = 0;
190 	void *newval = 0;
191 	int intval, newsize = 0;
192 	quad_t quadval;
193 	struct list *lp;
194 	int mib[CTL_MAXNAME];
195 	char *cp, *bufp, buf[BUFSIZ], strval[BUFSIZ];
196 
197 	bufp = buf;
198 	snprintf(buf, BUFSIZ, "%s", string);
199 	if ((cp = strchr(string, '=')) != NULL) {
200 		if (!wflag) {
201 			fprintf(stderr, "Must specify -w to set variables\n");
202 			exit(2);
203 		}
204 		*strchr(buf, '=') = '\0';
205 		*cp++ = '\0';
206 		while (isspace(*cp))
207 			cp++;
208 		newval = cp;
209 		newsize = strlen(cp);
210 	}
211 	if ((indx = findname(string, "top", &bufp, &toplist)) == -1)
212 		return;
213 	mib[0] = indx;
214 	if (indx == CTL_DEBUG)
215 		debuginit();
216 	lp = &secondlevel[indx];
217 	if (lp->list == 0) {
218 		fprintf(stderr, "%s: class is not implemented\n",
219 		    topname[indx]);
220 		return;
221 	}
222 	if (bufp == NULL) {
223 		listall(topname[indx].ctl_name, lp);
224 		return;
225 	}
226 	if ((indx = findname(string, "second", &bufp, lp)) == -1)
227 		return;
228 	mib[1] = indx;
229 	type = lp->list[indx].ctl_type;
230 	len = 2;
231 	switch (mib[0]) {
232 
233 	case CTL_KERN:
234 		switch (mib[1]) {
235 		case KERN_PROF:
236 			mib[2] = GPROF_STATE;
237 			size = sizeof state;
238 			if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) {
239 				if (flags == 0)
240 					return;
241 				if (!nflag)
242 					fprintf(stdout, "%s: ", string);
243 				fprintf(stderr,
244 				    "kernel is not compiled for profiling\n");
245 				return;
246 			}
247 			if (!nflag)
248 				fprintf(stdout, "%s: %s\n", string,
249 				    state == GMON_PROF_OFF ? "off" : "running");
250 			return;
251 		case KERN_VNODE:
252 		case KERN_FILE:
253 			if (flags == 0)
254 				return;
255 			fprintf(stderr,
256 			    "Use pstat to view %s information\n", string);
257 			return;
258 		case KERN_PROC:
259 			if (flags == 0)
260 				return;
261 			fprintf(stderr,
262 			    "Use ps to view %s information\n", string);
263 			return;
264 		case KERN_CLOCKRATE:
265 			special |= CLOCK;
266 			break;
267 		case KERN_BOOTTIME:
268 			special |= BOOTTIME;
269 			break;
270 		}
271 		break;
272 
273 	case CTL_HW:
274 		break;
275 
276 	case CTL_VM:
277 		if (mib[1] == VM_LOADAVG) {
278 			double loads[3];
279 
280 			getloadavg(loads, 3);
281 			if (!nflag)
282 				fprintf(stdout, "%s: ", string);
283 			fprintf(stdout, "%.2f %.2f %.2f\n",
284 			    loads[0], loads[1], loads[2]);
285 			return;
286 		}
287 		if (flags == 0)
288 			return;
289 		fprintf(stderr,
290 		    "Use vmstat or systat to view %s information\n", string);
291 		return;
292 
293 	case CTL_NET:
294 		if (mib[1] == PF_INET) {
295 			len = sysctl_inet(string, &bufp, mib, flags, &type);
296 			if (len >= 0)
297 				break;
298 			return;
299 		}
300 		if (flags == 0)
301 			return;
302 		fprintf(stderr, "Use netstat to view %s information\n", string);
303 		return;
304 
305 	case CTL_DEBUG:
306 		mib[2] = CTL_DEBUG_VALUE;
307 		len = 3;
308 		break;
309 
310 	case CTL_MACHDEP:
311 #ifdef CPU_CONSDEV
312 		if (mib[1] == CPU_CONSDEV)
313 			special |= CONSDEV;
314 #endif
315 		break;
316 
317 	case CTL_FS:
318 	case CTL_USER:
319 		break;
320 
321 	default:
322 		fprintf(stderr, "Illegal top level value: %d\n", mib[0]);
323 		return;
324 
325 	}
326 	if (bufp) {
327 		fprintf(stderr, "name %s in %s is unknown\n", *bufp, string);
328 		return;
329 	}
330 	if (newsize > 0) {
331 		switch (type) {
332 		case CTLTYPE_INT:
333 			intval = atoi(newval);
334 			newval = &intval;
335 			newsize = sizeof intval;
336 			break;
337 
338 		case CTLTYPE_QUAD:
339 			sscanf(newval, "%qd", &quadval);
340 			newval = &quadval;
341 			newsize = sizeof quadval;
342 			break;
343 		}
344 	}
345 	size = BUFSIZ;
346 	if (sysctl(mib, len, buf, &size, newsize ? newval : 0, newsize) == -1) {
347 		if (flags == 0)
348 			return;
349 		switch (errno) {
350 		case EOPNOTSUPP:
351 			fprintf(stderr, "%s: value is not available\n", string);
352 			return;
353 		case ENOTDIR:
354 			fprintf(stderr, "%s: specification is incomplete\n",
355 			    string);
356 			return;
357 		case ENOMEM:
358 			fprintf(stderr, "%s: type is unknown to this program\n",
359 			    string);
360 			return;
361 		default:
362 			perror(string);
363 			return;
364 		}
365 	}
366 	if (special & CLOCK) {
367 		struct clockinfo *clkp = (struct clockinfo *)buf;
368 
369 		if (!nflag)
370 			fprintf(stdout, "%s: ", string);
371 		fprintf(stdout,
372 		    "hz = %d, tick = %d, profhz = %d, stathz = %d\n",
373 		    clkp->hz, clkp->tick, clkp->profhz, clkp->stathz);
374 		return;
375 	}
376 	if (special & BOOTTIME) {
377 		struct timeval *btp = (struct timeval *)buf;
378 
379 		if (!nflag)
380 			fprintf(stdout, "%s = %s\n", string,
381 			    ctime(&btp->tv_sec));
382 		else
383 			fprintf(stdout, "%d\n", btp->tv_sec);
384 		return;
385 	}
386 	if (special & CONSDEV) {
387 		dev_t dev = *(dev_t *)buf;
388 
389 		if (!nflag)
390 			fprintf(stdout, "%s = %s\n", string,
391 			    devname(dev, S_IFCHR));
392 		else
393 			fprintf(stdout, "0x%x\n", dev);
394 		return;
395 	}
396 	switch (type) {
397 	case CTLTYPE_INT:
398 		if (newsize == 0) {
399 			if (!nflag)
400 				fprintf(stdout, "%s = ", string);
401 			fprintf(stdout, "%d\n", *(int *)buf);
402 		} else {
403 			if (!nflag)
404 				fprintf(stdout, "%s: %d -> ", string,
405 				    *(int *)buf);
406 			fprintf(stdout, "%d\n", *(int *)newval);
407 		}
408 		return;
409 
410 	case CTLTYPE_STRING:
411 		if (newsize == 0) {
412 			if (!nflag)
413 				fprintf(stdout, "%s = ", string);
414 			fprintf(stdout, "%s\n", buf);
415 		} else {
416 			if (!nflag)
417 				fprintf(stdout, "%s: %s -> ", string, buf);
418 			fprintf(stdout, "%s\n", newval);
419 		}
420 		return;
421 
422 	case CTLTYPE_QUAD:
423 		if (newsize == 0) {
424 			if (!nflag)
425 				fprintf(stdout, "%s = ", string);
426 			fprintf(stdout, "%qd\n", *(quad_t *)buf);
427 		} else {
428 			if (!nflag)
429 				fprintf(stdout, "%s: %qd -> ", string,
430 				    *(quad_t *)buf);
431 			fprintf(stdout, "%qd\n", *(quad_t *)newval);
432 		}
433 		return;
434 
435 	case CTLTYPE_STRUCT:
436 		fprintf(stderr, "%s: unknown structure returned\n",
437 		    string);
438 		return;
439 
440 	default:
441 	case CTLTYPE_NODE:
442 		fprintf(stderr, "%s: unknown type returned\n",
443 		    string);
444 		return;
445 	}
446 }
447 
448 /*
449  * Initialize the set of debugging names
450  */
451 debuginit()
452 {
453 	int mib[3], size, loc, i;
454 
455 	if (secondlevel[CTL_DEBUG].list != 0)
456 		return;
457 	secondlevel[CTL_DEBUG].list = debugname;
458 	mib[0] = CTL_DEBUG;
459 	mib[2] = CTL_DEBUG_NAME;
460 	for (loc = 0, i = 0; i < CTL_DEBUG_MAXID; i++) {
461 		mib[1] = i;
462 		size = BUFSIZ - loc;
463 		if (sysctl(mib, 3, &names[loc], &size, NULL, 0) == -1)
464 			continue;
465 		debugname[i].ctl_name = &names[loc];
466 		debugname[i].ctl_type = CTLTYPE_INT;
467 		loc += size;
468 	}
469 }
470 
471 struct ctlname inetname[] = CTL_IPPROTO_NAMES;
472 struct ctlname ipname[] = IPCTL_NAMES;
473 struct ctlname icmpname[] = ICMPCTL_NAMES;
474 struct ctlname udpname[] = UDPCTL_NAMES;
475 struct list inetlist = { inetname, IPPROTO_MAXID };
476 struct list inetvars[] = {
477 	{ ipname, IPCTL_MAXID },	/* ip */
478 	{ icmpname, ICMPCTL_MAXID },	/* icmp */
479 	{ 0, 0 },			/* igmp */
480 	{ 0, 0 },			/* ggmp */
481 	{ 0, 0 },
482 	{ 0, 0 },
483 	{ 0, 0 },			/* tcp */
484 	{ 0, 0 },
485 	{ 0, 0 },			/* egp */
486 	{ 0, 0 },
487 	{ 0, 0 },
488 	{ 0, 0 },
489 	{ 0, 0 },			/* pup */
490 	{ 0, 0 },
491 	{ 0, 0 },
492 	{ 0, 0 },
493 	{ 0, 0 },
494 	{ udpname, UDPCTL_MAXID },	/* udp */
495 };
496 
497 /*
498  * handle internet requests
499  */
500 sysctl_inet(string, bufpp, mib, flags, typep)
501 	char *string;
502 	char **bufpp;
503 	int mib[];
504 	int flags;
505 	int *typep;
506 {
507 	struct list *lp;
508 	int indx;
509 
510 	if (*bufpp == NULL) {
511 		listall(string, &inetlist);
512 		return (-1);
513 	}
514 	if ((indx = findname(string, "third", bufpp, &inetlist)) == -1)
515 		return (-1);
516 	mib[2] = indx;
517 	if (indx <= IPPROTO_UDP && inetvars[indx].list != NULL)
518 		lp = &inetvars[indx];
519 	else if (!flags)
520 		return (-1);
521 	else {
522 		fprintf(stderr, "%s: no variables defined for this protocol\n",
523 		    string);
524 		return (-1);
525 	}
526 	if (*bufpp == NULL) {
527 		listall(string, lp);
528 		return (-1);
529 	}
530 	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
531 		return (-1);
532 	mib[3] = indx;
533 	*typep = lp->list[indx].ctl_type;
534 	return (4);
535 }
536 
537 /*
538  * Scan a list of names searching for a particular name.
539  */
540 findname(string, level, bufp, namelist)
541 	char *string;
542 	char *level;
543 	char **bufp;
544 	struct list *namelist;
545 {
546 	char *name;
547 	int i;
548 
549 	if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
550 		fprintf(stderr, "%s: incomplete specification\n", string);
551 		return (-1);
552 	}
553 	for (i = 0; i < namelist->size; i++)
554 		if (namelist->list[i].ctl_name != NULL &&
555 		    strcmp(name, namelist->list[i].ctl_name) == 0)
556 			break;
557 	if (i == namelist->size) {
558 		fprintf(stderr, "%s level name %s in %s is invalid\n",
559 		    level, name, string);
560 		return (-1);
561 	}
562 	return (i);
563 }
564 
565 usage()
566 {
567 
568 	(void)fprintf(stderr, "usage:\t%s\n\t%s\n\t%s\n\t%s\n",
569 	    "sysctl [-n] variable ...", "sysctl [-n] -w variable=value ...",
570 	    "sysctl [-n] -a", "sysctl [-n] -A");
571 	exit(1);
572 }
573