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