xref: /netbsd/sbin/sysctl/sysctl.c (revision bf9ec67e)
1 /*	$NetBSD: sysctl.c,v 1.58 2002/03/24 00:11:00 sommerfeld Exp $	*/
2 
3 /*
4  * Copyright (c) 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT(
39 "@(#) Copyright (c) 1993\n\
40 	The Regents of the University of California.  All rights reserved.\n");
41 #endif /* not lint */
42 
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)sysctl.c	8.1 (Berkeley) 6/6/93";
46 #else
47 __RCSID("$NetBSD: sysctl.c,v 1.58 2002/03/24 00:11:00 sommerfeld Exp $");
48 #endif
49 #endif /* not lint */
50 
51 #include <sys/param.h>
52 #include <sys/gmon.h>
53 #include <sys/stat.h>
54 #include <sys/sysctl.h>
55 #include <sys/socket.h>
56 #include <sys/mount.h>
57 #include <sys/mbuf.h>
58 #include <sys/resource.h>
59 #include <uvm/uvm_param.h>
60 #include <machine/cpu.h>
61 
62 #include <ufs/ufs/dinode.h>
63 #include <ufs/ufs/dir.h>
64 #include <ufs/ffs/fs.h>
65 #include <ufs/ffs/ffs_extern.h>
66 
67 #include <nfs/rpcv2.h>
68 #include <nfs/nfsproto.h>
69 #include <nfs/nfs.h>
70 
71 #include <netinet/in.h>
72 #include <netinet/in_systm.h>
73 #include <netinet/ip.h>
74 #include <netinet/ip_icmp.h>
75 #include <netinet/icmp_var.h>
76 #include <netinet/ip_var.h>
77 #include <netinet/udp.h>
78 #include <netinet/udp_var.h>
79 #include <netinet/tcp.h>
80 #include <netinet/tcp_timer.h>
81 #include <netinet/tcp_var.h>
82 
83 #ifdef INET6
84 #include <netinet/ip6.h>
85 #include <netinet/icmp6.h>
86 #include <netinet6/ip6_var.h>
87 #include <netinet6/udp6.h>
88 #include <netinet6/udp6_var.h>
89 #ifdef TCP6
90 #include <netinet6/tcp6.h>
91 #include <netinet6/tcp6_timer.h>
92 #include <netinet6/tcp6_var.h>
93 #endif
94 #include <netinet6/pim6_var.h>
95 #endif /* INET6 */
96 
97 #include "../../sys/compat/linux/common/linux_exec.h"
98 
99 #ifdef IPSEC
100 #include <net/route.h>
101 #include <netinet6/ipsec.h>
102 #include <netkey/key_var.h>
103 #endif /* IPSEC */
104 
105 #include <sys/pipe.h>
106 
107 #include <err.h>
108 #include <ctype.h>
109 #include <errno.h>
110 #include <stdio.h>
111 #include <stdlib.h>
112 #include <string.h>
113 #include <unistd.h>
114 #include <util.h>
115 
116 struct ctlname topname[] = CTL_NAMES;
117 struct ctlname kernname[] = CTL_KERN_NAMES;
118 struct ctlname vmname[] = CTL_VM_NAMES;
119 struct ctlname vfsname[] = CTL_VFS_NAMES;
120 struct ctlname netname[] = CTL_NET_NAMES;
121 struct ctlname hwname[] = CTL_HW_NAMES;
122 struct ctlname username[] = CTL_USER_NAMES;
123 struct ctlname ddbname[] = CTL_DDB_NAMES;
124 struct ctlname debugname[CTL_DEBUG_MAXID];
125 #ifdef CTL_MACHDEP_NAMES
126 struct ctlname machdepname[] = CTL_MACHDEP_NAMES;
127 #endif
128 struct ctlname emulname[] = CTL_EMUL_NAMES;
129 struct ctlname vendorname[] = { { 0, 0 } };
130 
131 /* this one is dummy, it's used only for '-a' or '-A' */
132 struct ctlname procname[] = { {0, 0}, {"curproc", CTLTYPE_NODE} };
133 
134 char names[BUFSIZ];
135 
136 struct list {
137 	struct	ctlname *list;
138 	int	size;
139 };
140 struct list toplist = { topname, CTL_MAXID };
141 struct list secondlevel[] = {
142 	{ 0, 0 },			/* CTL_UNSPEC */
143 	{ kernname, KERN_MAXID },	/* CTL_KERN */
144 	{ vmname, VM_MAXID },		/* CTL_VM */
145 	{ vfsname, VFS_MAXID },		/* CTL_VFS */
146 	{ netname, NET_MAXID },		/* CTL_NET */
147 	{ 0, CTL_DEBUG_MAXID },		/* CTL_DEBUG */
148 	{ hwname, HW_MAXID },		/* CTL_HW */
149 #ifdef CTL_MACHDEP_NAMES
150 	{ machdepname, CPU_MAXID },	/* CTL_MACHDEP */
151 #else
152 	{ 0, 0 },			/* CTL_MACHDEP */
153 #endif
154 	{ username, USER_MAXID },	/* CTL_USER_NAMES */
155 	{ ddbname, DDBCTL_MAXID },	/* CTL_DDB_NAMES */
156 	{ procname, 2 },		/* dummy name */
157 	{ vendorname, 0 },		/* CTL_VENDOR_NAMES */
158 	{ emulname, EMUL_MAXID },	/* CTL_EMUL_NAMES */
159 
160 	{ 0, 0},
161 };
162 
163 int	Aflag, aflag, nflag, qflag, wflag;
164 
165 /*
166  * Variables requiring special processing.
167  */
168 #define	CLOCK		0x00000001
169 #define	BOOTTIME	0x00000002
170 #define	CONSDEV		0x00000004
171 #define	DISKINFO	0x00000008
172 #define	CPTIME		0x00000010
173 
174 /*
175  * A dummy type for limits, which requires special parsing
176  */
177 #define CTLTYPE_LIMIT	((~0x1) << 31)
178 
179 int main(int, char *[]);
180 
181 static void listall(const char *, struct list *);
182 static void parse(char *, int);
183 static void debuginit(void);
184 static int sysctl_inet(char *, char **, int[], int, int *);
185 #ifdef INET6
186 static int sysctl_inet6(char *, char **, int[], int, int *);
187 #endif
188 static int sysctl_vfs(char *, char **, int[], int, int *);
189 static int sysctl_proc(char *, char **, int[], int, int *);
190 static int sysctl_3rd(struct list *, char *, char **, int[], int, int *);
191 
192 #ifdef IPSEC
193 struct ctlname keynames[] = KEYCTL_NAMES;
194 struct list keyvars = { keynames, KEYCTL_MAXID };
195 #endif /*IPSEC*/
196 struct ctlname vfsgenname[] = CTL_VFSGENCTL_NAMES;
197 struct list vfsgenvars = { vfsgenname, VFSGEN_MAXID };
198 struct ctlname mbufnames[] = CTL_MBUF_NAMES;
199 struct list mbufvars = { mbufnames, MBUF_MAXID };
200 struct ctlname pipenames[] = CTL_PIPE_NAMES;
201 struct list pipevars = { pipenames, KERN_PIPE_MAXID };
202 struct ctlname tkstatnames[] = KERN_TKSTAT_NAMES;
203 struct list tkstatvars = { tkstatnames, KERN_TKSTAT_MAXID };
204 
205 static int sysctl_linux(char *, char **, int[], int, int *);
206 static int findname(char *, char *, char **, struct list *);
207 static void usage(void);
208 
209 #define USEAPP(s, a) \
210     if (flags) printf("%s: use '%s' to view this information\n", s, a)
211 
212 
213 int
214 main(int argc, char *argv[])
215 {
216 	char *fn = NULL;
217 	int ch, lvl1;
218 
219 	while ((ch = getopt(argc, argv, "Aaf:nqw")) != -1) {
220 		switch (ch) {
221 
222 		case 'A':
223 			Aflag = 1;
224 			break;
225 
226 		case 'a':
227 			aflag = 1;
228 			break;
229 
230 		case 'f':
231 			fn = optarg;
232 			wflag = 1;
233 			break;
234 
235 		case 'n':
236 			nflag = 1;
237 			break;
238 
239 		case 'q':
240 			qflag = 1;
241 			break;
242 
243 		case 'w':
244 			wflag = 1;
245 			break;
246 
247 		default:
248 			usage();
249 		}
250 	}
251 
252 	if (qflag && !wflag)
253 		usage();
254 
255 	argc -= optind;
256 	argv += optind;
257 
258 	if (Aflag || aflag) {
259 		debuginit();
260 		for (lvl1 = 1; lvl1 < CTL_MAXID; lvl1++)
261 			listall(topname[lvl1].ctl_name, &secondlevel[lvl1]);
262 		return 0;
263 	}
264 
265 	if (fn) {
266 		FILE *fp;
267 		char *l;
268 
269 		fp = fopen(fn, "r");
270 		if (fp == NULL) {
271 			err(1, "%s", fn);
272 		} else {
273 			for (; (l = fparseln(fp, NULL, NULL, NULL, 0)) != NULL;
274 			    free(l)) {
275 				if (*l)
276 					parse(l, 1);
277 			}
278 			fclose(fp);
279 		}
280 	} else {
281 		if (argc == 0)
282 			usage();
283 		while (argc-- > 0)
284 			parse(*argv++, 1);
285 	}
286 	return 0;
287 }
288 
289 /*
290  * List all variables known to the system.
291  */
292 static void
293 listall(const char *prefix, struct list *lp)
294 {
295 	int lvl2;
296 	char *cp, name[BUFSIZ];
297 
298 	if (lp->list == 0)
299 		return;
300 	strcpy(name, prefix);
301 	cp = &name[strlen(name)];
302 	*cp++ = '.';
303 	for (lvl2 = 0; lvl2 < lp->size; lvl2++) {
304 		if (lp->list[lvl2].ctl_name == 0)
305 			continue;
306 		strcpy(cp, lp->list[lvl2].ctl_name);
307 		parse(name, Aflag);
308 	}
309 }
310 
311 /*
312  * Parse a name into a MIB entry.
313  * Lookup and print out the MIB entry if it exists.
314  * Set a new value if requested.
315  */
316 static void
317 parse(char *string, int flags)
318 {
319 	int indx, type, state, len;
320 	int special = 0;
321 	void *newval = 0;
322 	int intval, newsize = 0;
323 	quad_t quadval;
324 	size_t size;
325 	struct list *lp;
326 	int mib[CTL_MAXNAME];
327 	char *cp, *bufp, buf[BUFSIZ];
328 	double loads[3];
329 
330 	bufp = buf;
331 	snprintf(buf, BUFSIZ, "%s", string);
332 	if ((cp = strchr(string, '=')) != NULL) {
333 		if (!wflag)
334 			errx(2, "Must specify -w to set variables");
335 		*strchr(buf, '=') = '\0';
336 		*cp++ = '\0';
337 		while (isspace((unsigned char) *cp))
338 			cp++;
339 		newval = cp;
340 		newsize = strlen(cp);
341 	}
342 	if ((indx = findname(string, "top", &bufp, &toplist)) == -1)
343 		return;
344 	mib[0] = indx;
345 	if (indx == CTL_DEBUG)
346 		debuginit();
347 	if (mib[0] == CTL_PROC) {
348 		type = CTLTYPE_NODE;
349 		len = 1;
350 	} else {
351 		lp = &secondlevel[indx];
352 		if (lp->list == 0) {
353 			warnx("Class `%s' is not implemented",
354 			    topname[indx].ctl_name);
355 			return;
356 		}
357 		if (bufp == NULL) {
358 			listall(topname[indx].ctl_name, lp);
359 			return;
360 		}
361 		if ((indx = findname(string, "second", &bufp, lp)) == -1)
362 			return;
363 		mib[1] = indx;
364 		type = lp->list[indx].ctl_type;
365 		len = 2;
366 	}
367 	switch (mib[0]) {
368 
369 	case CTL_KERN:
370 		switch (mib[1]) {
371 		case KERN_PROF:
372 			mib[2] = GPROF_STATE;
373 			size = sizeof state;
374 			if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) {
375 				if (flags == 0)
376 					return;
377 				if (!nflag)
378 					printf("%s: ", string);
379 				printf(
380 				    "kernel is not compiled for profiling\n");
381 				return;
382 			}
383 			if (!nflag)
384 				printf("%s: %s\n", string,
385 				    state == GMON_PROF_OFF ? "off" : "running");
386 			return;
387 		case KERN_VNODE:
388 		case KERN_FILE:
389 			USEAPP(string, "pstat");
390 			return;
391 		case KERN_PROC:
392 		case KERN_PROC2:
393 		case KERN_PROC_ARGS:
394 			USEAPP(string, "ps");
395 			return;
396 		case KERN_CLOCKRATE:
397 			special |= CLOCK;
398 			break;
399 		case KERN_BOOTTIME:
400 			special |= BOOTTIME;
401 			break;
402 		case KERN_NTPTIME:
403 			USEAPP(string, "ntpdc -c kerninfo");
404 			return;
405 		case KERN_MBUF:
406 			len = sysctl_3rd(&mbufvars, string, &bufp, mib, flags,
407 			    &type);
408 			if (len < 0)
409 				return;
410 			break;
411 		case KERN_CP_TIME:
412 			special |= CPTIME;
413 			break;
414 		case KERN_MSGBUF:
415 			USEAPP(string, "dmesg");
416 			return;
417 		case KERN_CONSDEV:
418 			special |= CONSDEV;
419 			break;
420 		case KERN_PIPE:
421 			len = sysctl_3rd(&pipevars, string, &bufp, mib, flags,
422 			    &type);
423 			if (len < 0)
424 				return;
425 			break;
426 		case KERN_TKSTAT:
427 			len = sysctl_3rd(&tkstatvars, string, &bufp, mib, flags,
428 			    &type);
429 			if (len < 0)
430 				return;
431 			break;
432 		}
433 		break;
434 
435 	case CTL_HW:
436 		switch (mib[1]) {
437 		case HW_DISKSTATS:
438 			USEAPP(string, "iostat");
439 			return;
440 		}
441 		break;
442 
443 	case CTL_VM:
444 		switch (mib[1]) {
445 		case VM_LOADAVG:
446 			getloadavg(loads, 3);
447 			if (!nflag)
448 				printf("%s: ", string);
449 			printf("%.2f %.2f %.2f\n", loads[0], loads[1],
450 			    loads[2]);
451 			return;
452 
453 		case VM_METER:
454 		case VM_UVMEXP:
455 		case VM_UVMEXP2:
456 			USEAPP(string, "vmstat' or 'systat");
457 			return;
458 		}
459 		break;
460 
461 	case CTL_NET:
462 		if (mib[1] == PF_INET) {
463 			len = sysctl_inet(string, &bufp, mib, flags, &type);
464 			if (len >= 0)
465 				break;
466 			return;
467 		}
468 #ifdef INET6
469 		else if (mib[1] == PF_INET6) {
470 			len = sysctl_inet6(string, &bufp, mib, flags, &type);
471 			if (len >= 0)
472 				break;
473 			return;
474 		}
475 #endif /* INET6 */
476 #ifdef IPSEC
477 		else if (mib[1] == PF_KEY) {
478 			len = sysctl_3rd(&keyvars, string, &bufp, mib, flags,
479 			    &type);
480 			if (len >= 0)
481 				break;
482 			return;
483 		}
484 #endif /* IPSEC */
485 		if (flags == 0)
486 			return;
487 		USEAPP(string, "netstat");
488 		return;
489 
490 	case CTL_DEBUG:
491 		mib[2] = CTL_DEBUG_VALUE;
492 		len = 3;
493 		break;
494 
495 	case CTL_MACHDEP:
496 #ifdef CPU_CONSDEV
497 		if (mib[1] == CPU_CONSDEV)
498 			special |= CONSDEV;
499 #endif
500 #ifdef CPU_DISKINFO
501 		if (mib[1] == CPU_DISKINFO)
502 			special |= DISKINFO;
503 #endif
504 		break;
505 
506 	case CTL_VFS:
507 		if (mib[1] == VFS_GENERIC) {
508 			len = sysctl_3rd(&vfsgenvars, string, &bufp, mib, flags,
509 			    &type);
510 			/* Don't bother with VFS_CONF. */
511 			if (mib[2] == VFS_CONF)
512 				len = -1;
513 		} else
514 			len = sysctl_vfs(string, &bufp, mib, flags, &type);
515 		if (len < 0)
516 			return;
517 
518 		/* XXX Special-case for NFS stats. */
519 		if (mib[1] == 2 && mib[2] == NFS_NFSSTATS) {
520 			USEAPP(string, "nfsstat");
521 			return;
522 		}
523 		break;
524 
525 	case CTL_VENDOR:
526 	case CTL_USER:
527 	case CTL_DDB:
528 		break;
529 	case CTL_PROC:
530 		len = sysctl_proc(string, &bufp, mib, flags, &type);
531 		if (len < 0)
532 			return;
533 		break;
534 	case CTL_EMUL:
535 		switch (mib[1]) {
536 		case EMUL_LINUX:
537 		    len = sysctl_linux(string, &bufp, mib, flags, &type);
538 		    break;
539 		default:
540 		    warnx("Illegal emul level value: %d", mib[0]);
541 		    break;
542 		}
543 		if (len < 0)
544 			return;
545 		break;
546 	default:
547 		warnx("Illegal top level value: %d", mib[0]);
548 		return;
549 
550 	}
551 	if (bufp) {
552 		warnx("Name %s in %s is unknown", bufp, string);
553 		return;
554 	}
555 	if (newsize > 0) {
556 		switch (type) {
557 		case CTLTYPE_INT:
558 			intval = atoi(newval);
559 			newval = &intval;
560 			newsize = sizeof intval;
561 			break;
562 
563 		case CTLTYPE_LIMIT:
564 			if (strcmp(newval, "unlimited") == 0) {
565 				quadval = RLIM_INFINITY;
566 				newval = &quadval;
567 				newsize = sizeof quadval;
568 				break;
569 			}
570 			/* FALLTHROUGH */
571 		case CTLTYPE_QUAD:
572 			sscanf(newval, "%lld", (long long *)&quadval);
573 			newval = &quadval;
574 			newsize = sizeof quadval;
575 			break;
576 		}
577 	}
578 	size = BUFSIZ;
579 	if (sysctl(mib, len, buf, &size, newsize ? newval : 0, newsize) == -1) {
580 		if (flags == 0)
581 			return;
582 		switch (errno) {
583 		case EOPNOTSUPP:
584 			printf("%s: the value is not available\n", string);
585 			return;
586 		case ENOTDIR:
587 			printf("%s: the specification is incomplete\n", string);
588 			return;
589 		case ENOMEM:
590 			printf("%s: this type is unknown to this program\n",
591 			    string);
592 			return;
593 		default:
594 			printf("%s: sysctl() failed with %s\n",
595 			    string, strerror(errno));
596 			return;
597 		}
598 	}
599 	if (qflag && (newsize > 0))
600 		return;
601 	if (special & CLOCK) {
602 		struct clockinfo *clkp = (struct clockinfo *)buf;
603 
604 		if (!nflag)
605 			printf("%s: ", string);
606 		printf(
607 		    "tick = %d, tickadj = %d, hz = %d, profhz = %d, stathz = %d\n",
608 		    clkp->tick, clkp->tickadj, clkp->hz, clkp->profhz, clkp->stathz);
609 		return;
610 	}
611 	if (special & BOOTTIME) {
612 		struct timeval *btp = (struct timeval *)buf;
613 		time_t boottime;
614 
615 		if (!nflag) {
616 			boottime = btp->tv_sec;
617 			/* ctime() provides the trailing newline */
618 			printf("%s = %s", string, ctime(&boottime));
619 		} else
620 			printf("%ld\n", (long) btp->tv_sec);
621 		return;
622 	}
623 	if (special & CONSDEV) {
624 		dev_t dev = *(dev_t *)buf;
625 
626 		if (!nflag)
627 			printf("%s = %s\n", string, devname(dev, S_IFCHR));
628 		else
629 			printf("0x%x\n", dev);
630 		return;
631 	}
632 	if (special & DISKINFO) {
633 		/* Don't know a good way to deal with this i386 specific one */
634 		return;
635 	}
636 	if (special & CPTIME) {
637 		u_int64_t *cp_time = (u_int64_t *)buf;
638 
639 		if (!nflag)
640 			printf("%s: ", string);
641 		printf("user = %llu, nice = %llu, sys = %llu, intr = %llu, "
642 		    "idle = %llu\n", (unsigned long long) cp_time[0],
643 		    (unsigned long long) cp_time[1],
644 		    (unsigned long long) cp_time[2],
645 		    (unsigned long long) cp_time[3],
646 		    (unsigned long long) cp_time[4]);
647 		return;
648 	}
649 
650 	switch (type) {
651 	case CTLTYPE_INT:
652 		if (newsize == 0) {
653 			if (!nflag)
654 				printf("%s = ", string);
655 			printf("%d\n", *(int *)buf);
656 		} else {
657 			if (!nflag)
658 				printf("%s: %d -> ", string, *(int *)buf);
659 			printf("%d\n", *(int *)newval);
660 		}
661 		return;
662 
663 	case CTLTYPE_STRING:
664 		if (newsize == 0) {
665 			if (!nflag)
666 				printf("%s = ", string);
667 			printf("%s\n", buf);
668 		} else {
669 			if (!nflag)
670 				printf("%s: %s -> ", string, buf);
671 			printf("%s\n", (char *) newval);
672 		}
673 		return;
674 
675 	case CTLTYPE_LIMIT:
676 #define PRINTF_LIMIT(lim) { \
677 if ((lim) == RLIM_INFINITY) \
678 	printf("unlimited");\
679 else \
680 	printf("%lld", (long long)(lim)); \
681 }
682 
683 		if (newsize == 0) {
684 			if (!nflag)
685 				printf("%s = ", string);
686 			PRINTF_LIMIT((long long)(*(quad_t *)buf));
687 		} else {
688 			if (!nflag) {
689 				printf("%s: ", string);
690 				PRINTF_LIMIT((long long)(*(quad_t *)buf));
691 				printf(" -> ");
692 			}
693 			PRINTF_LIMIT((long long)(*(quad_t *)newval));
694 		}
695 		printf("\n");
696 		return;
697 #undef PRINTF_LIMIT
698 
699 	case CTLTYPE_QUAD:
700 		if (newsize == 0) {
701 			if (!nflag)
702 				printf("%s = ", string);
703 			printf("%lld\n", (long long)(*(quad_t *)buf));
704 		} else {
705 			if (!nflag)
706 				printf("%s: %lld -> ", string,
707 				    (long long)(*(quad_t *)buf));
708 			printf("%lld\n", (long long)(*(quad_t *)newval));
709 		}
710 		return;
711 
712 	case CTLTYPE_STRUCT:
713 		warnx("%s: unknown structure returned", string);
714 		return;
715 
716 	default:
717 	case CTLTYPE_NODE:
718 		warnx("%s: unknown type returned", string);
719 		return;
720 	}
721 }
722 
723 /*
724  * Initialize the set of debugging names
725  */
726 static void
727 debuginit(void)
728 {
729 	int mib[3], loc, i;
730 	size_t size;
731 
732 	if (secondlevel[CTL_DEBUG].list != 0)
733 		return;
734 	secondlevel[CTL_DEBUG].list = debugname;
735 	mib[0] = CTL_DEBUG;
736 	mib[2] = CTL_DEBUG_NAME;
737 	for (loc = 0, i = 0; i < CTL_DEBUG_MAXID; i++) {
738 		mib[1] = i;
739 		size = BUFSIZ - loc;
740 		if (sysctl(mib, 3, &names[loc], &size, NULL, 0) == -1)
741 			continue;
742 		debugname[i].ctl_name = &names[loc];
743 		debugname[i].ctl_type = CTLTYPE_INT;
744 		loc += size;
745 	}
746 }
747 
748 struct ctlname inetname[] = CTL_IPPROTO_NAMES;
749 struct ctlname ipname[] = IPCTL_NAMES;
750 struct ctlname icmpname[] = ICMPCTL_NAMES;
751 struct ctlname tcpname[] = TCPCTL_NAMES;
752 struct ctlname udpname[] = UDPCTL_NAMES;
753 #ifdef IPSEC
754 struct ctlname ipsecname[] = IPSECCTL_NAMES;
755 #endif
756 struct list inetlist = { inetname, IPPROTO_MAXID };
757 struct list inetvars[] = {
758 /*0*/	{ ipname, IPCTL_MAXID },	/* ip */
759 	{ icmpname, ICMPCTL_MAXID },	/* icmp */
760 	{ 0, 0 },			/* igmp */
761 	{ 0, 0 },			/* ggmp */
762 	{ 0, 0 },
763 	{ 0, 0 },
764 	{ tcpname, TCPCTL_MAXID },	/* tcp */
765 	{ 0, 0 },
766 	{ 0, 0 },			/* egp */
767 	{ 0, 0 },
768 /*10*/	{ 0, 0 },
769 	{ 0, 0 },
770 	{ 0, 0 },			/* pup */
771 	{ 0, 0 },
772 	{ 0, 0 },
773 	{ 0, 0 },
774 	{ 0, 0 },
775 	{ udpname, UDPCTL_MAXID },	/* udp */
776 	{ 0, 0 },
777 	{ 0, 0 },
778 /*20*/	{ 0, 0 },
779 	{ 0, 0 },
780 	{ 0, 0 },			/* idp */
781 	{ 0, 0 },
782 	{ 0, 0 },
783 	{ 0, 0 },
784 	{ 0, 0 },
785 	{ 0, 0 },
786 	{ 0, 0 },
787 	{ 0, 0 },
788 /*30*/	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
789 	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
790 /*40*/	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
791 	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
792 #ifdef IPSEC
793 	{ ipsecname, IPSECCTL_MAXID },	/* esp - for backward compatibility */
794 	{ ipsecname, IPSECCTL_MAXID },	/* ah */
795 #else
796 	{ 0, 0 },
797 	{ 0, 0 },
798 #endif
799 };
800 
801 /*
802  * handle internet requests
803  */
804 static int
805 sysctl_inet(char *string, char **bufpp, int mib[], int flags, int *typep)
806 {
807 	struct list *lp;
808 	int indx;
809 
810 	if (*bufpp == NULL) {
811 		listall(string, &inetlist);
812 		return (-1);
813 	}
814 	if ((indx = findname(string, "third", bufpp, &inetlist)) == -1)
815 		return (-1);
816 	mib[2] = indx;
817 	if (indx <= IPPROTO_MAXID && inetvars[indx].list != NULL)
818 		lp = &inetvars[indx];
819 	else if (!flags)
820 		return (-1);
821 	else {
822 		printf("%s: no variables defined for protocol\n", string);
823 		return (-1);
824 	}
825 	if (*bufpp == NULL) {
826 		listall(string, lp);
827 		return (-1);
828 	}
829 	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
830 		return (-1);
831 	mib[3] = indx;
832 	*typep = lp->list[indx].ctl_type;
833 	return (4);
834 }
835 
836 #ifdef INET6
837 struct ctlname inet6name[] = CTL_IPV6PROTO_NAMES;
838 struct ctlname ip6name[] = IPV6CTL_NAMES;
839 struct ctlname icmp6name[] = ICMPV6CTL_NAMES;
840 struct ctlname udp6name[] = UDP6CTL_NAMES;
841 struct ctlname pim6name[] = PIM6CTL_NAMES;
842 struct ctlname ipsec6name[] = IPSEC6CTL_NAMES;
843 struct list inet6list = { inet6name, IPV6PROTO_MAXID };
844 struct list inet6vars[] = {
845 /*0*/	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
846 	{ 0, 0 },
847 	{ tcpname, TCPCTL_MAXID },	/* tcp6 */
848 	{ 0, 0 },
849 	{ 0, 0 },
850 	{ 0, 0 },
851 /*10*/	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
852 	{ 0, 0 },
853 	{ 0, 0 },
854 	{ udp6name, UDP6CTL_MAXID },	/* udp6 */
855 	{ 0, 0 },
856 	{ 0, 0 },
857 /*20*/	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
858 	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
859 /*30*/	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
860 	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
861 /*40*/	{ 0, 0 },
862 	{ ip6name, IPV6CTL_MAXID },	/* ipv6 */
863 	{ 0, 0 },
864 	{ 0, 0 },
865 	{ 0, 0 },
866 	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
867 #ifdef IPSEC
868 /*50*/	{ ipsec6name, IPSECCTL_MAXID },	/* esp6 - for backward compatibility */
869 	{ ipsec6name, IPSECCTL_MAXID },	/* ah6 */
870 #else
871 	{ 0, 0 },
872 	{ 0, 0 },
873 #endif
874 	{ 0, 0 },
875 	{ 0, 0 },
876 	{ 0, 0 },
877 	{ 0, 0 },
878 	{ 0, 0 },
879 	{ 0, 0 },
880 	{ icmp6name, ICMPV6CTL_MAXID },	/* icmp6 */
881 	{ 0, 0 },
882 /*60*/	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
883 	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
884 /*70*/	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
885 	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
886 /*80*/	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
887 	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
888 /*90*/	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
889 	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
890 /*100*/	{ 0, 0 },
891 	{ 0, 0 },
892 	{ 0, 0 },
893 	{ pim6name, PIM6CTL_MAXID },	/* pim6 */
894 };
895 
896 /*
897  * handle internet6 requests
898  */
899 static int
900 sysctl_inet6(char *string, char **bufpp, int mib[], int flags, int *typep)
901 {
902 	struct list *lp;
903 	int indx;
904 
905 	if (*bufpp == NULL) {
906 		listall(string, &inet6list);
907 		return (-1);
908 	}
909 	if ((indx = findname(string, "third", bufpp, &inet6list)) == -1)
910 		return (-1);
911 	mib[2] = indx;
912 	if (indx <= sizeof(inet6vars)/sizeof(inet6vars[0])
913 	 && inet6vars[indx].list != NULL) {
914 		lp = &inet6vars[indx];
915 	} else if (!flags) {
916 		return (-1);
917 	} else {
918 		fprintf(stderr, "%s: no variables defined for this protocol\n",
919 		    string);
920 		return (-1);
921 	}
922 	if (*bufpp == NULL) {
923 		listall(string, lp);
924 		return (-1);
925 	}
926 	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
927 		return (-1);
928 	mib[3] = indx;
929 	*typep = lp->list[indx].ctl_type;
930 	return (4);
931 }
932 #endif /* INET6 */
933 
934 struct ctlname ffsname[] = FFS_NAMES;
935 struct ctlname nfsname[] = NFS_NAMES;
936 struct list vfsvars[] = {
937 	{ 0, 0 },			/* generic */
938 	{ ffsname, FFS_MAXID },		/* FFS */
939 	{ nfsname, NFS_MAXID },		/* NFS */
940 	{ 0, 0 },			/* MFS */
941 	{ 0, 0 },			/* MSDOS */
942 	{ 0, 0 },			/* LFS */
943 	{ 0, 0 },			/* old LOFS */
944 	{ 0, 0 },			/* FDESC */
945 	{ 0, 0 },			/* PORTAL */
946 	{ 0, 0 },			/* NULL */
947 	{ 0, 0 },			/* UMAP */
948 	{ 0, 0 },			/* KERNFS */
949 	{ 0, 0 },			/* PROCFS */
950 	{ 0, 0 },			/* AFS */
951 	{ 0, 0 },			/* CD9660 */
952 	{ 0, 0 },			/* UNION */
953 	{ 0, 0 },			/* ADOSFS */
954 	{ 0, 0 },			/* EXT2FS */
955 	{ 0, 0 },			/* CODA */
956 	{ 0, 0 },			/* FILECORE */
957 };
958 
959 /*
960  * handle vfs requests
961  */
962 static int
963 sysctl_vfs(char *string, char **bufpp, int mib[], int flags, int *typep)
964 {
965 	struct list *lp = &vfsvars[mib[1]];
966 	int indx;
967 
968 	if (lp->list == NULL) {
969 		if (flags)
970 			printf("%s: no variables defined for file system\n",
971 			    string);
972 		return (-1);
973 	}
974 	if (*bufpp == NULL) {
975 		listall(string, lp);
976 		return (-1);
977 	}
978 	if ((indx = findname(string, "third", bufpp, lp)) == -1)
979 		return (-1);
980 	mib[2] = indx;
981 	*typep = lp->list[indx].ctl_type;
982 	return (3);
983 }
984 
985 /*
986  * handle 3rd level requests.
987  */
988 static int
989 sysctl_3rd(struct list *lp, char *string, char **bufpp, int mib[], int flags,
990     int *typep)
991 {
992 	int indx;
993 
994 	if (*bufpp == NULL) {
995 		listall(string, lp);
996 		return (-1);
997 	}
998 	if ((indx = findname(string, "third", bufpp, lp)) == -1)
999 		return (-1);
1000 	mib[2] = indx;
1001 	*typep = lp->list[indx].ctl_type;
1002 	return (3);
1003 }
1004 
1005 struct ctlname procnames[] = PROC_PID_NAMES;
1006 struct list procvars = {procnames, PROC_PID_MAXID};
1007 struct ctlname proclimitnames[] = PROC_PID_LIMIT_NAMES;
1008 struct list proclimitvars = {proclimitnames, PROC_PID_LIMIT_MAXID};
1009 struct ctlname proclimittypenames[] = PROC_PID_LIMIT_TYPE_NAMES;
1010 struct list proclimittypevars = {proclimittypenames,
1011     PROC_PID_LIMIT_TYPE_MAXID};
1012 /*
1013  * handle kern.proc requests
1014  */
1015 static int
1016 sysctl_proc(char *string, char **bufpp, int mib[], int flags, int *typep)
1017 {
1018 	char *cp, name[BUFSIZ];
1019 	struct list *lp;
1020 	int indx;
1021 
1022 	if (*bufpp == NULL) {
1023 		strcpy(name, string);
1024 		cp = &name[strlen(name)];
1025 		*cp++ = '.';
1026 		strcpy(cp, "curproc");
1027 		parse(name, Aflag);
1028 		return (-1);
1029 	}
1030 	cp = strsep(bufpp, ".");
1031 	if (cp == NULL) {
1032 		warnx("%s: incomplete specification", string);
1033 		return (-1);
1034 	}
1035 	if (strcmp(cp, "curproc") == 0) {
1036 		mib[1] = PROC_CURPROC;
1037 	} else {
1038 		mib[1] = atoi(cp);
1039 		if (mib[1] == 0) {
1040 			warnx("second level name %s in %s is invalid", cp,
1041 			    string);
1042 			return (-1);
1043 		}
1044 	}
1045 	*typep = CTLTYPE_NODE;
1046 	lp = &procvars;
1047 	if (*bufpp == NULL) {
1048 		listall(string, lp);
1049 		return (-1);
1050 	}
1051 	if ((indx = findname(string, "third", bufpp, lp)) == -1)
1052 		return (-1);
1053 	mib[2] = indx;
1054 	*typep = lp->list[indx].ctl_type;
1055 	if (*typep != CTLTYPE_NODE)
1056 		return(3);
1057 	lp = &proclimitvars;
1058 	if (*bufpp == NULL) {
1059 		listall(string, lp);
1060 		return (-1);
1061 	}
1062 	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
1063 		return (-1);
1064 	mib[3] = indx;
1065 	lp = &proclimittypevars;
1066 	if (*bufpp == NULL) {
1067 		listall(string, lp);
1068 		return (-1);
1069 	}
1070 	if ((indx = findname(string, "fifth", bufpp, lp)) == -1)
1071 		return (-1);
1072 	mib[4] = indx;
1073 	*typep = CTLTYPE_LIMIT;
1074 	return(5);
1075 }
1076 
1077 struct ctlname linuxnames[] = EMUL_LINUX_NAMES;
1078 struct list linuxvars = { linuxnames, EMUL_LINUX_MAXID };
1079 struct ctlname linuxkernnames[] = EMUL_LINUX_KERN_NAMES;
1080 struct list linuxkernvars = { linuxkernnames, EMUL_LINUX_KERN_MAXID };
1081 
1082 static int
1083 sysctl_linux(char *string, char **bufpp, int mib[], int flags, int *typep)
1084 {
1085 	struct list *lp = &linuxvars;
1086 	int indx;
1087 	char name[BUFSIZ], *cp;
1088 
1089 	if (*bufpp == NULL) {
1090 		(void)strcpy(name, string);
1091 		cp = &name[strlen(name)];
1092 		*cp++ = '.';
1093 		(void)strcpy(cp, "kern");
1094 		listall(name, &linuxkernvars);
1095 		return (-1);
1096 	}
1097 	if ((indx = findname(string, "third", bufpp, lp)) == -1)
1098 		return (-1);
1099 	mib[2] = indx;
1100 	lp = &linuxkernvars;
1101 	*typep = lp->list[indx].ctl_type;
1102 	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
1103 		return (-1);
1104 	mib[3] = indx;
1105 	*typep = lp->list[indx].ctl_type;
1106 	return (4);
1107 }
1108 
1109 /*
1110  * Scan a list of names searching for a particular name.
1111  */
1112 static int
1113 findname(char *string, char *level, char **bufp, struct list *namelist)
1114 {
1115 	char *name;
1116 	int i;
1117 
1118 	if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
1119 		warnx("%s: incomplete specification", string);
1120 		return (-1);
1121 	}
1122 	for (i = 0; i < namelist->size; i++)
1123 		if (namelist->list[i].ctl_name != NULL &&
1124 		    strcmp(name, namelist->list[i].ctl_name) == 0)
1125 			break;
1126 	if (i == namelist->size) {
1127 		warnx("%s level name %s in %s is invalid",
1128 		    level, name, string);
1129 		return (-1);
1130 	}
1131 	return (i);
1132 }
1133 
1134 static void
1135 usage(void)
1136 {
1137 	const char *progname = getprogname();
1138 
1139 	(void)fprintf(stderr,
1140 	    "Usage:\t%s %s\n\t%s %s\n\t%s %s\n\t%s %s\n\t%s %s\n",
1141 	    progname, "[-n] variable ...",
1142 	    progname, "[-n] [-q] -w variable=value ...",
1143 	    progname, "[-n] -a",
1144 	    progname, "[-n] -A",
1145 	    progname, "[-n] [-q] -f file");
1146 	exit(1);
1147 }
1148