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