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