jls.c revision 194364
1/*-
2 * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
3 * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org>
4 * Copyright (c) 2009 James Gritton <jamie@FreeBSD.org>
5 * 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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/usr.sbin/jls/jls.c 194364 2009-06-17 14:32:08Z jamie $");
31
32#include <sys/param.h>
33#include <sys/jail.h>
34#include <sys/socket.h>
35#include <sys/sysctl.h>
36#include <sys/uio.h>
37
38#include <arpa/inet.h>
39#include <netinet/in.h>
40
41#include <err.h>
42#include <errno.h>
43#include <limits.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48
49#define	SJPARAM		"security.jail.param"
50#define	ARRAY_SLOP	5
51
52#define	CTLTYPE_BOOL	(CTLTYPE + 1)
53#define	CTLTYPE_NOBOOL	(CTLTYPE + 2)
54#define	CTLTYPE_IPADDR	(CTLTYPE + 3)
55#define	CTLTYPE_IP6ADDR	(CTLTYPE + 4)
56
57#define	PARAM_KEY	0x01
58#define	PARAM_USER	0x02
59#define	PARAM_ARRAY	0x04
60#define	PARAM_OPT	0x08
61#define	PARAM_WR	0x10
62
63#define	PRINT_DEFAULT	0x01
64#define	PRINT_HEADER	0x02
65#define	PRINT_NAMEVAL	0x04
66#define	PRINT_QUOTED	0x08
67#define	PRINT_SKIP	0x10
68#define	PRINT_VERBOSE	0x20
69
70struct param {
71	char	*name;
72	void	*value;
73	size_t	 size;
74	int	 type;
75	unsigned flags;
76	int	 noparent;
77};
78
79struct iovec2 {
80	struct iovec	name;
81	struct iovec	value;
82};
83
84static struct param *params;
85static int nparams;
86static char errmsg[256];
87
88static int add_param(const char *name, void *value, unsigned flags);
89static int get_param(const char *name, struct param *param);
90static int sort_param(const void *a, const void *b);
91static char *noname(const char *name);
92static char *nononame(const char *name);
93static int print_jail(int pflags, int jflags);
94static void quoted_print(char *str, int len);
95
96int
97main(int argc, char **argv)
98{
99	char *dot, *ep, *jname, *nname;
100	int c, i, jflags, jid, lastjid, pflags, spc;
101
102	jname = NULL;
103	pflags = jflags = jid = 0;
104	while ((c = getopt(argc, argv, "adj:hnqsv")) >= 0)
105		switch (c) {
106		case 'a':
107		case 'd':
108			jflags |= JAIL_DYING;
109			break;
110		case 'j':
111			jid = strtoul(optarg, &ep, 10);
112			if (!*optarg || *ep)
113				jname = optarg;
114			break;
115		case 'h':
116			pflags = (pflags & ~PRINT_SKIP) | PRINT_HEADER;
117			break;
118		case 'n':
119			pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL;
120			break;
121		case 'q':
122			pflags |= PRINT_QUOTED;
123			break;
124		case 's':
125			pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) |
126			    PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP;
127			break;
128		case 'v':
129			pflags = (pflags & ~(PRINT_NAMEVAL | PRINT_SKIP)) |
130			    PRINT_VERBOSE;
131			break;
132		default:
133			errx(1, "usage: jls [-dhnqv] [-j jail] [param ...]");
134		}
135
136	/* Add the parameters to print. */
137	if (optind == argc) {
138		if (pflags & PRINT_VERBOSE) {
139			add_param("jid", NULL, PARAM_USER);
140			add_param("host.hostname", NULL, PARAM_USER);
141			add_param("path", NULL, PARAM_USER);
142			add_param("name", NULL, PARAM_USER);
143			add_param("dying", NULL, PARAM_USER);
144			add_param("cpuset.id", NULL, PARAM_USER);
145			add_param("ip4.addr", NULL, PARAM_USER);
146			add_param("ip6.addr", NULL, PARAM_USER | PARAM_OPT);
147		} else {
148			pflags = (pflags &
149			    ~(PRINT_NAMEVAL | PRINT_SKIP | PRINT_VERBOSE)) |
150			    PRINT_DEFAULT;
151			add_param("jid", NULL, PARAM_USER);
152			add_param("ip4.addr", NULL, PARAM_USER);
153			add_param("host.hostname", NULL, PARAM_USER);
154			add_param("path", NULL, PARAM_USER);
155		}
156	} else
157		while (optind < argc)
158			add_param(argv[optind++], NULL, PARAM_USER);
159
160	if (pflags & PRINT_SKIP) {
161		/* Check for parameters with boolean parents. */
162		for (i = 0; i < nparams; i++) {
163			if ((params[i].flags & PARAM_USER) &&
164			    (dot = strchr(params[i].name, '.'))) {
165				*dot = 0;
166				nname = noname(params[i].name);
167				*dot = '.';
168				params[i].noparent =
169				    add_param(nname, NULL, PARAM_OPT);
170				free(nname);
171			}
172		}
173	}
174
175	/* Add the index key and errmsg parameters. */
176	if (jid != 0)
177		add_param("jid", &jid, PARAM_KEY);
178	else if (jname != NULL)
179		add_param("name", jname, PARAM_KEY);
180	else
181		add_param("lastjid", &lastjid, PARAM_KEY);
182	add_param("errmsg", errmsg, PARAM_KEY);
183
184	/* Print a header line if requested. */
185	if (pflags & PRINT_VERBOSE)
186		printf("   JID  Hostname                      Path\n"
187		       "        Name                          State\n"
188		       "        CPUSetID\n"
189		       "        IP Address(es)\n");
190	else if (pflags & PRINT_DEFAULT)
191		printf("   JID  IP Address      "
192		       "Hostname                      Path\n");
193	else if (pflags & PRINT_HEADER) {
194		for (i = spc = 0; i < nparams; i++)
195			if (params[i].flags & PARAM_USER) {
196				if (spc)
197					putchar(' ');
198				else
199					spc = 1;
200				fputs(params[i].name, stdout);
201			}
202		putchar('\n');
203	}
204
205	/* Fetch the jail(s) and print the paramters. */
206	if (jid != 0 || jname != NULL) {
207		if (print_jail(pflags, jflags) < 0) {
208			if (errmsg[0])
209				errx(1, "%s", errmsg);
210			err(1, "jail_get");
211		}
212	} else {
213		for (lastjid = 0;
214		     (lastjid = print_jail(pflags, jflags)) >= 0; )
215			;
216		if (errno != 0 && errno != ENOENT) {
217			if (errmsg[0])
218				errx(1, "%s", errmsg);
219			err(1, "jail_get");
220		}
221	}
222
223	return (0);
224}
225
226static int
227add_param(const char *name, void *value, unsigned flags)
228{
229	struct param *param;
230	char *nname;
231	size_t mlen1, mlen2, buflen;
232	int mib1[CTL_MAXNAME], mib2[CTL_MAXNAME - 2];
233	int i, tnparams;
234	char buf[MAXPATHLEN];
235
236	static int paramlistsize;
237
238	/* The pseudo-parameter "all" scans the list of available parameters. */
239	if (!strcmp(name, "all")) {
240		tnparams = nparams;
241		mib1[0] = 0;
242		mib1[1] = 2;
243		mlen1 = CTL_MAXNAME - 2;
244		if (sysctlnametomib(SJPARAM, mib1 + 2, &mlen1) < 0)
245			err(1, "sysctlnametomib(" SJPARAM ")");
246		for (;;) {
247			/* Get the next parameter. */
248			mlen2 = sizeof(mib2);
249			if (sysctl(mib1, mlen1 + 2, mib2, &mlen2, NULL, 0) < 0)
250				err(1, "sysctl(0.2)");
251			if (mib2[0] != mib1[2] || mib2[1] != mib1[3] ||
252			    mib2[2] != mib1[4])
253				break;
254			/* Convert it to an ascii name. */
255			memcpy(mib1 + 2, mib2, mlen2);
256			mlen1 = mlen2 / sizeof(int);
257			mib1[1] = 1;
258			buflen = sizeof(buf);
259			if (sysctl(mib1, mlen1 + 2, buf, &buflen, NULL, 0) < 0)
260				err(1, "sysctl(0.1)");
261			add_param(buf + sizeof(SJPARAM), NULL, flags);
262			/*
263			 * Convert nobool parameters to bool if their
264			 * counterpart is a node, ortherwise discard them.
265			 */
266			param = &params[nparams - 1];
267			if (param->type == CTLTYPE_NOBOOL) {
268				nname = nononame(param->name);
269				if (get_param(nname, param) >= 0 &&
270				    param->type != CTLTYPE_NODE) {
271					free(nname);
272					nparams--;
273				} else {
274					free(param->name);
275					param->name = nname;
276					param->type = CTLTYPE_BOOL;
277					param->size = sizeof(int);
278					param->value = NULL;
279				}
280			}
281			mib1[1] = 2;
282		}
283
284		qsort(params + tnparams, (size_t)(nparams - tnparams),
285		    sizeof(struct param), sort_param);
286		return -1;
287	}
288
289	/* Check for repeat parameters. */
290	for (i = 0; i < nparams; i++)
291		if (!strcmp(name, params[i].name)) {
292			params[i].value = value;
293			params[i].flags |= flags;
294			return i;
295		}
296
297	/* Make sure there is room for the new param record. */
298	if (!nparams) {
299		paramlistsize = 32;
300		params = malloc(paramlistsize * sizeof(*params));
301		if (params == NULL)
302			err(1, "malloc");
303	} else if (nparams >= paramlistsize) {
304		paramlistsize *= 2;
305		params = realloc(params, paramlistsize * sizeof(*params));
306		if (params == NULL)
307			err(1, "realloc");
308	}
309
310	/* Look up the parameter. */
311	param = params + nparams++;
312	memset(param, 0, sizeof *param);
313	param->name = strdup(name);
314	if (param->name == NULL)
315		err(1, "strdup");
316	param->flags = flags;
317	param->noparent = -1;
318	/* We have to know about pseudo-parameters without asking. */
319	if (!strcmp(param->name, "lastjid")) {
320		param->type = CTLTYPE_INT;
321		param->size = sizeof(int);
322		goto got_type;
323	}
324	if (!strcmp(param->name, "errmsg")) {
325		param->type = CTLTYPE_STRING;
326		param->size = sizeof(errmsg);
327		goto got_type;
328	}
329	if (get_param(name, param) < 0) {
330		if (errno != ENOENT)
331			err(1, "sysctl(0.3.%s)", name);
332		/* See if this the "no" part of an existing boolean. */
333		if ((nname = nononame(name))) {
334			i = get_param(nname, param);
335			free(nname);
336			if (i >= 0 && param->type == CTLTYPE_BOOL) {
337				param->type = CTLTYPE_NOBOOL;
338				goto got_type;
339			}
340		}
341		if (flags & PARAM_OPT) {
342			nparams--;
343			return -1;
344		}
345		errx(1, "unknown parameter: %s", name);
346	}
347	if (param->type == CTLTYPE_NODE) {
348		/*
349		 * A node isn't normally a parameter, but may be a boolean
350		 * if its "no" counterpart exists.
351		 */
352		nname = noname(name);
353		i = get_param(nname, param);
354		free(nname);
355		if (i >= 0 && param->type == CTLTYPE_NOBOOL) {
356			param->type = CTLTYPE_BOOL;
357			goto got_type;
358		}
359		errx(1, "unknown parameter: %s", name);
360	}
361
362 got_type:
363	param->value = value;
364	return param - params;
365}
366
367static int
368get_param(const char *name, struct param *param)
369{
370	char *p;
371	size_t buflen, mlen;
372	int mib[CTL_MAXNAME];
373	struct {
374		int i;
375		char s[MAXPATHLEN];
376	} buf;
377
378	/* Look up the MIB. */
379	mib[0] = 0;
380	mib[1] = 3;
381	snprintf(buf.s, sizeof(buf.s), SJPARAM ".%s", name);
382	mlen = sizeof(mib) - 2 * sizeof(int);
383	if (sysctl(mib, 2, mib + 2, &mlen, buf.s, strlen(buf.s)) < 0)
384		return (-1);
385	/* Get the type and size. */
386	mib[1] = 4;
387	buflen = sizeof(buf);
388	if (sysctl(mib, (mlen / sizeof(int)) + 2, &buf, &buflen, NULL, 0) < 0)
389		err(1, "sysctl(0.4.%s)", name);
390	param->type = buf.i & CTLTYPE;
391	if (buf.i & (CTLFLAG_WR | CTLFLAG_TUN))
392		param->flags |= PARAM_WR;
393	p = strchr(buf.s, '\0');
394	if (p - 2 >= buf.s && !strcmp(p - 2, ",a")) {
395		p[-2] = 0;
396		param->flags |= PARAM_ARRAY;
397	}
398	switch (param->type) {
399	case CTLTYPE_INT:
400		/* An integer parameter might be a boolean. */
401		if (buf.s[0] == 'B')
402			param->type = buf.s[1] == 'N'
403			    ? CTLTYPE_NOBOOL : CTLTYPE_BOOL;
404	case CTLTYPE_UINT:
405		param->size = sizeof(int);
406		break;
407	case CTLTYPE_LONG:
408	case CTLTYPE_ULONG:
409		param->size = sizeof(long);
410		break;
411	case CTLTYPE_STRUCT:
412		if (!strcmp(buf.s, "S,in_addr")) {
413			param->type = CTLTYPE_IPADDR;
414			param->size = sizeof(struct in_addr);
415		} else if (!strcmp(buf.s, "S,in6_addr")) {
416			param->type = CTLTYPE_IP6ADDR;
417			param->size = sizeof(struct in6_addr);
418		}
419		break;
420	case CTLTYPE_STRING:
421		buf.s[0] = 0;
422		sysctl(mib + 2, mlen / sizeof(int), buf.s, &buflen, NULL, 0);
423		param->size = strtoul(buf.s, NULL, 10);
424		if (param->size == 0)
425			param->size = BUFSIZ;
426	}
427	return (0);
428}
429
430static int
431sort_param(const void *a, const void *b)
432{
433	const struct param *parama, *paramb;
434	char *ap, *bp;
435
436	/* Put top-level parameters first. */
437	parama = a;
438	paramb = b;
439	ap = strchr(parama->name, '.');
440	bp = strchr(paramb->name, '.');
441	if (ap && !bp)
442		return (1);
443	if (bp && !ap)
444		return (-1);
445	return (strcmp(parama->name, paramb->name));
446}
447
448static char *
449noname(const char *name)
450{
451	char *nname, *p;
452
453	nname = malloc(strlen(name) + 3);
454	if (nname == NULL)
455		err(1, "malloc");
456	p = strrchr(name, '.');
457	if (p != NULL)
458		sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1);
459	else
460		sprintf(nname, "no%s", name);
461	return nname;
462}
463
464static char *
465nononame(const char *name)
466{
467	char *nname, *p;
468
469	p = strrchr(name, '.');
470	if (strncmp(p ? p + 1 : name, "no", 2))
471		return NULL;
472	nname = malloc(strlen(name) - 1);
473	if (nname == NULL)
474		err(1, "malloc");
475	if (p != NULL)
476		sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3);
477	else
478		strcpy(nname, name + 2);
479	return nname;
480}
481
482static int
483print_jail(int pflags, int jflags)
484{
485	char *nname;
486	int i, ai, jid, count, sanity, spc;
487	char ipbuf[INET6_ADDRSTRLEN];
488
489	static struct iovec2 *iov, *aiov;
490	static int narray, nkey;
491
492	/* Set up the parameter list(s) the first time around. */
493	if (iov == NULL) {
494		iov = malloc(nparams * sizeof(struct iovec2));
495		if (iov == NULL)
496			err(1, "malloc");
497		for (i = narray = 0; i < nparams; i++) {
498			iov[i].name.iov_base = params[i].name;
499			iov[i].name.iov_len = strlen(params[i].name) + 1;
500			iov[i].value.iov_base = params[i].value;
501			iov[i].value.iov_len =
502			    params[i].type == CTLTYPE_STRING &&
503			    params[i].value != NULL &&
504			    ((char *)params[i].value)[0] != '\0'
505			    ? strlen(params[i].value) + 1 : params[i].size;
506			if (params[i].flags & (PARAM_KEY | PARAM_ARRAY)) {
507				narray++;
508				if (params[i].flags & PARAM_KEY)
509					nkey++;
510			}
511		}
512		if (narray > nkey) {
513			aiov = malloc(narray * sizeof(struct iovec2));
514			if (aiov == NULL)
515				err(1, "malloc");
516			for (i = ai = 0; i < nparams; i++)
517				if (params[i].flags &
518				    (PARAM_KEY | PARAM_ARRAY))
519					aiov[ai++] = iov[i];
520		}
521	}
522	/* If there are array parameters, find their sizes. */
523	if (aiov != NULL) {
524		for (ai = 0; ai < narray; ai++)
525			if (aiov[ai].value.iov_base == NULL)
526				aiov[ai].value.iov_len = 0;
527		if (jail_get((struct iovec *)aiov, 2 * narray, jflags) < 0)
528			return (-1);
529	}
530	/* Allocate storage for all parameters. */
531	for (i = ai = 0; i < nparams; i++) {
532		if (params[i].flags & (PARAM_KEY | PARAM_ARRAY)) {
533			if (params[i].flags & PARAM_ARRAY) {
534				iov[i].value.iov_len = aiov[ai].value.iov_len +
535				    ARRAY_SLOP * params[i].size;
536				iov[i].value.iov_base =
537				    malloc(iov[i].value.iov_len);
538			}
539			ai++;
540		} else
541			iov[i].value.iov_base = malloc(params[i].size);
542		if (iov[i].value.iov_base == NULL)
543			err(1, "malloc");
544		if (params[i].value == NULL)
545			memset(iov[i].value.iov_base, 0, iov[i].value.iov_len);
546	}
547	/*
548	 * Get the actual prison.  If there are array elements, retry a few
549	 * times in case the size changed from under us.
550	 */
551	if ((jid = jail_get((struct iovec *)iov, 2 * nparams, jflags)) < 0) {
552		if (errno != EINVAL || aiov == NULL || errmsg[0])
553			return (-1);
554		for (sanity = 0;; sanity++) {
555			if (sanity == 10)
556				return (-1);
557			for (ai = 0; ai < narray; ai++)
558				if (params[i].flags & PARAM_ARRAY)
559					aiov[ai].value.iov_len = 0;
560			if (jail_get((struct iovec *)iov, 2 * narray, jflags) <
561			    0)
562				return (-1);
563			for (i = ai = 0; i < nparams; i++) {
564				if (!(params[i].flags &
565				    (PARAM_KEY | PARAM_ARRAY)))
566					continue;
567				if (params[i].flags & PARAM_ARRAY) {
568					iov[i].value.iov_len =
569					    aiov[ai].value.iov_len +
570					    ARRAY_SLOP * params[i].size;
571					iov[i].value.iov_base =
572					    realloc(iov[i].value.iov_base,
573					    iov[i].value.iov_len);
574					if (iov[i].value.iov_base == NULL)
575						err(1, "malloc");
576				}
577				ai++;
578			}
579		}
580	}
581	if (pflags & PRINT_VERBOSE) {
582		printf("%6d  %-29.29s %.74s\n"
583		       "%6s  %-29.29s %.74s\n"
584		       "%6s  %-6d\n",
585		    *(int *)iov[0].value.iov_base,
586		    (char *)iov[1].value.iov_base,
587		    (char *)iov[2].value.iov_base,
588		    "",
589		    (char *)iov[3].value.iov_base,
590		    *(int *)iov[4].value.iov_base ? "DYING" : "ACTIVE",
591		    "",
592		    *(int *)iov[5].value.iov_base);
593		count = iov[6].value.iov_len / sizeof(struct in_addr);
594		for (ai = 0; ai < count; ai++)
595			if (inet_ntop(AF_INET,
596			    &((struct in_addr *)iov[6].value.iov_base)[ai],
597			    ipbuf, sizeof(ipbuf)) == NULL)
598				err(1, "inet_ntop");
599			else
600				printf("%6s  %-15.15s\n", "", ipbuf);
601		if (!strcmp(params[7].name, "ip6.addr")) {
602			count = iov[7].value.iov_len / sizeof(struct in6_addr);
603			for (ai = 0; ai < count; ai++)
604				if (inet_ntop(AF_INET6, &((struct in_addr *)
605				    iov[7].value.iov_base)[ai],
606				    ipbuf, sizeof(ipbuf)) == NULL)
607					err(1, "inet_ntop");
608				else
609					printf("%6s  %-15.15s\n", "", ipbuf);
610		}
611	} else if (pflags & PRINT_DEFAULT)
612		printf("%6d  %-15.15s %-29.29s %.74s\n",
613		    *(int *)iov[0].value.iov_base,
614		    iov[1].value.iov_len == 0 ? "-"
615		    : inet_ntoa(*(struct in_addr *)iov[1].value.iov_base),
616		    (char *)iov[2].value.iov_base,
617		    (char *)iov[3].value.iov_base);
618	else {
619		for (i = spc = 0; i < nparams; i++) {
620			if (!(params[i].flags & PARAM_USER))
621				continue;
622			if ((pflags & PRINT_SKIP) &&
623			    ((!(params[i].flags & PARAM_WR)) ||
624			     (params[i].noparent >= 0 &&
625			      *(int *)iov[params[i].noparent].value.iov_base)))
626				continue;
627			if (spc)
628				putchar(' ');
629			else
630				spc = 1;
631			if (pflags & PRINT_NAMEVAL) {
632				/*
633				 * Generally "name=value", but for booleans
634				 * either "name" or "noname".
635				 */
636				switch (params[i].type) {
637				case CTLTYPE_BOOL:
638					if (*(int *)iov[i].value.iov_base)
639						printf("%s", params[i].name);
640					else {
641						nname = noname(params[i].name);
642						printf("%s", nname);
643						free(nname);
644					}
645					break;
646				case CTLTYPE_NOBOOL:
647					if (*(int *)iov[i].value.iov_base)
648						printf("%s", params[i].name);
649					else {
650						nname =
651						    nononame(params[i].name);
652						printf("%s", nname);
653						free(nname);
654					}
655					break;
656				default:
657					printf("%s=", params[i].name);
658				}
659			}
660			count = params[i].flags & PARAM_ARRAY
661			    ? iov[i].value.iov_len / params[i].size : 1;
662			if (count == 0) {
663				if (pflags & PRINT_QUOTED)
664					printf("\"\"");
665				else if (!(pflags & PRINT_NAMEVAL))
666					putchar('-');
667			}
668			for (ai = 0; ai < count; ai++) {
669				if (ai > 0)
670					putchar(',');
671				switch (params[i].type) {
672				case CTLTYPE_INT:
673					printf("%d", ((int *)
674					    iov[i].value.iov_base)[ai]);
675					break;
676				case CTLTYPE_UINT:
677					printf("%u", ((int *)
678					    iov[i].value.iov_base)[ai]);
679					break;
680				case CTLTYPE_IPADDR:
681					if (inet_ntop(AF_INET,
682					    &((struct in_addr *)
683					    iov[i].value.iov_base)[ai],
684					    ipbuf, sizeof(ipbuf)) == NULL)
685						err(1, "inet_ntop");
686					else
687						printf("%s", ipbuf);
688					break;
689				case CTLTYPE_IP6ADDR:
690					if (inet_ntop(AF_INET6,
691					    &((struct in6_addr *)
692					    iov[i].value.iov_base)[ai],
693					    ipbuf, sizeof(ipbuf)) == NULL)
694						err(1, "inet_ntop");
695					else
696						printf("%s", ipbuf);
697					break;
698				case CTLTYPE_LONG:
699					printf("%ld", ((long *)
700					    iov[i].value.iov_base)[ai]);
701				case CTLTYPE_ULONG:
702					printf("%lu", ((long *)
703					    iov[i].value.iov_base)[ai]);
704					break;
705				case CTLTYPE_STRING:
706					if (pflags & PRINT_QUOTED)
707						quoted_print((char *)
708						    iov[i].value.iov_base,
709						    params[i].size);
710					else
711						printf("%.*s",
712						    (int)params[i].size,
713						    (char *)
714						    iov[i].value.iov_base);
715					break;
716				case CTLTYPE_BOOL:
717				case CTLTYPE_NOBOOL:
718					if (!(pflags & PRINT_NAMEVAL))
719						printf(((int *)
720						    iov[i].value.iov_base)[ai]
721						    ? "true" : "false");
722				}
723			}
724		}
725		putchar('\n');
726	}
727	for (i = 0; i < nparams; i++)
728		if (params[i].value == NULL)
729			free(iov[i].value.iov_base);
730	return (jid);
731}
732
733static void
734quoted_print(char *str, int len)
735{
736	int c, qc;
737	char *p = str;
738	char *ep = str + len;
739
740	/* An empty string needs quoting. */
741	if (!*p) {
742		fputs("\"\"", stdout);
743		return;
744	}
745
746	/*
747	 * The value will be surrounded by quotes if it contains spaces
748	 * or quotes.
749	 */
750	qc = strchr(p, '\'') ? '"'
751	    : strchr(p, '"') ? '\''
752	    : strchr(p, ' ') || strchr(p, '\t') ? '"'
753	    : 0;
754	if (qc)
755		putchar(qc);
756	while (p < ep && (c = *p++)) {
757		if (c == '\\' || c == qc)
758			putchar('\\');
759		putchar(c);
760	}
761	if (qc)
762		putchar(qc);
763}
764