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