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