xref: /freebsd/sbin/sysctl/sysctl.c (revision 48991a368427cadb9cdac39581d1676c29619c52)
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.7 1995/06/11 19:32:58 rgrimes 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 #ifdef CTL_MACHDEP_NAMES
80 struct ctlname machdepname[] = CTL_MACHDEP_NAMES;
81 #endif
82 char names[BUFSIZ];
83 
84 struct list {
85 	struct	ctlname *list;
86 	int	size;
87 };
88 struct list toplist = { topname, CTL_MAXID };
89 struct list secondlevel[] = {
90 	{ 0, 0 },			/* CTL_UNSPEC */
91 	{ kernname, KERN_MAXID },	/* CTL_KERN */
92 	{ vmname, VM_MAXID },		/* CTL_VM */
93 	{ 0, 0 },			/* CTL_FS */
94 	{ netname, NET_MAXID },		/* CTL_NET */
95 	{ 0, 0 },			/* CTL_DEBUG */
96 	{ hwname, HW_MAXID },		/* CTL_HW */
97 #ifdef CTL_MACHDEP_NAMES
98 	{ machdepname, CPU_MAXID },	/* CTL_MACHDEP */
99 #else
100 	{ 0, 0 },			/* CTL_MACHDEP */
101 #endif
102 	{ username, USER_MAXID },	/* CTL_USER_NAMES */
103 };
104 
105 int	Aflag, aflag, nflag, wflag;
106 
107 /*
108  * Variables requiring special processing.
109  */
110 #define	CLOCK		0x00000001
111 #define	BOOTTIME	0x00000002
112 #define	CONSDEV		0x00000004
113 #define DUMPDEV		0x00000008
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 		for (lvl1 = 1; lvl1 < CTL_MAXID; lvl1++)
152 			listall(topname[lvl1].ctl_name, &secondlevel[lvl1]);
153 		exit(0);
154 	}
155 	if (argc == 0)
156 		usage();
157 	while (argc-- > 0)
158 		parse(*argv, 1);
159 	exit(0);
160 }
161 
162 /*
163  * List all variables known to the system.
164  */
165 listall(prefix, lp)
166 	char *prefix;
167 	struct list *lp;
168 {
169 	int lvl2;
170 	char *cp, name[BUFSIZ];
171 
172 	if (lp->list == 0)
173 		return;
174 	strcpy(name, prefix);
175 	cp = &name[strlen(name)];
176 	*cp++ = '.';
177 	for (lvl2 = 0; lvl2 < lp->size; lvl2++) {
178 		if (lp->list[lvl2].ctl_name == 0)
179 			continue;
180 		strcpy(cp, lp->list[lvl2].ctl_name);
181 		parse(name, Aflag);
182 	}
183 }
184 
185 /*
186  * Parse a name into a MIB entry.
187  * Lookup and print out the MIB entry if it exists.
188  * Set a new value if requested.
189  */
190 parse(string, flags)
191 	char *string;
192 	int flags;
193 {
194 	int indx, type, state, size, len;
195 	int special = 0;
196 	void *newval = 0;
197 	int intval, newsize = 0;
198 	quad_t quadval;
199 	struct list *lp;
200 	int mib[CTL_MAXNAME];
201 	char *cp, *bufp, buf[BUFSIZ], strval[BUFSIZ];
202 
203 	bufp = buf;
204 	snprintf(buf, BUFSIZ, "%s", string);
205 	if ((cp = strchr(string, '=')) != NULL) {
206 		if (!wflag) {
207 			fprintf(stderr, "Must specify -w to set variables\n");
208 			exit(2);
209 		}
210 		*strchr(buf, '=') = '\0';
211 		*cp++ = '\0';
212 		while (isspace(*cp))
213 			cp++;
214 		newval = cp;
215 		newsize = strlen(cp);
216 	}
217 	if ((indx = findname(string, "top", &bufp, &toplist)) == -1)
218 		return;
219 	mib[0] = indx;
220 	lp = &secondlevel[indx];
221 	if (lp->list == 0) {
222 		fprintf(stderr, "%s: class is not implemented\n",
223 		    topname[indx]);
224 		return;
225 	}
226 	if (bufp == NULL) {
227 		listall(topname[indx].ctl_name, lp);
228 		return;
229 	}
230 	if ((indx = findname(string, "second", &bufp, lp)) == -1)
231 		return;
232 	mib[1] = indx;
233 	type = lp->list[indx].ctl_type;
234 	len = 2;
235 	switch (mib[0]) {
236 
237 	case CTL_KERN:
238 		switch (mib[1]) {
239 		case KERN_PROF:
240 			mib[2] = GPROF_STATE;
241 			size = sizeof state;
242 			if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) {
243 				if (flags == 0)
244 					return;
245 				if (!nflag)
246 					fprintf(stdout, "%s: ", string);
247 				fprintf(stderr,
248 				    "kernel is not compiled for profiling\n");
249 				return;
250 			}
251 			if (!nflag)
252 				fprintf(stdout, "%s: %s\n", string,
253 				    state == GMON_PROF_OFF ? "off" : "running");
254 			return;
255 		case KERN_VNODE:
256 		case KERN_FILE:
257 			if (flags == 0)
258 				return;
259 			fprintf(stderr,
260 			    "Use pstat to view %s information\n", string);
261 			return;
262 		case KERN_PROC:
263 			if (flags == 0)
264 				return;
265 			fprintf(stderr,
266 			    "Use ps to view %s information\n", string);
267 			return;
268 		case KERN_CLOCKRATE:
269 			special |= CLOCK;
270 			break;
271 		case KERN_BOOTTIME:
272 			special |= BOOTTIME;
273 			break;
274 		case KERN_DUMPDEV:
275 			special |= DUMPDEV;
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 
314 	case CTL_MACHDEP:
315 #ifdef CPU_CONSDEV
316 		if (mib[1] == CPU_CONSDEV)
317 			special |= CONSDEV;
318 #endif
319 		break;
320 
321 	case CTL_FS:
322 	case CTL_USER:
323 		break;
324 
325 	default:
326 		fprintf(stderr, "Illegal top level value: %d\n", mib[0]);
327 		return;
328 
329 	}
330 	if (bufp) {
331 		fprintf(stderr, "name %s in %s is unknown\n", bufp, string);
332 		return;
333 	}
334 	if (newsize > 0) {
335 		switch (type) {
336 		case CTLTYPE_INT:
337 			intval = atoi(newval);
338 			newval = &intval;
339 			newsize = sizeof intval;
340 			break;
341 
342 		case CTLTYPE_QUAD:
343 			sscanf(newval, "%qd", &quadval);
344 			newval = &quadval;
345 			newsize = sizeof quadval;
346 			break;
347 		}
348 	}
349 	size = BUFSIZ;
350 	if (sysctl(mib, len, buf, &size, newsize ? newval : 0, newsize) == -1) {
351 		if (flags == 0)
352 			return;
353 		switch (errno) {
354 		case EOPNOTSUPP:
355 			fprintf(stderr, "%s: value is not available\n", string);
356 			return;
357 		case ENOTDIR:
358 			fprintf(stderr, "%s: specification is incomplete\n",
359 			    string);
360 			return;
361 		case ENOMEM:
362 			fprintf(stderr, "%s: type is unknown to this program\n",
363 			    string);
364 			return;
365 		default:
366 			perror(string);
367 			return;
368 		}
369 	}
370 	if (special & CLOCK) {
371 		struct clockinfo *clkp = (struct clockinfo *)buf;
372 
373 		if (!nflag)
374 			fprintf(stdout, "%s: ", string);
375 		fprintf(stdout,
376 		    "hz = %d, tick = %d, profhz = %d, stathz = %d\n",
377 		    clkp->hz, clkp->tick, clkp->profhz, clkp->stathz);
378 		return;
379 	}
380 	if (special & BOOTTIME) {
381 		struct timeval *btp = (struct timeval *)buf;
382 
383 		if (!nflag)
384 			fprintf(stdout, "%s = %s", string,
385 			    ctime(&btp->tv_sec));
386 		else
387 			fprintf(stdout, "%d\n", btp->tv_sec);
388 		return;
389 	}
390 	if (special & (CONSDEV | DUMPDEV)) {
391 		dev_t dev = *(dev_t *)buf;
392 
393 		if ((special & DUMPDEV) && dev == NODEV && !nflag) {
394 			printf("%s = disabled\n", string);
395 			return;
396 		}
397 		if (!nflag)
398 			fprintf(stdout, "%s = %s\n", string,
399 			    devname(dev,
400 				    (special & CONSDEV) ? S_IFCHR : S_IFBLK));
401 		else
402 			fprintf(stdout, "0x%x\n", dev);
403 		return;
404 	}
405 	switch (type) {
406 	case CTLTYPE_INT:
407 		if (newsize == 0) {
408 			if (!nflag)
409 				fprintf(stdout, "%s = ", string);
410 			fprintf(stdout, "%d\n", *(int *)buf);
411 		} else {
412 			if (!nflag)
413 				fprintf(stdout, "%s: %d -> ", string,
414 				    *(int *)buf);
415 			fprintf(stdout, "%d\n", *(int *)newval);
416 		}
417 		return;
418 
419 	case CTLTYPE_STRING:
420 		if (newsize == 0) {
421 			if (!nflag)
422 				fprintf(stdout, "%s = ", string);
423 			fprintf(stdout, "%s\n", buf);
424 		} else {
425 			if (!nflag)
426 				fprintf(stdout, "%s: %s -> ", string, buf);
427 			fprintf(stdout, "%s\n", newval);
428 		}
429 		return;
430 
431 	case CTLTYPE_QUAD:
432 		if (newsize == 0) {
433 			if (!nflag)
434 				fprintf(stdout, "%s = ", string);
435 			fprintf(stdout, "%qd\n", *(quad_t *)buf);
436 		} else {
437 			if (!nflag)
438 				fprintf(stdout, "%s: %qd -> ", string,
439 				    *(quad_t *)buf);
440 			fprintf(stdout, "%qd\n", *(quad_t *)newval);
441 		}
442 		return;
443 
444 	case CTLTYPE_STRUCT:
445 		fprintf(stderr, "%s: unknown structure returned\n",
446 		    string);
447 		return;
448 
449 	default:
450 	case CTLTYPE_NODE:
451 		fprintf(stderr, "%s: unknown type returned\n",
452 		    string);
453 		return;
454 	}
455 }
456 
457 
458 struct ctlname inetname[] = CTL_IPPROTO_NAMES;
459 struct ctlname ipname[] = IPCTL_NAMES;
460 struct ctlname icmpname[] = ICMPCTL_NAMES;
461 struct ctlname udpname[] = UDPCTL_NAMES;
462 struct ctlname tcpname[] = TCPCTL_NAMES;
463 struct ctlname igmpname[] = IGMPCTL_NAMES;
464 struct list inetlist = { inetname, IPPROTO_MAXID };
465 struct list inetvars[] = {
466 	{ ipname, IPCTL_MAXID },	/* ip */
467 	{ icmpname, ICMPCTL_MAXID },	/* icmp */
468 	{ igmpname, IGMPCTL_MAXID },	/* igmp */
469 	{ 0, 0 },			/* ggp */
470 	{ 0, 0 },			/* ipencap */
471 	{ 0, 0 },
472 	{ tcpname, TCPCTL_MAXID },	/* tcp */
473 	{ 0, 0 },
474 	{ 0, 0 },			/* egp */
475 	{ 0, 0 },
476 	{ 0, 0 },
477 	{ 0, 0 },
478 	{ 0, 0 },			/* pup */
479 	{ 0, 0 },
480 	{ 0, 0 },
481 	{ 0, 0 },
482 	{ 0, 0 },
483 	{ udpname, UDPCTL_MAXID },	/* udp */
484 };
485 
486 /*
487  * handle internet requests
488  */
489 int
490 sysctl_inet(string, bufpp, mib, flags, typep, specialp)
491 	char *string;
492 	char **bufpp;
493 	int mib[];
494 	int flags;
495 	int *typep;
496 	int *specialp;
497 {
498 	struct list *lp;
499 	int indx;
500 
501 	if (*bufpp == NULL) {
502 		listall(string, &inetlist);
503 		return (-1);
504 	}
505 	if ((indx = findname(string, "third", bufpp, &inetlist)) == -1)
506 		return (-1);
507 	mib[2] = indx;
508 	if (indx <= IPPROTO_UDP && inetvars[indx].list != NULL)
509 		lp = &inetvars[indx];
510 	else if (!flags)
511 		return (-1);
512 	else {
513 		fprintf(stderr, "%s: no variables defined for this protocol\n",
514 		    string);
515 		return (-1);
516 	}
517 	if (*bufpp == NULL) {
518 		listall(string, lp);
519 		return (-1);
520 	}
521 	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
522 		return (-1);
523 	mib[3] = indx;
524 	*typep = lp->list[indx].ctl_type;
525 	return (4);
526 }
527 
528 /*
529  * Scan a list of names searching for a particular name.
530  */
531 findname(string, level, bufp, namelist)
532 	char *string;
533 	char *level;
534 	char **bufp;
535 	struct list *namelist;
536 {
537 	char *name;
538 	int i;
539 
540 	if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
541 		fprintf(stderr, "%s: incomplete specification\n", string);
542 		return (-1);
543 	}
544 	for (i = 0; i < namelist->size; i++)
545 		if (namelist->list[i].ctl_name != NULL &&
546 		    strcmp(name, namelist->list[i].ctl_name) == 0)
547 			break;
548 	if (i == namelist->size) {
549 		fprintf(stderr, "%s level name %s in %s is invalid\n",
550 		    level, name, string);
551 		return (-1);
552 	}
553 	return (i);
554 }
555 
556 usage()
557 {
558 
559 	(void)fprintf(stderr, "usage:\t%s\n\t%s\n\t%s\n\t%s\n",
560 	    "sysctl [-n] variable ...", "sysctl [-n] -w variable=value ...",
561 	    "sysctl [-n] -a", "sysctl [-n] -A");
562 	exit(1);
563 }
564