xref: /netbsd-src/sbin/sysctl/sysctl.c (revision 76c7fc5f6b13ed0b1508e6b313e88e59977ed78e)
1 /*	$NetBSD: sysctl.c,v 1.162 2019/08/18 04:10:22 kamil Exp $ */
2 
3 /*-
4  * Copyright (c) 2003 The NetBSD Foundation, Inc.
5  *	All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Brown.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 1993
34  *	The Regents of the University of California.  All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. Neither the name of the University nor the names of its contributors
45  *    may be used to endorse or promote products derived from this software
46  *    without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58  * SUCH DAMAGE.
59  */
60 
61 #include <sys/cdefs.h>
62 #ifndef lint
63 __COPYRIGHT("@(#) Copyright (c) 1993\
64  The Regents of the University of California.  All rights reserved.");
65 #endif /* not lint */
66 
67 #ifndef lint
68 #if 0
69 static char sccsid[] = "@(#)sysctl.c	8.1 (Berkeley) 6/6/93";
70 #else
71 __RCSID("$NetBSD: sysctl.c,v 1.162 2019/08/18 04:10:22 kamil Exp $");
72 #endif
73 #endif /* not lint */
74 
75 #include <sys/types.h>
76 #include <sys/param.h>
77 #include <sys/sysctl.h>
78 #include <sys/mount.h>
79 #include <sys/resource.h>
80 #include <sys/stat.h>
81 #include <sys/sched.h>
82 #include <sys/socket.h>
83 #include <sys/bitops.h>
84 #include <netinet/in.h>
85 #include <netinet/ip_var.h>
86 #include <netinet/tcp.h>
87 #include <netinet/tcp_timer.h>
88 #include <netinet/tcp_var.h>
89 #include <netinet/icmp6.h>
90 #include <nfs/rpcv2.h>
91 #include <nfs/nfsproto.h>
92 #include <nfs/nfs.h>
93 #include <machine/cpu.h>
94 
95 #include <assert.h>
96 #include <ctype.h>
97 #include <err.h>
98 #include <errno.h>
99 #include <inttypes.h>
100 #include <regex.h>
101 #include <stdarg.h>
102 #include <stdio.h>
103 #include <stdlib.h>
104 #include <string.h>
105 #include <time.h>
106 #include <unistd.h>
107 
108 #include "prog_ops.h"
109 
110 /*
111  * this needs to be able to do the printing and the setting
112  */
113 #define HANDLER_PROTO const char *, const char *, char *, \
114 	int *, u_int, const struct sysctlnode *, \
115 	u_int, void *
116 #define HANDLER_ARGS const char *sname, const char *dname, char *value, \
117 	int *name, u_int namelen, const struct sysctlnode *pnode, \
118 	u_int type, void *v
119 #define DISPLAY_VALUE	0
120 #define DISPLAY_OLD	1
121 #define DISPLAY_NEW	2
122 
123 /*
124  * generic routines
125  */
126 static const struct handlespec *findhandler(const char *, regex_t *, size_t *);
127 static void canonicalize(const char *, char *);
128 static void purge_tree(struct sysctlnode *);
129 static void print_tree(int *, u_int, struct sysctlnode *, u_int, int, regex_t *,
130     size_t *);
131 static void write_number(int *, u_int, struct sysctlnode *, char *, bool);
132 static void write_string(int *, u_int, struct sysctlnode *, char *, bool);
133 static void display_number(const struct sysctlnode *, const char *,
134 			   const void *, size_t, int);
135 static void display_string(const struct sysctlnode *, const char *,
136 			   const void *, size_t, int);
137 static void display_struct(const struct sysctlnode *, const char *,
138 			   const void *, size_t, int);
139 static void hex_dump(const unsigned char *, size_t);
140 __dead static void usage(void);
141 static void parse(char *, regex_t *, size_t *);
142 static void parse_create(char *);
143 static void parse_destroy(char *);
144 static void parse_describe(char *);
145 static void getdesc1(int *, u_int, struct sysctlnode *);
146 static void getdesc(int *, u_int, struct sysctlnode *);
147 static void trim_whitespace(char *, int);
148 static void sysctlerror(int);
149 static void sysctlparseerror(u_int, const char *);
150 static void sysctlperror(const char *, ...) __printflike(1, 2);
151 #define EXIT(n) do { \
152 	if (fn == NULL) exit(n); else return; } while (/*CONSTCOND*/0)
153 
154 /*
155  * "borrowed" from libc:sysctlgetmibinfo.c
156  */
157 int __learn_tree(int *, u_int, struct sysctlnode *);
158 
159 /*
160  * "handlers"
161  */
162 static void printother(HANDLER_PROTO);
163 static void kern_clockrate(HANDLER_PROTO);
164 static void kern_boottime(HANDLER_PROTO);
165 static void kern_consdev(HANDLER_PROTO);
166 static void kern_cp_time(HANDLER_PROTO);
167 static void kern_cp_id(HANDLER_PROTO);
168 static void kern_drivers(HANDLER_PROTO);
169 static void vm_loadavg(HANDLER_PROTO);
170 static void proc_limit(HANDLER_PROTO);
171 #ifdef CPU_DISKINFO
172 static void machdep_diskinfo(HANDLER_PROTO);
173 #endif /* CPU_DISKINFO */
174 static void mode_bits(HANDLER_PROTO);
175 static void reserve(HANDLER_PROTO);
176 
177 static const struct handlespec {
178 	const char *ps_re;
179 	void (*ps_p)(HANDLER_PROTO);
180 	void (*ps_w)(HANDLER_PROTO);
181 	const void *ps_d;
182 } handlers[] = {
183 	{ "/kern/clockrate",			kern_clockrate, NULL, NULL },
184 	{ "/kern/evcnt",			printother, NULL, "vmstat -e" },
185 	{ "/kern/vnode",			printother, NULL, "pstat" },
186 	{ "/kern/proc(2|_args)?",		printother, NULL, "ps" },
187 	{ "/kern/file2?",			printother, NULL, "pstat" },
188 	{ "/kern/ntptime",			printother, NULL,
189 						"ntpdc -c kerninfo" },
190 	{ "/kern/msgbuf",			printother, NULL, "dmesg" },
191 	{ "/kern/boottime",			kern_boottime, NULL, NULL },
192 	{ "/kern/consdev",			kern_consdev, NULL, NULL },
193 	{ "/kern/cp_time(/[0-9]+)?",		kern_cp_time, NULL, NULL },
194 	{ "/kern/sysvipc_info",			printother, NULL, "ipcs" },
195 	{ "/kern/cp_id(/[0-9]+)?",		kern_cp_id, NULL, NULL },
196 
197 	{ "/kern/coredump/setid/mode",		mode_bits, mode_bits, NULL },
198 	{ "/kern/drivers",			kern_drivers, NULL, NULL },
199 
200 	{ "/kern/intr/list",			printother, NULL, "intrctl" },
201 	{ "/kern/intr/affinity",		printother, NULL, "intrctl" },
202 	{ "/kern/intr/intr",			printother, NULL, "intrctl" },
203 	{ "/kern/intr/nointr",			printother, NULL, "intrctl" },
204 
205 	{ "/vm/vmmeter",			printother, NULL,
206 						"vmstat' or 'systat" },
207 	{ "/vm/loadavg",			vm_loadavg, NULL, NULL },
208 	{ "/vm/uvmexp2?",			printother, NULL,
209 						"vmstat' or 'systat" },
210 
211 	{ "/vfs/nfs/nfsstats",			printother, NULL, "nfsstat" },
212 
213 	{ "/net/inet6?/tcp6?/ident",		printother, NULL, "identd" },
214 	{ "/net/inet6/icmp6/nd6_[dp]rlist",	printother, NULL, "ndp" },
215 	{ "/net/inet6/ip6/addctlpolicy",	printother, NULL,
216 						"ip6addrctl" },
217 	{ "/net/key/dumps[ap]",			printother, NULL, "setkey" },
218 	{ "/net/[^/]+/[^/]+/pcblist",		printother, NULL,
219 						"netstat' or 'sockstat" },
220 	{ "/net/(inet|inet6)/[^/]+/stats",	printother, NULL, "netstat"},
221 	{ "/net/inet/(ipip|esp|ah|ipcomp)/.*_stats",
222 						printother, NULL, "netstat"},
223 	{ "/net/inet/ipsec/ipsecstats",		printother, NULL, "netstat"},
224 	{ "/net/bpf/(stats|peers)",		printother, NULL, "netstat"},
225 
226 	{ "/net/inet.*/tcp.*/deb.*",		printother, NULL, "trpt" },
227 
228 	{ "/net/inet.*/ip.*/anonportalgo/reserve", reserve, reserve, NULL },
229 
230 	{ "/net/ns/spp/deb.*",			printother, NULL, "trsp" },
231 
232 	{ "/hw/diskstats",			printother, NULL, "iostat" },
233 
234 #ifdef CPU_CONSDEV
235 	{ "/machdep/consdev",			kern_consdev, NULL, NULL },
236 #endif /* CPU_CONSDEV */
237 #ifdef CPU_DISKINFO
238 	{ "/machdep/diskinfo",			machdep_diskinfo, NULL, NULL },
239 #endif /* CPU_CONSDEV */
240 
241 	{ "/proc/[^/]+/rlimit/[^/]+/[^/]+",	proc_limit, proc_limit, NULL },
242 
243 	/*
244 	 * these will only be called when the given node has no children
245 	 */
246 	{ "/net/[^/]+",				printother, NULL, NULL },
247 	{ "/debug",				printother, NULL, NULL },
248 	{ "/ddb",				printother, NULL, NULL },
249 	{ "/vendor",				printother, NULL, NULL },
250 
251 	{ NULL,					NULL, NULL, NULL },
252 };
253 
254 struct sysctlnode my_root = {
255 	.sysctl_flags = SYSCTL_VERSION|CTLFLAG_ROOT|CTLTYPE_NODE,
256 	.sysctl_size = sizeof(struct sysctlnode),
257 	.sysctl_num = 0,
258 	.sysctl_name = "(prog_root)",
259 };
260 
261 int	Aflag, aflag, dflag, Mflag, nflag, qflag, rflag, wflag, xflag;
262 size_t	nr;
263 char	*fn;
264 int	req, stale, errs;
265 FILE	*warnfp = stderr;
266 
267 #define MAXPORTS	0x10000
268 
269 /*
270  * vah-riables n stuff
271  */
272 char gsname[SYSCTL_NAMELEN * CTL_MAXNAME + CTL_MAXNAME],
273 	canonname[SYSCTL_NAMELEN * CTL_MAXNAME + CTL_MAXNAME],
274 	gdname[10 * CTL_MAXNAME + CTL_MAXNAME];
275 char sep[] = ".";
276 const char *eq = " = ";
277 const char *lname[] = {
278 	"top", "second", "third", "fourth", "fifth", "sixth",
279 	"seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth"
280 };
281 
282 /*
283  * you've heard of main, haven't you?
284  */
285 int
286 main(int argc, char *argv[])
287 {
288 	int name[CTL_MAXNAME];
289 	int ch;
290 	size_t lastcompiled = 0;
291 	regex_t *re;
292 
293 	setprogname(argv[0]);
294 	while ((ch = getopt(argc, argv, "Aabdef:Mnqrwx")) != -1) {
295 		switch (ch) {
296 		case 'A':
297 			Aflag++;
298 			break;
299 		case 'a':
300 			aflag++;
301 			break;
302 		case 'd':
303 			dflag++;
304 			break;
305 		case 'e':
306 			eq = "=";
307 			break;
308 		case 'f':
309 			fn = optarg;
310 			wflag++;
311 			break;
312 		case 'M':
313 			Mflag++;
314 			break;
315 		case 'n':
316 			nflag++;
317 			break;
318 		case 'q':
319 			qflag++;
320 			break;
321 		case 'b':	/* FreeBSD compat */
322 		case 'r':
323 			rflag++;
324 			break;
325 		case 'w':
326 			wflag++;
327 			break;
328 		case 'x':
329 			xflag++;
330 			break;
331 		default:
332 			usage();
333 		}
334 	}
335 
336 	argc -= optind;
337 	argv += optind;
338 
339 	if (xflag && rflag)
340 		usage();
341 	/* if ((xflag || rflag) && wflag)
342 		usage(); */
343 	/* if (aflag && (Mflag || qflag))
344 		usage(); */
345 	if ((aflag || Aflag) && qflag)
346 		usage();
347 	if ((Aflag || Mflag || dflag) && argc == 0 && fn == NULL)
348 		aflag = 1;
349 
350 	if (prog_init && prog_init() == -1)
351 		err(EXIT_FAILURE, "prog init failed");
352 
353 	if (Aflag)
354 		warnfp = stdout;
355 	stale = req = 0;
356 
357 	if ((re = malloc(sizeof(*re) * __arraycount(handlers))) == NULL)
358 		err(EXIT_FAILURE, "malloc regex");
359 
360 	if (aflag) {
361 		print_tree(&name[0], 0, NULL, CTLTYPE_NODE, 1,
362 		    re, &lastcompiled);
363 		/* if (argc == 0) */
364 		return (0);
365 	}
366 
367 	if (fn) {
368 		FILE *fp;
369 		char *l;
370 
371 		fp = fopen(fn, "r");
372 		if (fp == NULL) {
373 			err(EXIT_FAILURE, "%s", fn);
374 		} else {
375 			nr = 0;
376 			while ((l = fparseln(fp, NULL, &nr, NULL, 0)) != NULL)
377 			{
378 				if (*l) {
379 					parse(l, re, &lastcompiled);
380 					free(l);
381 				}
382 			}
383 			fclose(fp);
384 		}
385 		return errs ? 1 : 0;
386 	}
387 
388 	if (argc == 0)
389 		usage();
390 
391 	while (argc-- > 0)
392 		parse(*argv++, re, &lastcompiled);
393 
394 	return errs ? EXIT_FAILURE : EXIT_SUCCESS;
395 }
396 
397 /*
398  * ********************************************************************
399  * how to find someone special to handle the reading (or maybe even
400  * writing) of a particular node
401  * ********************************************************************
402  */
403 static const struct handlespec *
404 findhandler(const char *s, regex_t *re, size_t *lastcompiled)
405 {
406 	const struct handlespec *p;
407 	size_t i, l;
408 	int j;
409 	char eb[64];
410 	regmatch_t match;
411 
412 	p = &handlers[0];
413 	l = strlen(s);
414 	for (i = 0; p[i].ps_re != NULL; i++) {
415 		if (i >= *lastcompiled) {
416 			j = regcomp(&re[i], p[i].ps_re, REG_EXTENDED);
417 			if (j != 0) {
418 				regerror(j, &re[i], eb, sizeof(eb));
419 				errx(EXIT_FAILURE, "regcomp: %s: %s", p[i].ps_re, eb);
420 			}
421 			*lastcompiled = i + 1;
422 		}
423 		j = regexec(&re[i], s, 1, &match, 0);
424 		if (j == 0) {
425 			if (match.rm_so == 0 && match.rm_eo == (int)l)
426 				return &p[i];
427 		}
428 		else if (j != REG_NOMATCH) {
429 			regerror(j, &re[i], eb, sizeof(eb));
430 			errx(EXIT_FAILURE, "regexec: %s: %s", p[i].ps_re, eb);
431 		}
432 	}
433 
434 	return NULL;
435 }
436 
437 /*
438  * after sysctlgetmibinfo is done with the name, we convert all
439  * separators to / and stuff one at the front if it was missing
440  */
441 static void
442 canonicalize(const char *i, char *o)
443 {
444 	const char *t;
445 	char p[SYSCTL_NAMELEN + 1];
446 	int l;
447 
448 	if (i[0] != *sep) {
449 		o[0] = '/';
450 		o[1] = '\0';
451 	}
452 	else
453 		o[0] = '\0';
454 
455 	t = i;
456 	do {
457 		i = t;
458 		t = strchr(i, sep[0]);
459 		if (t == NULL)
460 			strcat(o, i);
461 		else {
462 			l = t - i;
463 			t++;
464 			memcpy(p, i, l);
465 			p[l] = '\0';
466 			strcat(o, p);
467 			strcat(o, "/");
468 		}
469 	} while (t != NULL);
470 }
471 
472 /*
473  * ********************************************************************
474  * convert this special number to a special string so we can print the
475  * mib
476  * ********************************************************************
477  */
478 static const char *
479 sf(u_int f)
480 {
481 	static char s[256];
482 	const char *c;
483 
484 	s[0] = '\0';
485 	c = "";
486 
487 #define print_flag(_f, _s, _c, _q, _x) \
488 	if (((_f) & (__CONCAT(CTLFLAG_,_x))) == (__CONCAT(CTLFLAG_,_q))) { \
489 		strlcat((_s), (_c), sizeof(_s)); \
490 		strlcat((_s), __STRING(_q), sizeof(_s)); \
491 		(_c) = ","; \
492 		(_f) &= ~(__CONCAT(CTLFLAG_,_x)); \
493 	}
494 	print_flag(f, s, c, READONLY,  READWRITE);
495 	print_flag(f, s, c, READWRITE, READWRITE);
496 	print_flag(f, s, c, ANYWRITE,  ANYWRITE);
497 	print_flag(f, s, c, PRIVATE,   PRIVATE);
498 	print_flag(f, s, c, PERMANENT, PERMANENT);
499 	print_flag(f, s, c, OWNDATA,   OWNDATA);
500 	print_flag(f, s, c, IMMEDIATE, IMMEDIATE);
501 	print_flag(f, s, c, HEX,       HEX);
502 	print_flag(f, s, c, ROOT,      ROOT);
503 	print_flag(f, s, c, ANYNUMBER, ANYNUMBER);
504 	print_flag(f, s, c, HIDDEN,    HIDDEN);
505 	print_flag(f, s, c, ALIAS,     ALIAS);
506 #undef print_flag
507 
508 	if (f) {
509 		char foo[9];
510 		snprintf(foo, sizeof(foo), "%x", f);
511 		strlcat(s, c, sizeof(s));
512 		strlcat(s, foo, sizeof(s));
513 	}
514 
515 	return (s);
516 }
517 
518 static const char *
519 st(u_int t)
520 {
521 
522 	switch (t) {
523 	case CTLTYPE_NODE:
524 		return "NODE";
525 	case CTLTYPE_INT:
526 		return "INT";
527 	case CTLTYPE_STRING:
528 		return "STRING";
529 	case CTLTYPE_QUAD:
530                 return "QUAD";
531 	case CTLTYPE_STRUCT:
532 		return "STRUCT";
533 	case CTLTYPE_BOOL:
534 		return "BOOL";
535 	}
536 
537 	return "???";
538 }
539 
540 /*
541  * ********************************************************************
542  * recursively eliminate all data belonging to the given node
543  * ********************************************************************
544  */
545 static void
546 purge_tree(struct sysctlnode *rnode)
547 {
548 	struct sysctlnode *node;
549 
550 	if (rnode == NULL ||
551 	    SYSCTL_TYPE(rnode->sysctl_flags) != CTLTYPE_NODE ||
552 	    rnode->sysctl_child == NULL)
553 		return;
554 
555 	for (node = rnode->sysctl_child;
556 	     node < &rnode->sysctl_child[rnode->sysctl_clen];
557 	     node++)
558 		purge_tree(node);
559 	free(rnode->sysctl_child);
560 	rnode->sysctl_csize = 0;
561 	rnode->sysctl_clen = 0;
562 	rnode->sysctl_child = NULL;
563 
564 	if (rnode->sysctl_desc == (const char*)-1)
565 		rnode->sysctl_desc = NULL;
566 	if (rnode->sysctl_desc != NULL)
567 		free(__UNCONST(rnode->sysctl_desc));
568 	rnode->sysctl_desc = NULL;
569 }
570 
571 static void __attribute__((__format__(__printf__, 3, 4)))
572 appendprintf(char **bp, size_t *lbp, const char *fmt, ...)
573 {
574 	int r;
575 	va_list ap;
576 
577 	va_start(ap, fmt);
578 	r = vsnprintf(*bp, *lbp, fmt, ap);
579 	va_end(ap);
580 	if (r < 0 || (size_t)r > *lbp)
581 		r = *lbp;
582 	*bp += r;
583 	*lbp -= r;
584 }
585 
586 /*
587  * ********************************************************************
588  * print this node and any others underneath it
589  * ********************************************************************
590  */
591 static void
592 print_tree(int *name, u_int namelen, struct sysctlnode *pnode, u_int type,
593    int add, regex_t *re, size_t *lastcompiled)
594 {
595 	struct sysctlnode *node;
596 	int rc;
597 	size_t ni, sz, ldp, lsp;
598 	char *sp, *dp, *tsp, *tdp;
599 	const struct handlespec *p;
600 
601 	sp = tsp = &gsname[strlen(gsname)];
602 	dp = tdp = &gdname[strlen(gdname)];
603 	ldp = sizeof(gdname) - (dp - gdname);
604 	lsp = sizeof(gsname) - (sp - gsname);
605 
606 	if (sp != &gsname[0] && dp == &gdname[0]) {
607 		/*
608 		 * aw...shucks.  now we must play catch up
609 		 */
610 		for (ni = 0; ni < namelen; ni++)
611 			appendprintf(&tdp, &ldp, "%s%d", ni > 0 ? "." : "",
612 			    name[ni]);
613 	}
614 
615 	if (pnode == NULL)
616 		pnode = &my_root;
617 	else if (add) {
618 		appendprintf(&tsp, &lsp, "%s%s", namelen > 1 ? sep : "",
619 			pnode->sysctl_name);
620 		appendprintf(&tdp, &ldp, "%s%d", namelen > 1 ? "." : "",
621 			pnode->sysctl_num);
622 	}
623 
624 	if (Mflag && pnode != &my_root) {
625 		if (nflag)
626 			printf("%s: ", gdname);
627 		else
628 			printf("%s (%s): ", gsname, gdname);
629 		printf("CTLTYPE_%s", st(type));
630 		if (type == CTLTYPE_NODE) {
631 			if (SYSCTL_FLAGS(pnode->sysctl_flags) & CTLFLAG_ALIAS)
632 				printf(", alias %d",
633 				       pnode->sysctl_alias);
634 			else
635 				printf(", children %d/%d",
636 				       pnode->sysctl_clen,
637 				       pnode->sysctl_csize);
638 		}
639 		printf(", size %zu", pnode->sysctl_size);
640 		printf(", flags 0x%x<%s>",
641 		       SYSCTL_FLAGS(pnode->sysctl_flags),
642 		       sf(SYSCTL_FLAGS(pnode->sysctl_flags)));
643 		if (pnode->sysctl_func)
644 			printf(", func=%p", pnode->sysctl_func);
645 		printf(", ver=%d", pnode->sysctl_ver);
646 		printf("\n");
647 		if (type != CTLTYPE_NODE) {
648 			*sp = *dp = '\0';
649 			return;
650 		}
651 	}
652 
653 	if (dflag && pnode != &my_root) {
654 		if (Aflag || type != CTLTYPE_NODE) {
655 			if (pnode->sysctl_desc == NULL)
656 				getdesc1(name, namelen, pnode);
657 			if (Aflag || !add ||
658 			    (pnode->sysctl_desc != NULL &&
659 			     pnode->sysctl_desc != (const char*)-1)) {
660 				if (!nflag)
661 					printf("%s: ", gsname);
662 				if (pnode->sysctl_desc == NULL ||
663 				    pnode->sysctl_desc == (const char*)-1)
664 					printf("(no description)\n");
665 				else
666 					printf("%s\n", pnode->sysctl_desc);
667 			}
668 		}
669 
670 		if (type != CTLTYPE_NODE) {
671 			*sp = *dp = '\0';
672 			return;
673 		}
674 	}
675 
676 	/*
677 	 * if this is an alias and we added our name, that means we
678 	 * got here by recursing down into the tree, so skip it.  The
679 	 * only way to print an aliased node is with either -M or by
680 	 * name specifically.
681 	 */
682 	if (SYSCTL_FLAGS(pnode->sysctl_flags) & CTLFLAG_ALIAS && add) {
683 		*sp = *dp = '\0';
684 		return;
685 	}
686 
687 	canonicalize(gsname, canonname);
688 	p = findhandler(canonname, re, lastcompiled);
689 	if (type != CTLTYPE_NODE && p != NULL) {
690 		if (p->ps_p == NULL) {
691 			sysctlperror("Cannot print `%s': %s\n", gsname,
692 			    strerror(EOPNOTSUPP));
693 			exit(EXIT_FAILURE);
694 		}
695 		(*p->ps_p)(gsname, gdname, NULL, name, namelen, pnode, type,
696 			   __UNCONST(p->ps_d));
697 		*sp = *dp = '\0';
698 		return;
699 	}
700 
701 	if (type != CTLTYPE_NODE && pnode->sysctl_size == 0) {
702 		rc = prog_sysctl(&name[0], namelen, NULL, &sz, NULL, 0);
703 		if (rc == -1) {
704 			sysctlerror(1);
705 			*sp = *dp = '\0';
706 			return;
707 		}
708 		if (sz == 0) {
709 			if ((Aflag || req) && !Mflag)
710 				printf("%s: node contains no data\n", gsname);
711 			*sp = *dp = '\0';
712 			return;
713 		}
714 	}
715 	else
716 		sz = pnode->sysctl_size;
717 
718 	switch (type) {
719 	case CTLTYPE_NODE: {
720 		__learn_tree(name, namelen, pnode);
721 		node = pnode->sysctl_child;
722 		if (node == NULL) {
723 			if (dflag)
724 				/* do nothing */;
725 			else if (p != NULL)
726 				(*p->ps_p)(gsname, gdname, NULL, name, namelen,
727 					   pnode, type, __UNCONST(p->ps_d));
728 			else if ((Aflag || req) && !Mflag)
729 				printf("%s: no children\n", gsname);
730 		}
731 		else {
732 			if (dflag)
733 				/*
734 				 * get all descriptions for next level
735 				 * in one chunk
736 				 */
737 				getdesc(name, namelen, pnode);
738 			req = 0;
739 			for (ni = 0; ni < pnode->sysctl_clen; ni++) {
740 				name[namelen] = node[ni].sysctl_num;
741 				if ((node[ni].sysctl_flags & CTLFLAG_HIDDEN) &&
742 				    !(Aflag || req))
743 					continue;
744 				print_tree(name, namelen + 1, &node[ni],
745 					   SYSCTL_TYPE(node[ni].sysctl_flags),
746 					   1, re, lastcompiled);
747 			}
748 		}
749 		break;
750 	}
751 	case CTLTYPE_INT: {
752 		int i;
753 		rc = prog_sysctl(name, namelen, &i, &sz, NULL, 0);
754 		if (rc == -1) {
755 			sysctlerror(1);
756 			break;
757 		}
758 		display_number(pnode, gsname, &i, sizeof(i), DISPLAY_VALUE);
759 		break;
760 	}
761 	case CTLTYPE_BOOL: {
762 		bool b;
763 		rc = prog_sysctl(name, namelen, &b, &sz, NULL, 0);
764 		if (rc == -1) {
765 			sysctlerror(1);
766 			break;
767 		}
768 		display_number(pnode, gsname, &b, sizeof(b), DISPLAY_VALUE);
769 		break;
770 	}
771 	case CTLTYPE_STRING: {
772 		unsigned char buf[1024], *tbuf;
773 		tbuf = buf;
774 		sz = sizeof(buf);
775 		rc = prog_sysctl(&name[0], namelen, tbuf, &sz, NULL, 0);
776 		if (rc == -1 && errno == ENOMEM) {
777 			tbuf = malloc(sz);
778 			if (tbuf == NULL) {
779 				sysctlerror(1);
780 				break;
781 			}
782 			rc = prog_sysctl(&name[0], namelen, tbuf, &sz, NULL, 0);
783 		}
784 		if (rc == -1)
785 			sysctlerror(1);
786 		else
787 			display_string(pnode, gsname, tbuf, sz, DISPLAY_VALUE);
788 		if (tbuf != buf)
789 			free(tbuf);
790 		break;
791 	}
792 	case CTLTYPE_QUAD: {
793 		u_quad_t q;
794 		sz = sizeof(q);
795 		rc = prog_sysctl(&name[0], namelen, &q, &sz, NULL, 0);
796 		if (rc == -1) {
797 			sysctlerror(1);
798 			break;
799 		}
800 		display_number(pnode, gsname, &q, sizeof(q), DISPLAY_VALUE);
801 		break;
802 	}
803 	case CTLTYPE_STRUCT: {
804 		/*
805 		 * we shouldn't actually get here, but if we
806 		 * do, would it be nice to have *something* to
807 		 * do other than completely ignore the
808 		 * request.
809 		 */
810 		unsigned char *d;
811 		if ((d = malloc(sz)) == NULL) {
812 			fprintf(warnfp, "%s: !malloc failed!\n", gsname);
813 			break;
814 		}
815 		rc = prog_sysctl(&name[0], namelen, d, &sz, NULL, 0);
816 		if (rc == -1) {
817 			sysctlerror(1);
818 			break;
819 		}
820 		display_struct(pnode, gsname, d, sz, DISPLAY_VALUE);
821 		free(d);
822 		break;
823 	}
824 	default:
825 		/* should i print an error here? */
826 		break;
827 	}
828 
829 	*sp = *dp = '\0';
830 }
831 
832 /*
833  * ********************************************************************
834  * parse a request, possibly determining that it's a create or destroy
835  * request
836  * ********************************************************************
837  */
838 static void
839 parse(char *l, regex_t *re, size_t *lastcompiled)
840 {
841 	struct sysctlnode *node;
842 	const struct handlespec *w;
843 	int name[CTL_MAXNAME], dodesc = 0;
844 	u_int namelen, type;
845 	char *key, *value, *dot;
846 	size_t sz;
847 	bool optional = false;
848 
849 	req = 1;
850 	key = l;
851 
852 	if ((value = strchr(l, '=')) != NULL) {
853 		if (value > l && value[-1] == '?') {
854 			value[-1] = '\0';
855 			optional = true;
856 		}
857 		*value++ = '\0';
858 	}
859 
860 	if ((dot = strpbrk(key, "./")) == NULL)
861 		sep[0] = '.';
862 	else
863 		sep[0] = dot[0];
864 	sep[1] = '\0';
865 
866 	while (key[0] == sep[0] && key[1] == sep[0]) {
867 		if (value != NULL)
868 			value[-1] = '=';
869 		if (strncmp(key + 2, "create", 6) == 0 &&
870 		    (key[8] == '=' || key[8] == sep[0]))
871 			parse_create(key + 8 + (key[8] == '=' ? 1 : 0));
872 		else if (strncmp(key + 2, "destroy", 7) == 0 &&
873 			 (key[9] == '=' || key[9] == sep[0]))
874 			parse_destroy(key + 9 + (key[9] == '=' ? 1 : 0));
875 		else if (strncmp(key + 2, "describe", 8) == 0 &&
876 			 (key[10] == '=' || key[10] == sep[0])) {
877 			key += 10 + (key[10] == '=');
878 			if ((value = strchr(key, '=')) != NULL)
879 				parse_describe(key);
880 			else {
881 				if (!dflag)
882 					dodesc = 1;
883 				break;
884 			}
885 		}
886 		else
887 			sysctlperror("unable to parse '%s'\n", key);
888 		return;
889 	}
890 
891 	if (stale) {
892 		purge_tree(&my_root);
893 		stale = 0;
894 	}
895 	node = &my_root;
896 	namelen = CTL_MAXNAME;
897 	sz = sizeof(gsname);
898 
899 	if (prog_sysctlgetmibinfo(key, &name[0], &namelen, gsname, &sz, &node,
900 			     SYSCTL_VERSION) == -1) {
901 		if (optional)
902 			return;
903 		sysctlparseerror(namelen, l);
904 		EXIT(EXIT_FAILURE);
905 	}
906 
907 	type = SYSCTL_TYPE(node->sysctl_flags);
908 
909 	if (value == NULL) {
910 		if (dodesc)
911 			dflag = 1;
912 		print_tree(&name[0], namelen, node, type, 0, re, lastcompiled);
913 		if (dodesc)
914 			dflag = 0;
915 		gsname[0] = '\0';
916 		return;
917 	}
918 
919 	if (fn)
920 		trim_whitespace(value, 1);
921 
922 	if (!wflag) {
923 		sysctlperror("Must specify -w to set variables\n");
924 		exit(EXIT_FAILURE);
925 	}
926 
927 	canonicalize(gsname, canonname);
928 	if (type != CTLTYPE_NODE && (w = findhandler(canonname, re,
929 	    lastcompiled)) != NULL) {
930 		if (w->ps_w == NULL) {
931 			sysctlperror("Cannot write `%s': %s\n", gsname,
932 			    strerror(EOPNOTSUPP));
933 			exit(EXIT_FAILURE);
934 		}
935 		(*w->ps_w)(gsname, gdname, value, name, namelen, node, type,
936 			   __UNCONST(w->ps_d));
937 		gsname[0] = '\0';
938 		return;
939 	}
940 
941 	switch (type) {
942 	case CTLTYPE_NODE:
943 		/*
944 		 * XXX old behavior is to print.  should we error instead?
945 		 */
946 		print_tree(&name[0], namelen, node, CTLTYPE_NODE, 1, re,
947 		    lastcompiled);
948 		break;
949 	case CTLTYPE_INT:
950 	case CTLTYPE_BOOL:
951 	case CTLTYPE_QUAD:
952 		write_number(&name[0], namelen, node, value, optional);
953 		break;
954 	case CTLTYPE_STRING:
955 		write_string(&name[0], namelen, node, value, optional);
956 		break;
957 	case CTLTYPE_STRUCT:
958 		/*
959 		 * XXX old behavior is to print.  should we error instead?
960 		 */
961 		/* fprintf(warnfp, "you can't write to %s\n", gsname); */
962 		print_tree(&name[0], namelen, node, type, 0, re, lastcompiled);
963 		break;
964 	}
965 }
966 
967 /*
968 
969   //create=foo.bar.whatever...,
970   [type=(int|quad|string|struct|node),]
971   [size=###,]
972   [n=###,]
973   [flags=(iohxparw12),]
974   [addr=0x####,|symbol=...|value=...]
975 
976   size is optional for some types.  type must be set before anything
977   else.  nodes can have [rwhp], but nothing else applies.  if no
978   size or type is given, node is asserted.  writeable is the default,
979   with [rw] being read-only and unconditionally writeable
980   respectively.  if you specify addr, it is assumed to be the name of
981   a kernel symbol, if value, CTLFLAG_OWNDATA will be asserted for
982   strings, CTLFLAG_IMMEDIATE for ints and u_quad_ts.  you cannot
983   specify both value and addr.
984 
985 */
986 
987 static void
988 parse_create(char *l)
989 {
990 	struct sysctlnode node;
991 	size_t sz;
992 	char *nname, *key, *value, *data, *addr, *c, *t;
993 	int name[CTL_MAXNAME], i, rc, method, flags, rw;
994 	u_int namelen, type;
995 	u_quad_t uq;
996 	quad_t q;
997 	bool b;
998 
999 	if (!wflag) {
1000 		sysctlperror("Must specify -w to create nodes\n");
1001 		exit(EXIT_FAILURE);
1002 	}
1003 
1004 	/*
1005 	 * these are the pieces that make up the description of a new
1006 	 * node
1007 	 */
1008 	memset(&node, 0, sizeof(node));
1009 	node.sysctl_num = CTL_CREATE;  /* any number is fine */
1010 	flags = 0;
1011 	rw = -1;
1012 	type = 0;
1013 	sz = 0;
1014 	data = addr = NULL;
1015 	memset(name, 0, sizeof(name));
1016 	namelen = 0;
1017 	method = 0;
1018 
1019 	/*
1020 	 * misc stuff used when constructing
1021 	 */
1022 	i = 0;
1023 	b = false;
1024 	uq = 0;
1025 	key = NULL;
1026 	value = NULL;
1027 
1028 	/*
1029 	 * the name of the thing we're trying to create is first, so
1030 	 * pick it off.
1031 	 */
1032 	nname = l;
1033 	if ((c = strchr(nname, ',')) != NULL)
1034 		*c++ = '\0';
1035 
1036 	while (c != NULL) {
1037 
1038 		/*
1039 		 * pull off the next "key=value" pair
1040 		 */
1041 		key = c;
1042 		if ((t = strchr(key, '=')) != NULL) {
1043 			*t++ = '\0';
1044 			value = t;
1045 		}
1046 		else
1047 			value = NULL;
1048 
1049 		/*
1050 		 * if the "key" is "value", then that eats the rest of
1051 		 * the string, so we're done, otherwise bite it off at
1052 		 * the next comma.
1053 		 */
1054 		if (strcmp(key, "value") == 0) {
1055 			c = NULL;
1056 			data = value;
1057 			break;
1058 		}
1059 		else if (value) {
1060 			if ((c = strchr(value, ',')) != NULL)
1061 				*c++ = '\0';
1062 		}
1063 
1064 		/*
1065 		 * note that we (mostly) let the invoker of prog_sysctl(8)
1066 		 * play rampant here and depend on the kernel to tell
1067 		 * them that they were wrong.  well...within reason.
1068 		 * we later check the various parameters against each
1069 		 * other to make sure it makes some sort of sense.
1070 		 */
1071 		if (strcmp(key, "addr") == 0) {
1072 			/*
1073 			 * we can't check these two.  only the kernel
1074 			 * can tell us when it fails to find the name
1075 			 * (or if the address is invalid).
1076 			 */
1077 			if (method != 0) {
1078 				sysctlperror(
1079 				    "%s: already have %s for new node\n",
1080 				    nname,
1081 				    method == CTL_CREATE ? "addr" : "symbol");
1082 				EXIT(EXIT_FAILURE);
1083 			}
1084 			if (value == NULL) {
1085 				sysctlperror("%s: missing value\n", nname);
1086 				EXIT(EXIT_FAILURE);
1087 			}
1088 			errno = 0;
1089 			addr = (void*)strtoul(value, &t, 0);
1090 			if (t == value || *t != '\0' || errno != 0) {
1091 				sysctlperror(
1092 				    "%s: '%s' is not a valid address\n",
1093 				    nname, value);
1094 				EXIT(EXIT_FAILURE);
1095 			}
1096 			method = CTL_CREATE;
1097 		}
1098 		else if (strcmp(key, "symbol") == 0) {
1099 			if (method != 0) {
1100 				sysctlperror(
1101 				    "%s: already have %s for new node\n",
1102 				    nname,
1103 				    method == CTL_CREATE ? "addr" : "symbol");
1104 				EXIT(EXIT_FAILURE);
1105 			}
1106 			addr = value;
1107 			method = CTL_CREATESYM;
1108 		}
1109 		else if (strcmp(key, "type") == 0) {
1110 			if (value == NULL) {
1111 				sysctlperror("%s: missing value\n", nname);
1112 				EXIT(EXIT_FAILURE);
1113 			}
1114 			if (strcmp(value, "node") == 0)
1115 				type = CTLTYPE_NODE;
1116 			else if (strcmp(value, "int") == 0) {
1117 				sz = sizeof(int);
1118 				type = CTLTYPE_INT;
1119 			}
1120 			else if (strcmp(value, "bool") == 0) {
1121 				sz = sizeof(bool);
1122 				type = CTLTYPE_BOOL;
1123 			}
1124 			else if (strcmp(value, "string") == 0)
1125 				type = CTLTYPE_STRING;
1126 			else if (strcmp(value, "quad") == 0) {
1127 				sz = sizeof(u_quad_t);
1128 				type = CTLTYPE_QUAD;
1129 			}
1130 			else if (strcmp(value, "struct") == 0)
1131 				type = CTLTYPE_STRUCT;
1132 			else {
1133 				sysctlperror(
1134 					"%s: '%s' is not a valid type\n",
1135 					nname, value);
1136 				EXIT(EXIT_FAILURE);
1137 			}
1138 		}
1139 		else if (strcmp(key, "size") == 0) {
1140 			if (value == NULL) {
1141 				sysctlperror("%s: missing value\n", nname);
1142 				EXIT(EXIT_FAILURE);
1143 			}
1144 			errno = 0;
1145 			/*
1146 			 * yes, i know size_t is not an unsigned long,
1147 			 * but we can all agree that it ought to be,
1148 			 * right?
1149 			 */
1150 			sz = strtoul(value, &t, 0);
1151 			if (t == value || *t != '\0' || errno != 0) {
1152 				sysctlperror(
1153 					"%s: '%s' is not a valid size\n",
1154 					nname, value);
1155 				EXIT(EXIT_FAILURE);
1156 			}
1157 		}
1158 		else if (strcmp(key, "n") == 0) {
1159 			if (value == NULL) {
1160 				sysctlperror("%s: missing value\n", nname);
1161 				EXIT(EXIT_FAILURE);
1162 			}
1163 			errno = 0;
1164 			q = strtoll(value, &t, 0);
1165 			if (t == value || *t != '\0' || errno != 0 ||
1166 			    q < INT_MIN || q > UINT_MAX) {
1167 				sysctlperror(
1168 				    "%s: '%s' is not a valid mib number\n",
1169 				    nname, value);
1170 				EXIT(EXIT_FAILURE);
1171 			}
1172 			node.sysctl_num = (int)q;
1173 		}
1174 		else if (strcmp(key, "flags") == 0) {
1175 			if (value == NULL) {
1176 				sysctlperror("%s: missing value\n", nname);
1177 				EXIT(EXIT_FAILURE);
1178 			}
1179 			t = value;
1180 			while (*t != '\0') {
1181 				switch (*t) {
1182 				case 'a':
1183 					flags |= CTLFLAG_ANYWRITE;
1184 					break;
1185 				case 'h':
1186 					flags |= CTLFLAG_HIDDEN;
1187 					break;
1188 				case 'i':
1189 					flags |= CTLFLAG_IMMEDIATE;
1190 					break;
1191 				case 'o':
1192 					flags |= CTLFLAG_OWNDATA;
1193 					break;
1194 				case 'p':
1195 					flags |= CTLFLAG_PRIVATE;
1196 					break;
1197 				case 'u':
1198 					flags |= CTLFLAG_UNSIGNED;
1199 					break;
1200 				case 'x':
1201 					flags |= CTLFLAG_HEX;
1202 					break;
1203 
1204 				case 'r':
1205 					rw = CTLFLAG_READONLY;
1206 					break;
1207 				case 'w':
1208 					rw = CTLFLAG_READWRITE;
1209 					break;
1210 				default:
1211 					sysctlperror(
1212 					   "%s: '%c' is not a valid flag\n",
1213 					    nname, *t);
1214 					EXIT(EXIT_FAILURE);
1215 				}
1216 				t++;
1217 			}
1218 		}
1219 		else {
1220 			sysctlperror("%s: unrecognized keyword '%s'\n",
1221 				     nname, key);
1222 			EXIT(EXIT_FAILURE);
1223 		}
1224 	}
1225 
1226 	/*
1227 	 * now that we've finished parsing the given string, fill in
1228 	 * anything they didn't specify
1229 	 */
1230 	if (type == 0)
1231 		type = CTLTYPE_NODE;
1232 
1233 	/*
1234 	 * the "data" can be interpreted various ways depending on the
1235 	 * type of node we're creating, as can the size
1236 	 */
1237 	if (data != NULL) {
1238 		if (addr != NULL) {
1239 			sysctlperror(
1240 				"%s: cannot specify both value and "
1241 				"address\n", nname);
1242 			EXIT(EXIT_FAILURE);
1243 		}
1244 
1245 		switch (type) {
1246 		case CTLTYPE_INT:
1247 			errno = 0;
1248 			q = strtoll(data, &t, 0);
1249 			if (t == data || *t != '\0' || errno != 0 ||
1250 				q < INT_MIN || q > UINT_MAX) {
1251 				sysctlperror(
1252 				    "%s: '%s' is not a valid integer\n",
1253 				    nname, value);
1254 				EXIT(EXIT_FAILURE);
1255 			}
1256 			i = (int)q;
1257 			if (!(flags & CTLFLAG_OWNDATA)) {
1258 				flags |= CTLFLAG_IMMEDIATE;
1259 				node.sysctl_idata = i;
1260 			}
1261 			else
1262 				node.sysctl_data = &i;
1263 			if (sz == 0)
1264 				sz = sizeof(int);
1265 			break;
1266 		case CTLTYPE_BOOL:
1267 			errno = 0;
1268 			q = strtoll(data, &t, 0);
1269 			if (t == data || *t != '\0' || errno != 0 ||
1270 				(q != 0 && q != 1)) {
1271 				sysctlperror(
1272 				    "%s: '%s' is not a valid bool\n",
1273 				    nname, value);
1274 				EXIT(EXIT_FAILURE);
1275 			}
1276 			b = q == 1;
1277 			if (!(flags & CTLFLAG_OWNDATA)) {
1278 				flags |= CTLFLAG_IMMEDIATE;
1279 				node.sysctl_idata = b;
1280 			}
1281 			else
1282 				node.sysctl_data = &b;
1283 			if (sz == 0)
1284 				sz = sizeof(bool);
1285 			break;
1286 		case CTLTYPE_STRING:
1287 			flags |= CTLFLAG_OWNDATA;
1288 			node.sysctl_data = data;
1289 			if (sz == 0)
1290 				sz = strlen(data) + 1;
1291 			else if (sz < strlen(data) + 1) {
1292 				sysctlperror("%s: ignoring size=%zu for "
1293 					"string node, too small for given "
1294 					"value\n", nname, sz);
1295 				sz = strlen(data) + 1;
1296 			}
1297 			break;
1298 		case CTLTYPE_QUAD:
1299 			errno = 0;
1300 			uq = strtouq(data, &t, 0);
1301 			if (t == data || *t != '\0' || errno != 0) {
1302 				sysctlperror(
1303 					"%s: '%s' is not a valid quad\n",
1304 					nname, value);
1305 				EXIT(EXIT_FAILURE);
1306 			}
1307 			if (!(flags & CTLFLAG_OWNDATA)) {
1308 				flags |= CTLFLAG_IMMEDIATE;
1309 				node.sysctl_qdata = uq;
1310 			}
1311 			else
1312 				node.sysctl_data = &uq;
1313 			if (sz == 0)
1314 				sz = sizeof(u_quad_t);
1315 			break;
1316 		case CTLTYPE_STRUCT:
1317 			sysctlperror("%s: struct not initializable\n",
1318 				     nname);
1319 			EXIT(EXIT_FAILURE);
1320 		}
1321 
1322 		/*
1323 		 * these methods have all provided local starting
1324 		 * values that the kernel must copy in
1325 		 */
1326 	}
1327 
1328 	/*
1329 	 * hmm...no data, but we have an address of data.  that's
1330 	 * fine.
1331 	 */
1332 	else if (addr != 0)
1333 		node.sysctl_data = (void*)addr;
1334 
1335 	/*
1336 	 * no data and no address?  well...okay.  we might be able to
1337 	 * manage that.
1338 	 */
1339 	else if (type != CTLTYPE_NODE) {
1340 		if (sz == 0) {
1341 			sysctlperror(
1342 			    "%s: need a size or a starting value\n",
1343 			    nname);
1344                         EXIT(EXIT_FAILURE);
1345                 }
1346 		if (!(flags & CTLFLAG_IMMEDIATE))
1347 			flags |= CTLFLAG_OWNDATA;
1348 	}
1349 
1350 	/*
1351 	 * now we do a few sanity checks on the description we've
1352 	 * assembled
1353 	 */
1354 	if ((flags & CTLFLAG_IMMEDIATE) &&
1355 	    (type == CTLTYPE_STRING || type == CTLTYPE_STRUCT)) {
1356 		sysctlperror("%s: cannot make an immediate %s\n",
1357 			     nname,
1358 			     (type == CTLTYPE_STRING) ? "string" : "struct");
1359 		EXIT(EXIT_FAILURE);
1360 	}
1361 	if (type == CTLTYPE_NODE && node.sysctl_data != NULL) {
1362 		sysctlperror("%s: nodes do not have data\n", nname);
1363 		EXIT(EXIT_FAILURE);
1364 	}
1365 
1366 	/*
1367 	 * some types must have a particular size
1368 	 */
1369 	if (sz != 0) {
1370 		if ((type == CTLTYPE_INT && sz != sizeof(int)) ||
1371 		    (type == CTLTYPE_BOOL && sz != sizeof(bool)) ||
1372 		    (type == CTLTYPE_QUAD && sz != sizeof(u_quad_t)) ||
1373 		    (type == CTLTYPE_NODE && sz != 0)) {
1374 			sysctlperror("%s: wrong size for type\n", nname);
1375 			EXIT(EXIT_FAILURE);
1376 		}
1377 	}
1378 	else if (type == CTLTYPE_STRUCT) {
1379 		sysctlperror("%s: struct must have size\n", nname);
1380 		EXIT(EXIT_FAILURE);
1381 	}
1382 
1383 	/*
1384 	 * now...if no one said anything yet, we default nodes or
1385 	 * any type that owns data being writeable, and everything
1386 	 * else being readonly.
1387 	 */
1388 	if (rw == -1) {
1389 		if (type == CTLTYPE_NODE ||
1390 		    (flags & (CTLFLAG_OWNDATA|CTLFLAG_IMMEDIATE)))
1391 			rw = CTLFLAG_READWRITE;
1392 		else
1393 			rw = CTLFLAG_READONLY;
1394 	}
1395 
1396 	/*
1397 	 * if a kernel address was specified, that can't be made
1398 	 * writeable by us.
1399 	if (rw != CTLFLAG_READONLY && addr) {
1400 		sysctlperror("%s: kernel data can only be readable\n", nname);
1401 		EXIT(EXIT_FAILURE);
1402 	}
1403 	 */
1404 
1405 	/*
1406 	 * what separator were they using in the full name of the new
1407 	 * node?
1408 	 */
1409 	if ((t = strpbrk(nname, "./")) == NULL)
1410 		sep[0] = '.';
1411 	else
1412 		sep[0] = t[0];
1413 	sep[1] = '\0';
1414 
1415 	/*
1416 	 * put it all together, now.  t'ain't much, is it?
1417 	 */
1418 	node.sysctl_flags = SYSCTL_VERSION|flags|rw|type;
1419 	node.sysctl_size = sz;
1420 	t = strrchr(nname, sep[0]);
1421 	if (t != NULL)
1422 		strlcpy(node.sysctl_name, t + 1, sizeof(node.sysctl_name));
1423 	else
1424 		strlcpy(node.sysctl_name, nname, sizeof(node.sysctl_name));
1425 	if (t == nname)
1426 		t = NULL;
1427 
1428 	/*
1429 	 * if this is a new top-level node, then we don't need to find
1430 	 * the mib for its parent
1431 	 */
1432 	if (t == NULL) {
1433 		namelen = 0;
1434 		gsname[0] = '\0';
1435 	}
1436 
1437 	/*
1438 	 * on the other hand, if it's not a top-level node...
1439 	 */
1440 	else {
1441 		namelen = sizeof(name) / sizeof(name[0]);
1442 		sz = sizeof(gsname);
1443 		*t = '\0';
1444 		rc = prog_sysctlgetmibinfo(nname, &name[0], &namelen,
1445 				      gsname, &sz, NULL, SYSCTL_VERSION);
1446 		*t = sep[0];
1447 		if (rc == -1) {
1448 			sysctlparseerror(namelen, nname);
1449 			EXIT(EXIT_FAILURE);
1450 		}
1451 	}
1452 
1453 	/*
1454 	 * yes, a new node is being created
1455 	 */
1456 	if (method != 0)
1457 		name[namelen++] = method;
1458 	else
1459 		name[namelen++] = CTL_CREATE;
1460 
1461 	sz = sizeof(node);
1462 	rc = prog_sysctl(&name[0], namelen, &node, &sz, &node, sizeof(node));
1463 
1464 	if (rc == -1) {
1465 		sysctlperror("%s: CTL_CREATE failed: %s\n",
1466 			     nname, strerror(errno));
1467 		EXIT(EXIT_FAILURE);
1468 	}
1469 	else {
1470 		if (!qflag && !nflag)
1471 			printf("%s(%s): (created)\n", nname, st(type));
1472 		stale = 1;
1473 	}
1474 }
1475 
1476 static void
1477 parse_destroy(char *l)
1478 {
1479 	struct sysctlnode node;
1480 	size_t sz;
1481 	int name[CTL_MAXNAME], rc;
1482 	u_int namelen;
1483 
1484 	if (!wflag) {
1485 		sysctlperror("Must specify -w to destroy nodes\n");
1486 		exit(EXIT_FAILURE);
1487 	}
1488 
1489 	memset(name, 0, sizeof(name));
1490 	namelen = sizeof(name) / sizeof(name[0]);
1491 	sz = sizeof(gsname);
1492 	rc = prog_sysctlgetmibinfo(l, &name[0], &namelen, gsname, &sz, NULL,
1493 			      SYSCTL_VERSION);
1494 	if (rc == -1) {
1495 		sysctlparseerror(namelen, l);
1496 		EXIT(EXIT_FAILURE);
1497 	}
1498 
1499 	memset(&node, 0, sizeof(node));
1500 	node.sysctl_flags = SYSCTL_VERSION;
1501 	node.sysctl_num = name[namelen - 1];
1502 	name[namelen - 1] = CTL_DESTROY;
1503 
1504 	sz = sizeof(node);
1505 	rc = prog_sysctl(&name[0], namelen, &node, &sz, &node, sizeof(node));
1506 
1507 	if (rc == -1) {
1508 		sysctlperror("%s: CTL_DESTROY failed: %s\n",
1509 			     l, strerror(errno));
1510 		EXIT(EXIT_FAILURE);
1511 	}
1512 	else {
1513 		if (!qflag && !nflag)
1514 			printf("%s(%s): (destroyed)\n", gsname,
1515 			       st(SYSCTL_TYPE(node.sysctl_flags)));
1516 		stale = 1;
1517 	}
1518 }
1519 
1520 static void
1521 parse_describe(char *l)
1522 {
1523 	struct sysctlnode newdesc;
1524 	char buf[1024], *value;
1525 	struct sysctldesc *d = (void*)&buf[0];
1526 	int name[CTL_MAXNAME], rc;
1527 	u_int namelen;
1528 	size_t sz;
1529 
1530 	if (!wflag) {
1531 		sysctlperror("Must specify -w to set descriptions\n");
1532 		exit(EXIT_FAILURE);
1533 	}
1534 
1535 	value = strchr(l, '=');
1536 	*value++ = '\0';
1537 
1538 	memset(name, 0, sizeof(name));
1539 	namelen = sizeof(name) / sizeof(name[0]);
1540 	sz = sizeof(gsname);
1541 	rc = prog_sysctlgetmibinfo(l, &name[0], &namelen, gsname, &sz, NULL,
1542 			      SYSCTL_VERSION);
1543 	if (rc == -1) {
1544 		sysctlparseerror(namelen, l);
1545 		EXIT(EXIT_FAILURE);
1546 	}
1547 
1548 	sz = sizeof(buf);
1549 	memset(&newdesc, 0, sizeof(newdesc));
1550 	newdesc.sysctl_flags = SYSCTL_VERSION|CTLFLAG_OWNDESC;
1551 	newdesc.sysctl_num = name[namelen - 1];
1552 	newdesc.sysctl_desc = value;
1553 	name[namelen - 1] = CTL_DESCRIBE;
1554 	rc = prog_sysctl(name, namelen, d, &sz, &newdesc, sizeof(newdesc));
1555 	if (rc == -1)
1556 		sysctlperror("%s: CTL_DESCRIBE failed: %s\n",
1557 			     gsname, strerror(errno));
1558 	else if (d->descr_len == 1)
1559 		sysctlperror("%s: description not set\n", gsname);
1560 	else if (!qflag && !nflag)
1561 		printf("%s: %s\n", gsname, d->descr_str);
1562 }
1563 
1564 /*
1565  * ********************************************************************
1566  * when things go wrong...
1567  * ********************************************************************
1568  */
1569 static void
1570 usage(void)
1571 {
1572 	const char *progname = getprogname();
1573 
1574 	(void)fprintf(stderr,
1575 		      "usage:\t%s %s\n"
1576 		      "\t%s %s\n"
1577 		      "\t%s %s\n"
1578 		      "\t%s %s\n"
1579 		      "\t%s %s\n"
1580 		      "\t%s %s\n",
1581 		      progname, "[-dneq] [-x[x]|-r] variable ...",
1582 		      progname, "[-ne] [-q] -w variable=value ...",
1583 		      progname, "[-dne] -a",
1584 		      progname, "[-dne] -A",
1585 		      progname, "[-ne] -M",
1586 		      progname, "[-dne] [-q] -f file");
1587 	exit(EXIT_FAILURE);
1588 }
1589 
1590 static void
1591 getdesc1(int *name, u_int namelen, struct sysctlnode *pnode)
1592 {
1593 	struct sysctlnode node;
1594 	char buf[1024], *desc;
1595 	struct sysctldesc *d = (void*)buf;
1596 	size_t sz = sizeof(buf);
1597 	int rc;
1598 
1599 	memset(&node, 0, sizeof(node));
1600 	node.sysctl_flags = SYSCTL_VERSION;
1601 	node.sysctl_num = name[namelen - 1];
1602 	name[namelen - 1] = CTL_DESCRIBE;
1603 	rc = prog_sysctl(name, namelen, d, &sz, &node, sizeof(node));
1604 
1605 	if (rc == -1 ||
1606 	    d->descr_len == 1 ||
1607 	    d->descr_num != pnode->sysctl_num ||
1608 	    d->descr_ver != pnode->sysctl_ver)
1609 		desc = (char *)-1;
1610 	else
1611 		desc = malloc(d->descr_len);
1612 
1613 	if (desc == NULL)
1614 		desc = (char *)-1;
1615 	if (desc != (char *)-1)
1616 		memcpy(desc, &d->descr_str[0], d->descr_len);
1617 	name[namelen - 1] = node.sysctl_num;
1618 	if (pnode->sysctl_desc != NULL &&
1619 	    pnode->sysctl_desc != (const char *)-1)
1620 		free(__UNCONST(pnode->sysctl_desc));
1621 	pnode->sysctl_desc = desc;
1622 }
1623 
1624 static void
1625 getdesc(int *name, u_int namelen, struct sysctlnode *pnode)
1626 {
1627 	struct sysctlnode *node = pnode->sysctl_child;
1628 	struct sysctldesc *d, *p, *plim;
1629 	char *desc;
1630 	size_t i, sz, child_cnt;
1631 	int rc;
1632 
1633 	sz = 128 * pnode->sysctl_clen;
1634 	name[namelen] = CTL_DESCRIBE;
1635 
1636 	/*
1637 	 * attempt *twice* to get the description chunk.  if two tries
1638 	 * doesn't work, give up.
1639 	 */
1640 	i = 0;
1641 	do {
1642 		d = malloc(sz);
1643 		if (d == NULL)
1644 			return;
1645 		rc = prog_sysctl(name, namelen + 1, d, &sz, NULL, 0);
1646 		if (rc == -1) {
1647 			free(d);
1648 			d = NULL;
1649 			if (i == 0 && errno == ENOMEM)
1650 				i = 1;
1651 			else
1652 				return;
1653 		}
1654 	} while (d == NULL);
1655 
1656 	/*
1657 	 * hokey nested loop here, giving O(n**2) behavior, but should
1658 	 * suffice for now
1659 	 */
1660 	plim = /*LINTED ptr cast*/(struct sysctldesc *)((char*)d + sz);
1661 	child_cnt = (pnode->sysctl_flags & CTLTYPE_NODE) ? pnode->sysctl_clen
1662 	    : 0;
1663 	for (i = 0; i < child_cnt; i++) {
1664 		node = &pnode->sysctl_child[i];
1665 		for (p = d; p < plim; p = NEXT_DESCR(p))
1666 			if (node->sysctl_num == p->descr_num)
1667 				break;
1668 		if (p < plim && node->sysctl_ver == p->descr_ver) {
1669 			/*
1670 			 * match found, attempt to attach description
1671 			 */
1672 			if (p->descr_len == 1)
1673 				desc = NULL;
1674 			else
1675 				desc = malloc(p->descr_len);
1676 			if (desc == NULL)
1677 				desc = (char *)-1;
1678 			else
1679 				memcpy(desc, &p->descr_str[0], p->descr_len);
1680 			node->sysctl_desc = desc;
1681 		}
1682 	}
1683 
1684 	free(d);
1685 }
1686 
1687 static void
1688 trim_whitespace(char *s, int dir)
1689 {
1690 	char *i, *o;
1691 
1692 	i = o = s;
1693 	if (dir & 1)
1694 		while (isspace((unsigned char)*i))
1695 			i++;
1696 	while ((*o++ = *i++) != '\0');
1697 	o -= 2; /* already past nul, skip back to before it */
1698 	if (dir & 2)
1699 		while (o > s && isspace((unsigned char)*o))
1700 			*o-- = '\0';
1701 }
1702 
1703 void
1704 sysctlerror(int soft)
1705 {
1706 	if (soft) {
1707 		switch (errno) {
1708 		case ENOENT:
1709 		case ENOPROTOOPT:
1710 		case ENOTDIR:
1711 		case EINVAL:
1712 		case EOPNOTSUPP:
1713 		case EPROTONOSUPPORT:
1714 			if (Aflag || req)
1715 				sysctlperror("%s: the value is not available "
1716 				    "(%s)\n", gsname, strerror(errno));
1717 			return;
1718 		}
1719 	}
1720 
1721 	if (Aflag || req)
1722 		sysctlperror("%s: %s\n", gsname, strerror(errno));
1723 	if (!soft)
1724 		EXIT(EXIT_FAILURE);
1725 }
1726 
1727 void
1728 sysctlparseerror(u_int namelen, const char *pname)
1729 {
1730 
1731 	if (qflag) {
1732 		errs++;
1733 		return;
1734 	}
1735 	sysctlperror("%s level name '%s' in '%s' is invalid\n",
1736 		     lname[namelen], gsname, pname);
1737 }
1738 
1739 static void
1740 sysctlperror(const char *fmt, ...)
1741 {
1742 	va_list ap;
1743 
1744 	(void)fprintf(warnfp, "%s: ", getprogname());
1745 	if (fn)
1746 		(void)fprintf(warnfp, "%s#%zu: ", fn, nr);
1747 	va_start(ap, fmt);
1748 	(void)vfprintf(warnfp, fmt, ap);
1749 	va_end(ap);
1750 	errs++;
1751 }
1752 
1753 
1754 /*
1755  * ********************************************************************
1756  * how to write to a "simple" node
1757  * ********************************************************************
1758  */
1759 static void
1760 write_number(int *name, u_int namelen, struct sysctlnode *node, char *value,
1761 	bool optional)
1762 {
1763 	u_int ii, io;
1764 	u_quad_t qi, qo;
1765 	size_t si, so;
1766 	bool bi, bo;
1767 	int rc;
1768 	void *i, *o;
1769 	char *t;
1770 
1771 	if (fn)
1772 		trim_whitespace(value, 3);
1773 
1774 	si = so = 0;
1775 	i = o = NULL;
1776 	bi = bo = false;
1777 	errno = 0;
1778 	qi = strtouq(value, &t, 0);
1779 	if (qi == UQUAD_MAX && errno == ERANGE) {
1780 		sysctlperror("%s: %s\n", value, strerror(errno));
1781 		EXIT(EXIT_FAILURE);
1782 	}
1783 	if (t == value || *t != '\0') {
1784 		sysctlperror("%s: not a number\n", value);
1785 		EXIT(EXIT_FAILURE);
1786 	}
1787 
1788 	switch (SYSCTL_TYPE(node->sysctl_flags)) {
1789 	case CTLTYPE_INT:
1790 		ii = (u_int)qi;
1791 		io = (u_int)(qi >> 32);
1792 		if (io != (u_int)-1 && io != 0) {
1793 			sysctlperror("%s: %s\n", value, strerror(ERANGE));
1794 			EXIT(EXIT_FAILURE);
1795 		}
1796 		o = &io;
1797 		so = sizeof(io);
1798 		i = &ii;
1799 		si = sizeof(ii);
1800 		break;
1801 	case CTLTYPE_BOOL:
1802 		bi = (bool)qi;
1803 		o = &bo;
1804 		so = sizeof(bo);
1805 		i = &bi;
1806 		si = sizeof(bi);
1807 		break;
1808 	case CTLTYPE_QUAD:
1809 		o = &qo;
1810 		so = sizeof(qo);
1811 		i = &qi;
1812 		si = sizeof(qi);
1813 		break;
1814 	}
1815 
1816 	rc = prog_sysctl(name, namelen, o, &so, i, si);
1817 	if (rc == -1) {
1818 		if (!optional || errno != EPERM) {
1819 			sysctlerror(0);
1820 		}
1821 		return;
1822 	}
1823 
1824 	switch (SYSCTL_TYPE(node->sysctl_flags)) {
1825 	case CTLTYPE_INT:
1826 		display_number(node, gsname, &io, sizeof(io), DISPLAY_OLD);
1827 		display_number(node, gsname, &ii, sizeof(ii), DISPLAY_NEW);
1828 		break;
1829 	case CTLTYPE_BOOL:
1830 		display_number(node, gsname, &bo, sizeof(bo), DISPLAY_OLD);
1831 		display_number(node, gsname, &bi, sizeof(bi), DISPLAY_NEW);
1832 		break;
1833 	case CTLTYPE_QUAD:
1834 		display_number(node, gsname, &qo, sizeof(qo), DISPLAY_OLD);
1835 		display_number(node, gsname, &qi, sizeof(qi), DISPLAY_NEW);
1836 		break;
1837 	}
1838 }
1839 
1840 static void
1841 write_string(int *name, u_int namelen, struct sysctlnode *node, char *value,
1842 	bool optional)
1843 {
1844 	char *i, *o;
1845 	size_t si, so;
1846 	int rc;
1847 
1848 	i = value;
1849 	si = strlen(i) + 1;
1850 	so = node->sysctl_size;
1851 	if (si > so && so != 0) {
1852 		sysctlperror("%s: string too long\n", value);
1853 		EXIT(EXIT_FAILURE);
1854 	}
1855 	o = malloc(so);
1856 	if (o == NULL) {
1857 		sysctlperror("%s: !malloc failed!\n", gsname);
1858 		exit(EXIT_FAILURE);
1859 	}
1860 
1861 	rc = prog_sysctl(name, namelen, o, &so, i, si);
1862 	if (rc == -1) {
1863 		if (!optional || errno != EPERM) {
1864 			sysctlerror(0);
1865 		}
1866 		free(o);
1867 		return;
1868 	}
1869 
1870 	display_string(node, gsname, o, so, DISPLAY_OLD);
1871 	display_string(node, gsname, i, si, DISPLAY_NEW);
1872 	free(o);
1873 }
1874 
1875 /*
1876  * ********************************************************************
1877  * simple ways to print stuff consistently
1878  * ********************************************************************
1879  */
1880 static void
1881 display_number(const struct sysctlnode *node, const char *name,
1882 	       const void *data, size_t sz, int n)
1883 {
1884 	u_quad_t q;
1885 	bool b;
1886 	int i;
1887 
1888 	if (qflag)
1889 		return;
1890 	if ((nflag || rflag) && (n == DISPLAY_OLD))
1891 		return;
1892 
1893 	if (rflag && n != DISPLAY_OLD) {
1894 		fwrite(data, sz, 1, stdout);
1895 		return;
1896 	}
1897 
1898 	if (!nflag) {
1899 		if (n == DISPLAY_VALUE)
1900 			printf("%s%s", name, eq);
1901 		else if (n == DISPLAY_OLD)
1902 			printf("%s: ", name);
1903 	}
1904 
1905 	if (xflag > 1) {
1906 		if (n != DISPLAY_NEW)
1907 			printf("\n");
1908 		hex_dump(data, sz);
1909 		return;
1910 	}
1911 
1912 	switch (SYSCTL_TYPE(node->sysctl_flags)) {
1913 	case CTLTYPE_INT:
1914 		memcpy(&i, data, sz);
1915 		if (xflag)
1916 			printf("0x%0*x", (int)sz * 2, i);
1917 		else if (node->sysctl_flags & CTLFLAG_HEX)
1918 			printf("%#x", i);
1919 		else if (node->sysctl_flags & CTLFLAG_UNSIGNED)
1920 			printf("%u", i);
1921 		else
1922 			printf("%d", i);
1923 		break;
1924 	case CTLTYPE_BOOL:
1925 		memcpy(&b, data, sz);
1926 		if (xflag)
1927 			printf("0x%0*x", (int)sz * 2, b);
1928 		else if (node->sysctl_flags & CTLFLAG_HEX)
1929 			printf("%#x", b);
1930 		else
1931 			printf("%d", b);
1932 		break;
1933 	case CTLTYPE_QUAD:
1934 		memcpy(&q, data, sz);
1935 		if (xflag)
1936 			printf("0x%0*" PRIx64, (int)sz * 2, q);
1937 		else if (node->sysctl_flags & CTLFLAG_HEX)
1938 			printf("%#" PRIx64, q);
1939 		else if (node->sysctl_flags & CTLFLAG_UNSIGNED)
1940 			printf("%" PRIu64, q);
1941 		else
1942 			printf("%" PRIu64, q);
1943 		break;
1944 	}
1945 
1946 	if (n == DISPLAY_OLD)
1947 		printf(" -> ");
1948 	else
1949 		printf("\n");
1950 }
1951 
1952 static void
1953 display_string(const struct sysctlnode *node, const char *name,
1954 	       const void *data, size_t sz, int n)
1955 {
1956 	const unsigned char *buf = data;
1957 	int ni;
1958 
1959 	if (qflag)
1960 		return;
1961 	if ((nflag || rflag) && (n == DISPLAY_OLD))
1962 		return;
1963 
1964 	if (rflag && n != DISPLAY_OLD) {
1965 		fwrite(data, sz, 1, stdout);
1966 		return;
1967 	}
1968 
1969 	if (!nflag) {
1970 		if (n == DISPLAY_VALUE)
1971 			printf("%s%s", name, eq);
1972 		else if (n == DISPLAY_OLD)
1973 			printf("%s: ", name);
1974 	}
1975 
1976 	if (xflag > 1) {
1977 		if (n != DISPLAY_NEW)
1978 			printf("\n");
1979 		hex_dump(data, sz);
1980 		return;
1981 	}
1982 
1983 	if (xflag || node->sysctl_flags & CTLFLAG_HEX) {
1984 		for (ni = 0; ni < (int)sz; ni++) {
1985 			if (xflag)
1986 				printf("%02x", buf[ni]);
1987 			if (buf[ni] == '\0')
1988 				break;
1989 			if (!xflag)
1990 				printf("\\x%2.2x", buf[ni]);
1991 		}
1992 	}
1993 	else
1994 		printf("%.*s", (int)sz, buf);
1995 
1996 	if (n == DISPLAY_OLD)
1997 		printf(" -> ");
1998 	else
1999 		printf("\n");
2000 }
2001 
2002 /*ARGSUSED*/
2003 static void
2004 display_struct(const struct sysctlnode *node, const char *name,
2005 	       const void *data, size_t sz, int n)
2006 {
2007 	const unsigned char *buf = data;
2008 	int ni;
2009 	size_t more;
2010 
2011 	if (qflag)
2012 		return;
2013 	if (!(xflag || rflag)) {
2014 		if (Aflag || req)
2015 			sysctlperror(
2016 			    "%s: this type is unknown to this program\n",
2017 			    gsname);
2018 		return;
2019 	}
2020 	if ((nflag || rflag) && (n == DISPLAY_OLD))
2021 		return;
2022 
2023 	if (rflag && n != DISPLAY_OLD) {
2024 		fwrite(data, sz, 1, stdout);
2025 		return;
2026 	}
2027 
2028         if (!nflag) {
2029                 if (n == DISPLAY_VALUE)
2030                         printf("%s%s", name, eq);
2031                 else if (n == DISPLAY_OLD)
2032                         printf("%s: ", name);
2033         }
2034 
2035 	if (xflag > 1) {
2036 		if (n != DISPLAY_NEW)
2037 			printf("\n");
2038 		hex_dump(data, sz);
2039 		return;
2040 	}
2041 
2042 	if (sz > 16) {
2043 		more = sz - 16;
2044 		sz = 16;
2045 	}
2046 	else
2047 		more = 0;
2048 	for (ni = 0; ni < (int)sz; ni++)
2049 		printf("%02x", buf[ni]);
2050 	if (more)
2051 		printf("...(%zu more bytes)", more);
2052 	printf("\n");
2053 }
2054 
2055 static void
2056 hex_dump(const unsigned char *buf, size_t len)
2057 {
2058 	unsigned int i;
2059 	int j;
2060 	char line[80], tmp[12];
2061 
2062 	memset(line, ' ', sizeof(line));
2063 	for (i = 0, j = 15; i < len; i++) {
2064 		j = i % 16;
2065 		/* reset line */
2066 		if (j == 0) {
2067 			line[58] = '|';
2068 			line[77] = '|';
2069 			line[78] = 0;
2070 			snprintf(tmp, sizeof(tmp), "%07x", i);
2071 			memcpy(&line[0], tmp, 7);
2072 		}
2073 		/* copy out hex version of byte */
2074 		snprintf(tmp, sizeof(tmp), "%02x", buf[i]);
2075 		memcpy(&line[9 + j * 3], tmp, 2);
2076 		/* copy out plain version of byte */
2077 		line[60 + j] = (isprint(buf[i])) ? buf[i] : '.';
2078 		/* print a full line and erase it */
2079 		if (j == 15) {
2080 			printf("%s\n", line);
2081 			memset(line, ' ', sizeof(line));
2082 		}
2083 	}
2084 	if (line[0] != ' ')
2085 		printf("%s\n", line);
2086 	printf("%07zu bytes\n", len);
2087 }
2088 
2089 /*
2090  * ********************************************************************
2091  * functions that handle particular nodes
2092  * ********************************************************************
2093  */
2094 /*ARGSUSED*/
2095 static void
2096 printother(HANDLER_ARGS)
2097 {
2098 	int rc;
2099 	void *p;
2100 	size_t sz1, sz2;
2101 
2102 	if (!(Aflag || req) || Mflag)
2103 		return;
2104 
2105 	/*
2106 	 * okay...you asked for it, so let's give it a go
2107 	 */
2108 	while (type != CTLTYPE_NODE && (xflag || rflag)) {
2109 		rc = prog_sysctl(name, namelen, NULL, &sz1, NULL, 0);
2110 		if (rc == -1 || sz1 == 0)
2111 			break;
2112 		p = malloc(sz1);
2113 		if (p == NULL)
2114 			break;
2115 		sz2 = sz1;
2116 		rc = prog_sysctl(name, namelen, p, &sz2, NULL, 0);
2117 		if (rc == -1 || sz1 != sz2) {
2118 			free(p);
2119 			break;
2120 		}
2121 		display_struct(pnode, gsname, p, sz1, DISPLAY_VALUE);
2122 		free(p);
2123 		return;
2124 	}
2125 
2126 	/*
2127 	 * that didn't work...do we have a specific message for this
2128 	 * thing?
2129 	 */
2130 	if (v != NULL) {
2131 		sysctlperror("%s: use '%s' to view this information\n",
2132 			     gsname, (const char *)v);
2133 		return;
2134 	}
2135 
2136 	/*
2137 	 * hmm...i wonder if we have any generic hints?
2138 	 */
2139 	switch (name[0]) {
2140 	case CTL_NET:
2141 		sysctlperror("%s: use 'netstat' to view this information\n",
2142 			     sname);
2143 		break;
2144 	case CTL_DEBUG:
2145 		sysctlperror("%s: missing 'options DEBUG' from kernel?\n",
2146 			     sname);
2147 		break;
2148 	case CTL_DDB:
2149 		sysctlperror("%s: missing 'options DDB' from kernel?\n",
2150 			     sname);
2151 		break;
2152 	case CTL_VENDOR:
2153 		sysctlperror("%s: no vendor extensions installed\n",
2154 			     sname);
2155 		break;
2156 	}
2157 }
2158 
2159 /*ARGSUSED*/
2160 static void
2161 kern_clockrate(HANDLER_ARGS)
2162 {
2163 	struct clockinfo clkinfo;
2164 	size_t sz;
2165 	int rc;
2166 
2167 	sz = sizeof(clkinfo);
2168 	rc = prog_sysctl(name, namelen, &clkinfo, &sz, NULL, 0);
2169 	if (rc == -1) {
2170 		sysctlerror(1);
2171 		return;
2172 	}
2173 	if (sz != sizeof(clkinfo))
2174 		errx(EXIT_FAILURE, "%s: !returned size wrong!", sname);
2175 
2176 	if (xflag || rflag) {
2177 		display_struct(pnode, sname, &clkinfo, sz,
2178 			       DISPLAY_VALUE);
2179 		return;
2180 	}
2181 	else if (!nflag)
2182 		printf("%s: ", sname);
2183 	printf("tick = %d, tickadj = %d, hz = %d, profhz = %d, stathz = %d\n",
2184 	       clkinfo.tick, clkinfo.tickadj,
2185 	       clkinfo.hz, clkinfo.profhz, clkinfo.stathz);
2186 }
2187 
2188 /*ARGSUSED*/
2189 static void
2190 kern_boottime(HANDLER_ARGS)
2191 {
2192 	struct timespec timespec;
2193 	time_t boottime;
2194 	size_t sz;
2195 	int rc;
2196 
2197 	sz = sizeof(timespec);
2198 	rc = prog_sysctl(name, namelen, &timespec, &sz, NULL, 0);
2199 	if (rc == -1) {
2200 		sysctlerror(1);
2201 		return;
2202 	}
2203 	if (sz != sizeof(timespec))
2204 		errx(EXIT_FAILURE, "%s: !returned size wrong!", sname);
2205 
2206 	boottime = timespec.tv_sec;
2207 	if (xflag || rflag)
2208 		display_struct(pnode, sname, &timespec, sz,
2209 			       DISPLAY_VALUE);
2210 	else if (!nflag)
2211 		/* ctime() provides the \n */
2212 		printf("%s%s%s", sname, eq, ctime(&boottime));
2213 	else if (nflag == 1)
2214 		printf("%lld\n", (long long)boottime);
2215 	else
2216 		printf("%lld.%9.9ld\n", (long long)timespec.tv_sec,
2217 		       timespec.tv_nsec);
2218 }
2219 
2220 /*ARGSUSED*/
2221 static void
2222 kern_consdev(HANDLER_ARGS)
2223 {
2224 	dev_t cons;
2225 	size_t sz;
2226 	int rc;
2227 
2228 	sz = sizeof(cons);
2229 	rc = prog_sysctl(name, namelen, &cons, &sz, NULL, 0);
2230 	if (rc == -1) {
2231 		sysctlerror(1);
2232 		return;
2233 	}
2234 	if (sz != sizeof(cons))
2235 		errx(EXIT_FAILURE, "%s: !returned size wrong!", sname);
2236 
2237 	if (xflag || rflag)
2238 		display_struct(pnode, sname, &cons, sz,
2239 			       DISPLAY_VALUE);
2240 	else {
2241 		if (!nflag)
2242 			printf("%s%s", sname, eq);
2243 		if (nflag < 2 && (sname = devname(cons, S_IFCHR)) != NULL)
2244 			printf("%s\n", sname);
2245 		else
2246 			printf("0x%llx\n", (unsigned long long)cons);
2247 	}
2248 }
2249 
2250 /*ARGSUSED*/
2251 static void
2252 kern_cp_time(HANDLER_ARGS)
2253 {
2254 	u_int64_t *cp_time;
2255 	size_t sz, osz;
2256 	int rc, i, n;
2257 	char s[sizeof("kern.cp_time.nnnnnn")];
2258 	const char *tname;
2259 
2260 	/*
2261 	 * three things to do here.
2262 	 * case 1: get sum (no Aflag and namelen == 2)
2263 	 * case 2: get specific processor (namelen == 3)
2264 	 * case 3: get all processors (Aflag and namelen == 2)
2265 	 */
2266 
2267 	if (namelen == 2 && Aflag) {
2268 		sz = sizeof(n);
2269 		rc = prog_sysctlbyname("hw.ncpu", &n, &sz, NULL, 0);
2270 		if (rc != 0)
2271 			return; /* XXX print an error, eh? */
2272 		n++; /* Add on space for the sum. */
2273 		sz = n * sizeof(u_int64_t) * CPUSTATES;
2274 	}
2275 	else {
2276 		n = -1; /* Just print one data set. */
2277 		sz = sizeof(u_int64_t) * CPUSTATES;
2278 	}
2279 
2280 	cp_time = malloc(sz);
2281 	if (cp_time == NULL) {
2282 		sysctlerror(1);
2283 		return;
2284 	}
2285 
2286 	osz = sz;
2287 	rc = prog_sysctl(name, namelen, cp_time + (n != -1) * CPUSTATES, &osz,
2288 		    NULL, 0);
2289 
2290 	if (rc == -1) {
2291 		sysctlerror(1);
2292 		free(cp_time);
2293 		return;
2294 	}
2295 
2296 	/*
2297 	 * Check, but account for space we'll occupy with the sum.
2298 	 */
2299 	if (osz != sz - (n != -1) * CPUSTATES * sizeof(u_int64_t))
2300 		errx(EXIT_FAILURE, "%s: !returned size wrong!", sname);
2301 
2302 	/*
2303 	 * Compute the actual sum.  Two calls would be easier (we
2304 	 * could just call ourselves recursively above), but the
2305 	 * numbers wouldn't add up.
2306 	 */
2307 	if (n != -1) {
2308 		memset(cp_time, 0, sizeof(u_int64_t) * CPUSTATES);
2309 		for (i = 1; i < n; i++) {
2310 			cp_time[CP_USER] += cp_time[i * CPUSTATES + CP_USER];
2311                         cp_time[CP_NICE] += cp_time[i * CPUSTATES + CP_NICE];
2312                         cp_time[CP_SYS] += cp_time[i * CPUSTATES + CP_SYS];
2313                         cp_time[CP_INTR] += cp_time[i * CPUSTATES + CP_INTR];
2314                         cp_time[CP_IDLE] += cp_time[i * CPUSTATES + CP_IDLE];
2315 		}
2316 	}
2317 
2318 	tname = sname;
2319 	for (i = 0; n == -1 || i < n; i++) {
2320 		if (i > 0) {
2321 			(void)snprintf(s, sizeof(s), "%s%s%d", sname, sep,
2322 				       i - 1);
2323 			tname = s;
2324 		}
2325 		if (xflag || rflag)
2326 			display_struct(pnode, tname, cp_time + (i * CPUSTATES),
2327 				       sizeof(u_int64_t) * CPUSTATES,
2328 				       DISPLAY_VALUE);
2329 		else {
2330 			if (!nflag)
2331 				printf("%s: ", tname);
2332 			printf("user = %" PRIu64
2333 			       ", nice = %" PRIu64
2334 			       ", sys = %" PRIu64
2335 			       ", intr = %" PRIu64
2336 			       ", idle = %" PRIu64
2337 			       "\n",
2338 			       cp_time[i * CPUSTATES + CP_USER],
2339 			       cp_time[i * CPUSTATES + CP_NICE],
2340 			       cp_time[i * CPUSTATES + CP_SYS],
2341 			       cp_time[i * CPUSTATES + CP_INTR],
2342 			       cp_time[i * CPUSTATES + CP_IDLE]);
2343 		}
2344 		/*
2345 		 * Just printing the one node.
2346 		 */
2347 		if (n == -1)
2348 			break;
2349 	}
2350 
2351 	free(cp_time);
2352 }
2353 
2354 /*ARGSUSED*/
2355 static void
2356 kern_drivers(HANDLER_ARGS)
2357 {
2358 	struct kinfo_drivers *kd;
2359 	size_t sz, i;
2360 	int rc;
2361 	const char *comma;
2362 
2363 	rc = prog_sysctl(name, namelen, NULL, &sz, NULL, 0);
2364 	if (rc == -1) {
2365 		sysctlerror(1);
2366 		return;
2367 	}
2368 
2369 	if (sz % sizeof(*kd))
2370 		err(EXIT_FAILURE, "bad size %zu for kern.drivers", sz);
2371 
2372 	kd = malloc(sz);
2373 	if (kd == NULL) {
2374 		sysctlerror(1);
2375 		return;
2376 	}
2377 
2378 	rc = prog_sysctl(name, namelen, kd, &sz, NULL, 0);
2379 	if (rc == -1) {
2380 		sysctlerror(1);
2381 		free(kd);
2382 		return;
2383 	}
2384 
2385 	comma = "";
2386 	if (!nflag)
2387 		printf("%s%s", sname, eq);
2388 	for (i = 0, sz /= sizeof(*kd); i < sz; i++) {
2389 		(void)printf("%s[%d %d %s]", comma, kd[i].d_cmajor,
2390 		    kd[i].d_bmajor, kd[i].d_name);
2391 		comma = ", ";
2392 	}
2393 	(void)printf("\n");
2394 	free(kd);
2395 }
2396 
2397 /*ARGSUSED*/
2398 static void
2399 kern_cp_id(HANDLER_ARGS)
2400 {
2401 	u_int64_t *cp_id;
2402 	size_t sz, osz;
2403 	int rc, i, n;
2404 	char s[sizeof("kern.cp_id.nnnnnn")];
2405 	const char *tname;
2406 	struct sysctlnode node = *pnode;
2407 
2408 	/*
2409 	 * three things to do here.
2410 	 * case 1: print a specific cpu id (namelen == 3)
2411 	 * case 2: print all cpu ids separately (Aflag set)
2412 	 * case 3: print all cpu ids on one line
2413 	 */
2414 
2415 	if (namelen == 2) {
2416 		sz = sizeof(n);
2417 		rc = prog_sysctlbyname("hw.ncpu", &n, &sz, NULL, 0);
2418 		if (rc != 0)
2419 			return; /* XXX print an error, eh? */
2420 		sz = n * sizeof(u_int64_t);
2421 	}
2422 	else {
2423 		n = -1; /* Just print one cpu id. */
2424 		sz = sizeof(u_int64_t);
2425 	}
2426 
2427 	cp_id = malloc(sz);
2428 	if (cp_id == NULL) {
2429 		sysctlerror(1);
2430 		return;
2431 	}
2432 
2433 	osz = sz;
2434 	rc = prog_sysctl(name, namelen, cp_id, &osz, NULL, 0);
2435 	if (rc == -1) {
2436 		sysctlerror(1);
2437 		free(cp_id);
2438 		return;
2439 	}
2440 
2441 	/*
2442 	 * Check that we got back what we asked for.
2443 	 */
2444 	if (osz != sz)
2445 		errx(EXIT_FAILURE, "%s: !returned size wrong!", sname);
2446 
2447 	/* pretend for output purposes */
2448 	node.sysctl_flags = SYSCTL_FLAGS(pnode->sysctl_flags) |
2449 		SYSCTL_TYPE(CTLTYPE_QUAD);
2450 
2451 	tname = sname;
2452 	if (namelen == 3)
2453 		display_number(&node, tname, cp_id,
2454 			       sizeof(u_int64_t),
2455 			       DISPLAY_VALUE);
2456 	else if (Aflag) {
2457 		for (i = 0; i < n; i++) {
2458 			(void)snprintf(s, sizeof(s), "%s%s%d", sname, sep, i);
2459 			tname = s;
2460 			display_number(&node, tname, &cp_id[i],
2461 				       sizeof(u_int64_t),
2462 				       DISPLAY_VALUE);
2463 		}
2464 	}
2465 	else {
2466 		if (xflag || rflag)
2467 			display_struct(pnode, tname, cp_id, sz, DISPLAY_VALUE);
2468 		else {
2469 			if (!nflag)
2470 				printf("%s: ", tname);
2471 			for (i = 0; i < n; i++) {
2472 				if (i)
2473 					printf(", ");
2474 				printf("%d = %" PRIu64, i, cp_id[i]);
2475 			}
2476 			printf("\n");
2477 		}
2478 	}
2479 
2480 	free(cp_id);
2481 }
2482 
2483 /*ARGSUSED*/
2484 static void
2485 vm_loadavg(HANDLER_ARGS)
2486 {
2487 	struct loadavg loadavg;
2488 	size_t sz;
2489 	int rc;
2490 
2491 	sz = sizeof(loadavg);
2492 	rc = prog_sysctl(name, namelen, &loadavg, &sz, NULL, 0);
2493 	if (rc == -1) {
2494 		sysctlerror(1);
2495 		return;
2496 	}
2497 	if (sz != sizeof(loadavg))
2498 		errx(EXIT_FAILURE, "%s: !returned size wrong!", sname);
2499 
2500 	if (xflag || rflag) {
2501 		display_struct(pnode, sname, &loadavg, sz,
2502 			       DISPLAY_VALUE);
2503 		return;
2504 	}
2505 	if (!nflag)
2506 		printf("%s: ", sname);
2507 	printf("%.2f %.2f %.2f\n",
2508 	       (double) loadavg.ldavg[0] / loadavg.fscale,
2509 	       (double) loadavg.ldavg[1] / loadavg.fscale,
2510 	       (double) loadavg.ldavg[2] / loadavg.fscale);
2511 }
2512 
2513 /*ARGSUSED*/
2514 static void
2515 proc_limit(HANDLER_ARGS)
2516 {
2517 	u_quad_t olim, *newp, nlim;
2518 	size_t osz, nsz;
2519 	char *t;
2520 	int rc;
2521 
2522 	if (fn)
2523 		trim_whitespace(value, 3);
2524 
2525 	osz = sizeof(olim);
2526 	if (value != NULL) {
2527 		nsz = sizeof(nlim);
2528 		newp = &nlim;
2529 		if (strcmp(value, "unlimited") == 0)
2530 			nlim = RLIM_INFINITY;
2531 		else {
2532 			errno = 0;
2533 			nlim = strtouq(value, &t, 0);
2534 			if (t == value || *t != '\0' || errno != 0) {
2535 				sysctlperror("%s: '%s' is not a valid limit\n",
2536 					     sname, value);
2537 				EXIT(EXIT_FAILURE);
2538 			}
2539 		}
2540 	}
2541 	else {
2542 		nsz = 0;
2543 		newp = NULL;
2544 	}
2545 
2546 	rc = prog_sysctl(name, namelen, &olim, &osz, newp, nsz);
2547 	if (rc == -1) {
2548 		sysctlerror(newp == NULL);
2549 		return;
2550 	}
2551 
2552 	if (newp && qflag)
2553 		return;
2554 
2555 	if (rflag || xflag || olim != RLIM_INFINITY)
2556 		display_number(pnode, sname, &olim, sizeof(olim),
2557 			       newp ? DISPLAY_OLD : DISPLAY_VALUE);
2558 	else
2559 		display_string(pnode, sname, "unlimited", 10,
2560 			       newp ? DISPLAY_OLD : DISPLAY_VALUE);
2561 
2562 	if (newp) {
2563 		if (rflag || xflag || nlim != RLIM_INFINITY)
2564 			display_number(pnode, sname, &nlim, sizeof(nlim),
2565 				       DISPLAY_NEW);
2566 		else
2567 			display_string(pnode, sname, "unlimited", 10,
2568 				       DISPLAY_NEW);
2569 	}
2570 }
2571 
2572 #ifdef CPU_DISKINFO
2573 /*ARGSUSED*/
2574 static void
2575 machdep_diskinfo(HANDLER_ARGS)
2576 {
2577 	struct disklist *dl;
2578 	struct biosdisk_info *bi;
2579 	struct nativedisk_info *ni;
2580 	int rc;
2581 	size_t sz;
2582 	uint i, b, lim;
2583 
2584 	rc = prog_sysctl(name, namelen, NULL, &sz, NULL, 0);
2585 	if (rc == -1) {
2586 		sysctlerror(1);
2587 		return;
2588 	}
2589 	dl = malloc(sz);
2590 	if (dl == NULL) {
2591 		sysctlerror(1);
2592 		return;
2593 	}
2594 	rc = prog_sysctl(name, namelen, dl, &sz, NULL, 0);
2595 	if (rc == -1) {
2596 		sysctlerror(1);
2597 		return;
2598 	}
2599 
2600 	if (!nflag)
2601 		printf("%s: ", sname);
2602 	lim = dl->dl_nbiosdisks;
2603 	if (lim > MAX_BIOSDISKS)
2604 		lim = MAX_BIOSDISKS;
2605 	for (bi = dl->dl_biosdisks, i = 0; i < lim; bi++, i++)
2606 		printf("%x:%" PRIu64 "(%d/%d/%d),%x ",
2607 		       bi->bi_dev, bi->bi_lbasecs,
2608 		       bi->bi_cyl, bi->bi_head, bi->bi_sec,
2609 		       bi->bi_flags);
2610 	lim = dl->dl_nnativedisks;
2611 	ni = dl->dl_nativedisks;
2612 	bi = dl->dl_biosdisks;
2613 	/* LINTED -- pointer casts are tedious */
2614 	if ((char *)&ni[lim] != (char *)dl + sz) {
2615 		sysctlperror("%s: size mismatch\n", gsname);
2616 		return;
2617 	}
2618 	for (i = 0; i < lim; ni++, i++) {
2619 		char t = ':';
2620 		printf(" %.*s", (int)sizeof ni->ni_devname,
2621 		       ni->ni_devname);
2622 		for (b = 0; b < (unsigned int)ni->ni_nmatches; t = ',', b++)
2623 			printf("%c%x", t,
2624 			       bi[ni->ni_biosmatches[b]].bi_dev);
2625 	}
2626 	printf("\n");
2627 	free(dl);
2628 }
2629 #endif /* CPU_DISKINFO */
2630 
2631 /*ARGSUSED*/
2632 static void
2633 mode_bits(HANDLER_ARGS)
2634 {
2635 	char buf[12], outbuf[100];
2636 	int o, m, *newp, rc;
2637 	size_t osz, nsz;
2638 	mode_t om, mm;
2639 
2640 	if (fn)
2641 		trim_whitespace(value, 3);
2642 
2643 	newp = NULL;
2644 	osz = sizeof(o);
2645 	if (value != NULL) {
2646 		void *foo;
2647 		int tt;
2648 		size_t ttsz = sizeof(tt);
2649 		mode_t old_umask;
2650 
2651 		nsz = sizeof(m);
2652 		newp = &m;
2653 		errno = 0;
2654 		rc = prog_sysctl(name, namelen, &tt, &ttsz, NULL, 0);
2655 		if (rc == -1) {
2656 			sysctlperror("%s: failed query\n", sname);
2657 			return;
2658 		}
2659 
2660 		old_umask = umask(0);
2661 		foo = setmode(value);
2662 		umask(old_umask);
2663 		if (foo == NULL) {
2664 			sysctlperror("%s: '%s' is an invalid mode\n", sname,
2665 				     value);
2666 			EXIT(EXIT_FAILURE);
2667 		}
2668 		old_umask = umask(0);
2669 		m = getmode(foo, (mode_t)tt);
2670 		umask(old_umask);
2671 		if (errno) {
2672 			sysctlperror("%s: '%s' is an invalid mode\n", sname,
2673 				     value);
2674 			EXIT(EXIT_FAILURE);
2675 		}
2676 	}
2677 	else {
2678 		nsz = 0;
2679 		newp = NULL;
2680 	}
2681 
2682 	rc = prog_sysctl(name, namelen, &o, &osz, newp, nsz);
2683 	if (rc == -1) {
2684 		sysctlerror(newp == NULL);
2685 		return;
2686 	}
2687 
2688 	if (newp && qflag)
2689 		return;
2690 
2691 	om = (mode_t)o;
2692 	mm = (mode_t)m;
2693 
2694 	if (rflag || xflag)
2695 		display_number(pnode, sname, &o, sizeof(o),
2696 			       newp ? DISPLAY_OLD : DISPLAY_VALUE);
2697 	else {
2698 		memset(buf, 0, sizeof(buf));
2699 		strmode(om, buf);
2700 		rc = snprintf(outbuf, sizeof(outbuf), "%04o (%s)", om, buf + 1);
2701 		display_string(pnode, sname, outbuf, rc, newp ? DISPLAY_OLD : DISPLAY_VALUE);
2702 	}
2703 
2704 	if (newp) {
2705 		if (rflag || xflag)
2706 			display_number(pnode, sname, &m, sizeof(m),
2707 				       DISPLAY_NEW);
2708 		else {
2709 			memset(buf, 0, sizeof(buf));
2710 			strmode(mm, buf);
2711 			rc = snprintf(outbuf, sizeof(outbuf), "%04o (%s)", mm, buf + 1);
2712 			display_string(pnode, sname, outbuf, rc, DISPLAY_NEW);
2713 		}
2714 	}
2715 }
2716 
2717 typedef __BITMAP_TYPE(, uint32_t, 0x10000) bitmap;
2718 
2719 static char *
2720 bitmask_print(const bitmap *o)
2721 {
2722 	char *s, *os;
2723 
2724 	s = os = NULL;
2725 	for (size_t i = 0; i < MAXPORTS; i++)
2726 		if (__BITMAP_ISSET(i, o)) {
2727 			int rv;
2728 
2729 			if (os)
2730 			    	rv = asprintf(&s, "%s,%zu", os, i);
2731 			else
2732 			    	rv = asprintf(&s, "%zu", i);
2733 			if (rv == -1)
2734 				err(EXIT_FAILURE, "%s 1", __func__);
2735 			free(os);
2736 			os = s;
2737 		}
2738 	if (s == NULL && (s = strdup("")) == NULL)
2739 		err(EXIT_FAILURE, "%s 2", __func__);
2740 	return s;
2741 }
2742 
2743 static void
2744 bitmask_scan(const void *v, bitmap *o)
2745 {
2746 	char *s = strdup(v);
2747 	if (s == NULL)
2748 		err(EXIT_FAILURE, "%s", __func__);
2749 
2750 	__BITMAP_ZERO(o);
2751 	for (s = strtok(s, ","); s; s = strtok(NULL, ",")) {
2752 		char *e;
2753 		errno = 0;
2754 		unsigned long l = strtoul(s, &e, 0);
2755 		if ((l == ULONG_MAX && errno == ERANGE) || s == e || *e)
2756 			errx(EXIT_FAILURE, "Invalid port: %s", s);
2757 		if (l >= MAXPORTS)
2758 			errx(EXIT_FAILURE, "Port out of range: %s", s);
2759 		__BITMAP_SET(l, o);
2760 	}
2761 }
2762 
2763 
2764 static void
2765 reserve(HANDLER_ARGS)
2766 {
2767 	int rc;
2768 	size_t osz, nsz;
2769 	bitmap o, n;
2770 
2771 	if (fn)
2772 		trim_whitespace(value, 3);
2773 
2774 	osz = sizeof(o);
2775 	if (value) {
2776 		bitmask_scan(value, &n);
2777 		value = (char *)&n;
2778 		nsz = sizeof(n);
2779 	} else
2780 		nsz = 0;
2781 
2782 	rc = prog_sysctl(name, namelen, &o, &osz, value, nsz);
2783 	if (rc == -1) {
2784 		sysctlerror(value == NULL);
2785 		return;
2786 	}
2787 
2788 	if (value && qflag)
2789 		return;
2790 
2791 	if (rflag || xflag)
2792 		display_struct(pnode, sname, &o, sizeof(o),
2793 		    value ? DISPLAY_OLD : DISPLAY_VALUE);
2794 	else {
2795 		char *s = bitmask_print(&o);
2796 		display_string(pnode, sname, s, strlen(s),
2797 		    value ? DISPLAY_OLD : DISPLAY_VALUE);
2798 		free(s);
2799 	}
2800 
2801 	if (value) {
2802 		if (rflag || xflag)
2803 			display_struct(pnode, sname, &n, sizeof(n),
2804 			    DISPLAY_NEW);
2805 		else {
2806 			char *s = bitmask_print(&n);
2807 			display_string(pnode, sname, s, strlen(s), DISPLAY_NEW);
2808 			free(s);
2809 		}
2810 	}
2811 }
2812