xref: /original-bsd/usr.sbin/sysctl/sysctl.c (revision cb36a3b0)
1 /*
2  * Copyright (c) 1993 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1993 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)sysctl.c	5.8 (Berkeley) 04/27/93";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/stat.h>
20 #include <sys/sysctl.h>
21 #include <sys/socket.h>
22 #include <vm/vm_param.h>
23 
24 #include <netinet/in.h>
25 #include <netinet/in_systm.h>
26 #include <netinet/ip.h>
27 #include <netinet/ip_icmp.h>
28 #include <netinet/icmp_var.h>
29 #include <netinet/ip_var.h>
30 #include <netinet/udp.h>
31 #include <netinet/udp_var.h>
32 
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 struct ctlname topname[] = CTL_NAMES;
39 struct ctlname kernname[] = CTL_KERN_NAMES;
40 struct ctlname vmname[] = CTL_VM_NAMES;
41 struct ctlname netname[] = CTL_NET_NAMES;
42 struct ctlname hwname[] = CTL_HW_NAMES;
43 struct ctlname debugname[CTL_DEBUG_MAXID];
44 char names[BUFSIZ];
45 
46 struct list {
47 	struct	ctlname *list;
48 	int	size;
49 };
50 struct list toplist = { topname, CTL_MAXID };
51 struct list secondlevel[] = {
52 	{ 0, 0 },			/* CTL_UNSPEC */
53 	{ kernname, KERN_MAXID },	/* CTL_KERN */
54 	{ vmname, VM_MAXID },		/* CTL_VM */
55 	{ 0, 0 },			/* CTL_FS */
56 	{ netname, NET_MAXID },		/* CTL_NET */
57 	{ 0, CTL_DEBUG_MAXID },		/* CTL_DEBUG */
58 	{ hwname, HW_MAXID },		/* CTL_HW */
59 	{ 0, 0 },			/* CTL_MACHDEP */
60 };
61 
62 int	Aflag, aflag, nflag, wflag;
63 
64 int
65 main(argc, argv)
66 	int argc;
67 	char *argv[];
68 {
69 	extern char *optarg;
70 	extern int optind;
71 	int ch, lvl1;
72 
73 	while ((ch = getopt(argc, argv, "Aanw")) != EOF) {
74 		switch (ch) {
75 
76 		case 'A':
77 			Aflag = 1;
78 			break;
79 
80 		case 'a':
81 			aflag = 1;
82 			break;
83 
84 		case 'n':
85 			nflag = 1;
86 			break;
87 
88 		case 'w':
89 			wflag = 1;
90 			break;
91 
92 		default:
93 			usage();
94 		}
95 	}
96 	argc -= optind;
97 	argv += optind;
98 
99 	if (Aflag || aflag) {
100 		debuginit();
101 		for (lvl1 = 1; lvl1 < CTL_MAXID; lvl1++)
102 			listall(topname[lvl1].ctl_name, &secondlevel[lvl1]);
103 		exit(0);
104 	}
105 	if (argc == 0)
106 		usage();
107 	while (argc-- > 0)
108 		parse(*argv, 1);
109 	exit(0);
110 }
111 
112 /*
113  * List all variables known to the system.
114  */
115 listall(prefix, lp)
116 	char *prefix;
117 	struct list *lp;
118 {
119 	int lvl2;
120 	char *cp, name[BUFSIZ];
121 
122 	if (lp->list == 0)
123 		return;
124 	strcpy(name, prefix);
125 	cp = &name[strlen(name)];
126 	*cp++ = '.';
127 	for (lvl2 = 0; lvl2 < lp->size; lvl2++) {
128 		if (lp->list[lvl2].ctl_name == 0)
129 			continue;
130 		strcpy(cp, lp->list[lvl2].ctl_name);
131 		parse(name, Aflag);
132 	}
133 }
134 
135 /*
136  * Parse a name into a MIB entry.
137  * Lookup and print out the MIB entry if it exists.
138  * Set a new value if requested.
139  */
140 parse(string, flags)
141 	char *string;
142 	int flags;
143 {
144 	int indx, type, size, len;
145 	int isclockrate = 0;
146 	void *newval = 0;
147 	int intval, newsize = 0;
148 	quad_t quadval;
149 	struct list *lp;
150 	int mib[CTL_MAXNAME];
151 	char *cp, *bufp, buf[BUFSIZ], strval[BUFSIZ];
152 
153 	bufp = buf;
154 	snprintf(buf, BUFSIZ, "%s", string);
155 	if ((cp = strchr(string, '=')) != NULL) {
156 		if (!wflag) {
157 			fprintf(stderr, "Must specify -w to set variables\n");
158 			exit(2);
159 		}
160 		*strchr(buf, '=') = '\0';
161 		*cp++ = '\0';
162 		while (isspace(*cp))
163 			cp++;
164 		newval = cp;
165 		newsize = strlen(cp);
166 	}
167 	if ((indx = findname(string, "top", &bufp, &toplist)) == -1)
168 		return;
169 	mib[0] = indx;
170 	if (indx == CTL_DEBUG)
171 		debuginit();
172 	lp = &secondlevel[indx];
173 	if (lp->list == 0) {
174 		fprintf(stderr, "%s: class is not implemented\n",
175 		    topname[indx]);
176 		return;
177 	}
178 	if (bufp == NULL) {
179 		listall(topname[indx].ctl_name, lp);
180 		return;
181 	}
182 	if ((indx = findname(string, "second", &bufp, lp)) == -1)
183 		return;
184 	mib[1] = indx;
185 	type = lp->list[indx].ctl_type;
186 	len = 2;
187 	switch (mib[0]) {
188 
189 	case CTL_KERN:
190 		switch (mib[1]) {
191 		case KERN_VNODE:
192 		case KERN_FILE:
193 			if (flags == 0)
194 				return;
195 			fprintf(stderr,
196 			    "Use pstat to view %s information\n", string);
197 			return;
198 		case KERN_PROC:
199 			if (flags == 0)
200 				return;
201 			fprintf(stderr,
202 			    "Use ps to view %s information\n", string);
203 			return;
204 		case KERN_CLOCKRATE:
205 			isclockrate = 1;
206 			break;
207 		}
208 		break;
209 
210 	case CTL_HW:
211 		break;
212 
213 	case CTL_VM:
214 		if (mib[1] == VM_LOADAVG) {
215 			double loads[3];
216 
217 			getloadavg(loads, 3);
218 			if (!nflag)
219 				fprintf(stdout, "%s: ", string);
220 			fprintf(stdout, "%.2f %.2f %.2f\n",
221 			    loads[0], loads[1], loads[2]);
222 			return;
223 		}
224 		if (flags == 0)
225 			return;
226 		fprintf(stderr,
227 		    "Use vmstat or systat to view %s information\n", string);
228 		return;
229 
230 	case CTL_NET:
231 		if (mib[1] == PF_INET) {
232 			len = sysctl_inet(string, &bufp, mib, flags, &type);
233 			if (len >= 0)
234 				break;
235 			return;
236 		}
237 		if (flags == 0)
238 			return;
239 		fprintf(stderr, "Use netstat to view %s information\n", string);
240 		return;
241 
242 	case CTL_DEBUG:
243 		mib[2] = CTL_DEBUG_VALUE;
244 		len = 3;
245 		break;
246 
247 	case CTL_FS:
248 	case CTL_MACHDEP:
249 		break;
250 
251 	default:
252 		fprintf(stderr, "Illegal top level value: %d\n", mib[0]);
253 		return;
254 
255 	}
256 	if (bufp) {
257 		fprintf(stderr, "name %s in %s is unknown\n", *bufp, string);
258 		return;
259 	}
260 	if (newsize > 0) {
261 		switch (type) {
262 		case CTLTYPE_INT:
263 			intval = atoi(newval);
264 			newval = &intval;
265 			newsize = sizeof intval;
266 			break;
267 
268 		case CTLTYPE_QUAD:
269 			sscanf(newval, "%qd", &quadval);
270 			newval = &quadval;
271 			newsize = sizeof quadval;
272 			break;
273 		}
274 	}
275 	size = BUFSIZ;
276 	if (sysctl(mib, len, buf, &size, newsize ? newval : 0, newsize) == -1) {
277 		if (flags == 0)
278 			return;
279 		switch (errno) {
280 		case EOPNOTSUPP:
281 			fprintf(stderr, "%s: value is not available\n", string);
282 			return;
283 		case ENOTDIR:
284 			fprintf(stderr, "%s: specification is incomplete\n",
285 			    string);
286 			return;
287 		case ENOMEM:
288 			fprintf(stderr, "%s: type is unknown to this program\n",
289 			    string);
290 			return;
291 		default:
292 			perror(string);
293 			return;
294 		}
295 	}
296 	if (isclockrate) {
297 		struct clockinfo *clkp = (struct clockinfo *)buf;
298 
299 		if (!nflag)
300 			fprintf(stdout, "%s: ", string);
301 		fprintf(stdout,
302 		    "hz = %d, tick = %d, profhz = %d, stathz = %d\n",
303 		    clkp->hz, clkp->tick, clkp->profhz, clkp->stathz);
304 		return;
305 	}
306 	switch (type) {
307 	case CTLTYPE_INT:
308 		if (newsize == 0) {
309 			if (!nflag)
310 				fprintf(stdout, "%s = ", string);
311 			fprintf(stdout, "%d\n", *(int *)buf);
312 		} else {
313 			if (!nflag)
314 				fprintf(stdout, "%s: %d -> ", string,
315 				    *(int *)buf);
316 			fprintf(stdout, "%d\n", *(int *)newval);
317 		}
318 		return;
319 
320 	case CTLTYPE_STRING:
321 		if (newsize == 0) {
322 			if (!nflag)
323 				fprintf(stdout, "%s = ", string);
324 			fprintf(stdout, "%s\n", buf);
325 		} else {
326 			if (!nflag)
327 				fprintf(stdout, "%s: %s -> ", string, buf);
328 			fprintf(stdout, "%s\n", newval);
329 		}
330 		return;
331 
332 	case CTLTYPE_QUAD:
333 		if (newsize == 0) {
334 			if (!nflag)
335 				fprintf(stdout, "%s = ", string);
336 			fprintf(stdout, "%qd\n", *(quad_t *)buf);
337 		} else {
338 			if (!nflag)
339 				fprintf(stdout, "%s: %qd -> ", string,
340 				    *(quad_t *)buf);
341 			fprintf(stdout, "%qd\n", *(quad_t *)newval);
342 		}
343 		return;
344 
345 	case CTLTYPE_STRUCT:
346 		fprintf(stderr, "%s: unknown structure returned\n",
347 		    string);
348 		return;
349 
350 	default:
351 	case CTLTYPE_NODE:
352 		fprintf(stderr, "%s: unknown type returned\n",
353 		    string);
354 		return;
355 	}
356 }
357 
358 /*
359  * Initialize the set of debugging names
360  */
361 debuginit()
362 {
363 	int mib[3], size, loc, i;
364 
365 	if (secondlevel[CTL_DEBUG].list != 0)
366 		return;
367 	secondlevel[CTL_DEBUG].list = debugname;
368 	mib[0] = CTL_DEBUG;
369 	mib[2] = CTL_DEBUG_NAME;
370 	for (loc = 0, i = 0; i < CTL_DEBUG_MAXID; i++) {
371 		mib[1] = i;
372 		size = BUFSIZ - loc;
373 		if (sysctl(mib, 3, &names[loc], &size, NULL, 0) == -1)
374 			continue;
375 		debugname[i].ctl_name = &names[loc];
376 		debugname[i].ctl_type = CTLTYPE_INT;
377 		loc += size;
378 	}
379 }
380 
381 struct ctlname inetname[] = CTL_IPPROTO_NAMES;
382 struct ctlname ipname[] = IPCTL_NAMES;
383 struct ctlname icmpname[] = ICMPCTL_NAMES;
384 struct ctlname udpname[] = UDPCTL_NAMES;
385 struct list inetlist = { inetname, IPPROTO_MAXID };
386 struct list inetvars[] = {
387 	{ ipname, IPCTL_MAXID },	/* ip */
388 	{ icmpname, ICMPCTL_MAXID },	/* icmp */
389 	{ 0, 0 },			/* igmp */
390 	{ 0, 0 },			/* ggmp */
391 	{ 0, 0 },
392 	{ 0, 0 },
393 	{ 0, 0 },			/* tcp */
394 	{ 0, 0 },
395 	{ 0, 0 },			/* egp */
396 	{ 0, 0 },
397 	{ 0, 0 },
398 	{ 0, 0 },
399 	{ 0, 0 },			/* pup */
400 	{ 0, 0 },
401 	{ 0, 0 },
402 	{ 0, 0 },
403 	{ 0, 0 },
404 	{ udpname, UDPCTL_MAXID },	/* udp */
405 };
406 
407 /*
408  * handle internet requests
409  */
410 sysctl_inet(string, bufpp, mib, flags, typep)
411 	char *string;
412 	char **bufpp;
413 	int mib[];
414 	int flags;
415 	int *typep;
416 {
417 	struct list *lp;
418 	int indx;
419 
420 	if (*bufpp == NULL) {
421 		listall(string, &inetlist);
422 		return (-1);
423 	}
424 	if ((indx = findname(string, "third", bufpp, &inetlist)) == -1)
425 		return (-1);
426 	mib[2] = indx;
427 	if (indx <= IPPROTO_UDP && inetvars[indx].list != NULL)
428 		lp = &inetvars[indx];
429 	else if (!flags)
430 		return (-1);
431 	else {
432 		fprintf(stderr, "%s: no variables defined for this protocol\n",
433 		    string);
434 		return (-1);
435 	}
436 	if (*bufpp == NULL) {
437 		listall(string, lp);
438 		return (-1);
439 	}
440 	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
441 		return (-1);
442 	mib[3] = indx;
443 	*typep = lp->list[indx].ctl_type;
444 	return (4);
445 }
446 
447 /*
448  * Scan a list of names searching for a particular name.
449  */
450 findname(string, level, bufp, namelist)
451 	char *string;
452 	char *level;
453 	char **bufp;
454 	struct list *namelist;
455 {
456 	char *name;
457 	int i;
458 
459 	if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
460 		fprintf(stderr, "%s: incomplete specification\n", string);
461 		return (-1);
462 	}
463 	for (i = 0; i < namelist->size; i++)
464 		if (namelist->list[i].ctl_name != NULL &&
465 		    strcmp(name, namelist->list[i].ctl_name) == 0)
466 			break;
467 	if (i == namelist->size) {
468 		fprintf(stderr, "%s level name %s in %s is invalid\n",
469 		    level, name, string);
470 		return (-1);
471 	}
472 	return (i);
473 }
474 
475 usage()
476 {
477 
478 	(void)fprintf(stderr, "usage:\t%s\n\t%s\n\t%s\n\t%s\n",
479 	    "sysctl [-n] variable ...", "sysctl [-n] -w variable=value ...",
480 	    "sysctl [-n] -a", "sysctl [-n] -A");
481 	exit(1);
482 }
483