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