xref: /freebsd/usr.sbin/jls/jls.c (revision 9ae680fcfd36e03547ffc7989c80dcbaac200556)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
5  * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org>
6  * Copyright (c) 2009 James Gritton <jamie@FreeBSD.org>
7  * Copyright (c) 2015 Emmanuel Vadot <manu@bocal.org>
8  * All rights reserved.
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/jail.h>
34 #include <sys/socket.h>
35 #include <sys/sysctl.h>
36 
37 #include <arpa/inet.h>
38 #include <netinet/in.h>
39 
40 #include <assert.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <jail.h>
44 #include <limits.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <libxo/xo.h>
50 
51 #define	JP_USER		0x01000000
52 #define	JP_OPT		0x02000000
53 
54 #define JLS_XO_VERSION	"2"
55 
56 #define	PRINT_DEFAULT	0x01
57 #define	PRINT_HEADER	0x02
58 #define	PRINT_NAMEVAL	0x04
59 #define	PRINT_QUOTED	0x08
60 #define	PRINT_SKIP	0x10
61 #define	PRINT_VERBOSE	0x20
62 #define	PRINT_JAIL_NAME	0x40
63 #define	PRINT_EXISTS	0x80
64 
65 static struct jailparam *params;
66 static int *param_parent;
67 static int nparams;
68 #ifdef INET6
69 static int ip6_ok;
70 #endif
71 #ifdef INET
72 static int ip4_ok;
73 #endif
74 
75 static int add_param(const char *name, void *value, size_t valuelen,
76 		struct jailparam *source, unsigned flags);
77 static int sort_param(const void *a, const void *b);
78 static char *noname(const char *name);
79 static char *nononame(const char *name);
80 static int print_jail(int pflags, int jflags);
81 static int special_print(int pflags, struct jailparam *param);
82 static void quoted_print(int pflags, char *name, char *value);
83 static void emit_ip_addr_list(int af_family, const char *list_name,
84 		struct jailparam *param);
85 
86 static void
usage(void)87 usage(void)
88 {
89 	xo_errx(1,
90 	    "usage: jls [-dhNnqv] [-j jail] [param ...]\n"
91 	    "            jls -c [-d] -j jail");
92 }
93 
94 int
main(int argc,char ** argv)95 main(int argc, char **argv)
96 {
97 	char *dot, *ep, *jname, *pname;
98 	int c, i, jflags, jid, lastjid, pflags, spc;
99 
100 	argc = xo_parse_args(argc, argv);
101 	if (argc < 0)
102 		exit(1);
103 
104         xo_set_version(JLS_XO_VERSION);
105 	jname = NULL;
106 	pflags = jflags = jid = 0;
107 	while ((c = getopt(argc, argv, "acdj:hNnqsv")) >= 0)
108 		switch (c) {
109 		case 'a':
110 		case 'd':
111 			jflags |= JAIL_DYING;
112 			break;
113 		case 'c':
114 			pflags |= PRINT_EXISTS;
115 			break;
116 		case 'j':
117 			jid = strtoul(optarg, &ep, 10);
118 			if (!jid || *ep) {
119 				jid = 0;
120 				jname = optarg;
121 			}
122 			break;
123 		case 'h':
124 			pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) |
125 			    PRINT_HEADER;
126 			break;
127 		case 'N':
128 			pflags |= PRINT_JAIL_NAME;
129 			break;
130 		case 'n':
131 			pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL;
132 			break;
133 		case 'q':
134 			pflags |= PRINT_QUOTED;
135 			break;
136 		case 's':
137 			pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) |
138 			    PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP;
139 			break;
140 		case 'v':
141 			pflags = (pflags &
142 			    ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) |
143 			    PRINT_VERBOSE;
144 			break;
145 		default:
146 			usage();
147 		}
148 
149 #ifdef INET6
150 	ip6_ok = feature_present("inet6");
151 #endif
152 #ifdef INET
153 	ip4_ok = feature_present("inet");
154 #endif
155 
156 	argc -= optind;
157 	argv += optind;
158 
159 	/* Add the parameters to print. */
160 	if ((pflags & PRINT_EXISTS) != 0) {
161 		if ((pflags & ~PRINT_EXISTS) != 0) {
162 			xo_warnx("-c is incompatible with other print options");
163 			usage();
164 		} else if (argc != 0) {
165 			xo_warnx("-c does not accept non-option arguments");
166 			usage();
167 		} else if (jid == 0 && jname == NULL) {
168 			xo_warnx("-j jail to check must be provided for -c");
169 			usage();
170 		}
171 
172 		/*
173 		 * Force libxo to be silent, as well -- we're only wanting our
174 		 * exit status.
175 		 */
176 		xo_set_style(NULL, XO_STYLE_TEXT);
177 	} else if (argc == 0) {
178 		if (pflags & (PRINT_HEADER | PRINT_NAMEVAL))
179 			add_param("all", NULL, (size_t)0, NULL, JP_USER);
180 		else if (pflags & PRINT_VERBOSE) {
181 			add_param(JAIL_PARAM_JID, NULL, (size_t)0, NULL,
182 			    JP_USER);
183 			add_param(JAIL_PARAM_HOST_HOSTNAME, NULL, (size_t)0,
184 			    NULL, JP_USER);
185 			add_param(JAIL_PARAM_PATH, NULL, (size_t)0, NULL,
186 			    JP_USER);
187 			add_param(JAIL_PARAM_NAME, NULL, (size_t)0, NULL,
188 			    JP_USER);
189 			add_param(JAIL_PARAM_DYING, NULL, (size_t)0, NULL,
190 			    JP_USER);
191 			add_param(JAIL_PARAM_CPUSET_ID, NULL, (size_t)0, NULL,
192 			    JP_USER);
193 #ifdef INET
194 			if (ip4_ok)
195 				add_param(JAIL_PARAM_IP4_ADDR, NULL,
196 				    (size_t)0, NULL, JP_USER);
197 #endif
198 #ifdef INET6
199 			if (ip6_ok)
200 				add_param(JAIL_PARAM_IP6_ADDR, NULL,
201 				    (size_t)0, NULL, JP_USER | JP_OPT);
202 #endif
203 		} else {
204 			pflags |= PRINT_DEFAULT;
205 			if (pflags & PRINT_JAIL_NAME)
206 				add_param(JAIL_PARAM_NAME, NULL, (size_t)0,
207 				    NULL, JP_USER);
208 			else
209 				add_param(JAIL_PARAM_JID, NULL, (size_t)0,
210 				    NULL, JP_USER);
211 #ifdef INET
212 			if (ip4_ok)
213 				add_param(JAIL_PARAM_IP4_ADDR, NULL,
214 				    (size_t)0, NULL, JP_USER);
215 #endif
216 			add_param(JAIL_PARAM_HOST_HOSTNAME, NULL, (size_t)0,
217 			    NULL, JP_USER);
218 			add_param(JAIL_PARAM_PATH, NULL, (size_t)0, NULL,
219 			    JP_USER);
220 		}
221 	} else {
222 		pflags &= ~PRINT_VERBOSE;
223 		for (i = 0; i < argc; i++)
224 			add_param(argv[i], NULL, (size_t)0, NULL, JP_USER);
225 	}
226 
227 	if (pflags & PRINT_SKIP) {
228 		/* Check for parameters with jailsys parents. */
229 		for (i = 0; i < nparams; i++) {
230 			if ((params[i].jp_flags & JP_USER) &&
231 			    (dot = strchr(params[i].jp_name, '.'))) {
232 				pname = alloca((dot - params[i].jp_name) + 1);
233 				strlcpy(pname, params[i].jp_name,
234 				    (dot - params[i].jp_name) + 1);
235 				param_parent[i] = add_param(pname,
236 				    NULL, (size_t)0, NULL, JP_OPT);
237 			}
238 		}
239 	}
240 
241 	/* Add the index key parameters. */
242 	if (jid != 0)
243 		add_param(JAIL_PARAM_JID, &jid, sizeof(jid), NULL, 0);
244 	else if (jname != NULL)
245 		add_param(JAIL_PARAM_NAME, jname, strlen(jname), NULL, 0);
246 	else
247 		add_param(JAIL_PARAM_LASTJID, &lastjid, sizeof(lastjid),
248 		    NULL, 0);
249 
250 	/* Print a header line if requested. */
251 	if (pflags & PRINT_VERBOSE) {
252 		xo_emit("{T:/%3s}{T:JID}{P:  }{T:Hostname}{Pd:/%22s}{T:Path}\n",
253 		        "", "");
254 		xo_emit("{P:/%8s}{T:Name}{Pd:/%26s}{T:State}\n", "", "");
255 		xo_emit("{P:/%8s}{T:CPUSetID}\n", "");
256 		xo_emit("{P:/%8s}{T:IP Address(es)}\n", "");
257 	}
258 	else if (pflags & PRINT_DEFAULT)
259 		if (pflags & PRINT_JAIL_NAME)
260 			xo_emit("{P: }{T:JID/%-15s}{P: }{T:IP Address/%-15s}"
261 			        "{P: }{T:Hostname/%-29s}{P: }{T:Path}\n");
262 		else
263 			xo_emit("{T:JID/%6s}{P:  }{T:IP Address}{P:/%6s}"
264 			        "{T:Hostname}{P:/%22s}{T:Path}\n", "", "");
265 	else if (pflags & PRINT_HEADER) {
266 		for (i = spc = 0; i < nparams; i++)
267 			if (params[i].jp_flags & JP_USER) {
268 				if (spc)
269 					xo_emit("{P: }");
270 				else
271 					spc = 1;
272 				xo_emit(params[i].jp_name);
273 			}
274 		xo_emit("{P:\n}");
275 	}
276 
277 	xo_open_container("jail-information");
278 	xo_open_list("jail");
279 	/* Fetch the jail(s) and print the parameters. */
280 	if (jid != 0 || jname != NULL) {
281 		if (print_jail(pflags, jflags) < 0) {
282 			/*
283 			 * We omit errors from existential issues if we're just
284 			 * doing a -c check that the jail exists.
285 			 */
286 			if (pflags & PRINT_EXISTS)
287 				exit(1);
288 			xo_errx(1, "%s", jail_errmsg);
289 		}
290 	} else {
291 		assert((pflags & PRINT_EXISTS) == 0);
292 		for (lastjid = 0;
293 		     (lastjid = print_jail(pflags, jflags)) >= 0; )
294 			;
295 		if (errno != 0 && errno != ENOENT)
296 			xo_errx(1, "%s", jail_errmsg);
297 	}
298 	xo_close_list("jail");
299 	xo_close_container("jail-information");
300 	if (xo_finish() < 0)
301 		xo_err(1, "stdout");
302 	exit(0);
303 }
304 
305 static int
add_param(const char * name,void * value,size_t valuelen,struct jailparam * source,unsigned flags)306 add_param(const char *name, void *value, size_t valuelen,
307     struct jailparam *source, unsigned flags)
308 {
309 	struct jailparam *param, *tparams;
310 	int i, tnparams;
311 
312 	static int paramlistsize;
313 
314 	/* The pseudo-parameter "all" scans the list of available parameters. */
315 	if (!strcmp(name, "all")) {
316 		tnparams = jailparam_all(&tparams);
317 		if (tnparams < 0)
318 			xo_errx(1, "%s", jail_errmsg);
319 		qsort(tparams, (size_t)tnparams, sizeof(struct jailparam),
320 		    sort_param);
321 		for (i = 0; i < tnparams; i++)
322 			add_param(tparams[i].jp_name, NULL, (size_t)0,
323 			    tparams + i, flags);
324 		free(tparams);
325 		return -1;
326 	}
327 
328 	/* Check for repeat parameters. */
329 	for (i = 0; i < nparams; i++)
330 		if (!strcmp(name, params[i].jp_name)) {
331 			if (value != NULL && jailparam_import_raw(params + i,
332 			    value, valuelen) < 0)
333 				xo_errx(1, "%s", jail_errmsg);
334 			params[i].jp_flags |= flags;
335 			if (source != NULL)
336 				jailparam_free(source, 1);
337 			return i;
338 		}
339 
340 	/* Make sure there is room for the new param record. */
341 	if (!nparams) {
342 		paramlistsize = 32;
343 		params = malloc(paramlistsize * sizeof(*params));
344 		param_parent = malloc(paramlistsize * sizeof(*param_parent));
345 		if (params == NULL || param_parent == NULL)
346 			xo_err(1, "malloc");
347 	} else if (nparams >= paramlistsize) {
348 		paramlistsize *= 2;
349 		params = realloc(params, paramlistsize * sizeof(*params));
350 		param_parent = realloc(param_parent,
351 		    paramlistsize * sizeof(*param_parent));
352 		if (params == NULL || param_parent == NULL)
353 			xo_err(1, "realloc");
354 	}
355 
356 	/* Look up the parameter. */
357 	param_parent[nparams] = -1;
358 	param = params + nparams++;
359 	if (source != NULL) {
360 		*param = *source;
361 		param->jp_flags |= flags;
362 		return param - params;
363 	}
364 	if (jailparam_init(param, name) < 0 ||
365 	    (value != NULL ? jailparam_import_raw(param, value, valuelen)
366 	     : jailparam_import(param, value)) < 0) {
367 		if (flags & JP_OPT) {
368 			nparams--;
369 			return (-1);
370 		}
371 		xo_errx(1, "%s", jail_errmsg);
372 	}
373 	param->jp_flags |= flags;
374 	return param - params;
375 }
376 
377 static int
sort_param(const void * a,const void * b)378 sort_param(const void *a, const void *b)
379 {
380 	const struct jailparam *parama, *paramb;
381 	char *ap, *bp;
382 
383 	/* Put top-level parameters first. */
384 	parama = a;
385 	paramb = b;
386 	ap = strchr(parama->jp_name, '.');
387 	bp = strchr(paramb->jp_name, '.');
388 	if (ap && !bp)
389 		return (1);
390 	if (bp && !ap)
391 		return (-1);
392 	return (strcmp(parama->jp_name, paramb->jp_name));
393 }
394 
395 static char *
noname(const char * name)396 noname(const char *name)
397 {
398 	char *nname, *p;
399 
400 	nname = malloc(strlen(name) + 3);
401 	if (nname == NULL)
402 		xo_err(1, "malloc");
403 	p = strrchr(name, '.');
404 	if (p != NULL)
405 		sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1);
406 	else
407 		sprintf(nname, "no%s", name);
408 	return nname;
409 }
410 
411 static char *
nononame(const char * name)412 nononame(const char *name)
413 {
414 	char *nname, *p;
415 
416 	p = strrchr(name, '.');
417 	if (strncmp(p ? p + 1 : name, "no", 2))
418 		return NULL;
419 	nname = malloc(strlen(name) - 1);
420 	if (nname == NULL)
421 		xo_err(1, "malloc");
422 	if (p != NULL)
423 		sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3);
424 	else
425 		strcpy(nname, name + 2);
426 	return nname;
427 }
428 
429 static int
print_jail(int pflags,int jflags)430 print_jail(int pflags, int jflags)
431 {
432 	char *nname, *xo_nname;
433 	char **param_values;
434 	int i, jid, spc;
435 #if (defined INET || defined INET6)
436 	int n;
437 #endif
438 
439 	jid = jailparam_get(params, nparams, jflags);
440 	if (jid < 0)
441 		return jid;
442 	else if (pflags & PRINT_EXISTS)
443 		return 0;
444 
445 	xo_open_instance("jail");
446 
447 	if (pflags & PRINT_VERBOSE) {
448 		xo_emit("{:jid/%6d}{P:  }{:hostname/%-29.29s/%s}{P: }"
449 		    "{:path/%.74s/%s}\n",
450 		    *(int *)params[0].jp_value,
451 		    (char *)params[1].jp_value,
452 		    (char *)params[2].jp_value);
453 		xo_emit("{P:        }{:name/%-29.29s/%s}{P: }{:state/%.74s}\n",
454 		    (char *)params[3].jp_value,
455 		    *(int *)params[4].jp_value ? "DYING" : "ACTIVE");
456 		xo_emit("{P:        }{:cpusetid/%d}\n", *(int *)params[5].jp_value);
457 #if (defined INET || defined INET6)
458 		n = 6;
459 #endif
460 #ifdef INET
461 		if (ip4_ok && !strcmp(params[n].jp_name, JAIL_PARAM_IP4_ADDR)) {
462 			emit_ip_addr_list(AF_INET, "ipv4_addrs", params + n);
463 			n++;
464 		}
465 #endif
466 #ifdef INET6
467 		if (ip6_ok && !strcmp(params[n].jp_name, JAIL_PARAM_IP6_ADDR)) {
468 			emit_ip_addr_list(AF_INET6, "ipv6_addrs", params + n);
469 			n++;
470 		}
471 #endif
472 	} else if (pflags & PRINT_DEFAULT) {
473 		if (pflags & PRINT_JAIL_NAME)
474 			xo_emit("{P: }{:name/%-15s/%s}{P: }",
475 			    (char *)params[0].jp_value);
476 		else
477 			xo_emit("{:jid/%6d}{P:  }", *(int *)params[0].jp_value);
478 		xo_emit("{:ipv4/%-15.15s/%s}{P: }{:hostname/%-29.29s/%s}{P: }{:path/%.74s/%s}\n",
479 #ifdef INET
480 		    (!ip4_ok || params[1].jp_valuelen == 0) ? ""
481 		    : inet_ntoa(*(struct in_addr *)params[1].jp_value),
482 		    (char *)params[2-!ip4_ok].jp_value,
483 		    (char *)params[3-!ip4_ok].jp_value);
484 #else
485 		    "-",
486 		    (char *)params[1].jp_value,
487 		    (char *)params[2].jp_value);
488 #endif
489 	} else {
490 		param_values = alloca(nparams * sizeof(*param_values));
491 		for (i = 0; i < nparams; i++) {
492 			if (!(params[i].jp_flags & JP_USER))
493 				continue;
494 			param_values[i] = jailparam_export(params + i);
495 			if (param_values[i] == NULL)
496 				xo_errx(1, "%s", jail_errmsg);
497 		}
498 		for (i = spc = 0; i < nparams; i++) {
499 			if (!(params[i].jp_flags & JP_USER))
500 				continue;
501 			if ((pflags & PRINT_SKIP) &&
502 			    !(params[i].jp_flags & JP_KEYVALUE) &&
503 			    ((!(params[i].jp_ctltype &
504 				(CTLFLAG_WR | CTLFLAG_TUN))) ||
505 			     (param_parent[i] >= 0 &&
506 			      *(int *)params[param_parent[i]].jp_value !=
507 			      JAIL_SYS_NEW)))
508 				continue;
509 			if (spc)
510 				xo_emit("{P: }");
511 			else
512 				spc = 1;
513 			if ((params[i].jp_flags & JP_KEYVALUE) &&
514 			    params[i].jp_valuelen == 0) {
515 				/* Communicate back a missing key. */
516 				if (pflags & PRINT_NAMEVAL)
517 					xo_emit("{d:%s}", params[i].jp_name);
518 				continue;
519 			}
520 			if (pflags & PRINT_NAMEVAL) {
521 				/*
522 				 * Generally "name=value", but for booleans
523 				 * either "name" or "noname".
524 				 */
525 				if (params[i].jp_flags &
526 				    (JP_BOOL | JP_NOBOOL)) {
527 					if (*(int *)params[i].jp_value) {
528 						asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name);
529 						xo_emit(xo_nname);
530 						xo_emit("{d:/%s}", params[i].jp_name);
531 					}
532 					else {
533 						nname = (params[i].jp_flags &
534 						    JP_NOBOOL) ?
535 						    nononame(params[i].jp_name)
536 						    : noname(params[i].jp_name);
537 						if (params[i].jp_flags & JP_NOBOOL) {
538 							asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name);
539 							xo_emit(xo_nname);
540 						} else {
541 							asprintf(&xo_nname, "{en:%s/false}", params[i].jp_name);
542 							xo_emit(xo_nname);
543 						}
544 						xo_emit("{d:/%s}", nname);
545 						free(nname);
546 					}
547 					free(xo_nname);
548 					continue;
549 				}
550 				xo_emit("{d:%s}=", params[i].jp_name);
551 			}
552 			if (!special_print(pflags, params + i))
553 				quoted_print(pflags, params[i].jp_name, param_values[i]);
554 		}
555 		xo_emit("{P:\n}");
556 		for (i = 0; i < nparams; i++)
557 			if (params[i].jp_flags & JP_USER)
558 				free(param_values[i]);
559 	}
560 
561 	xo_close_instance("jail");
562 	return (jid);
563 }
564 
565 static void
quoted_print(int pflags,char * name,char * value)566 quoted_print(int pflags, char *name, char *value)
567 {
568 	int qc;
569 	char *p = value;
570 
571 	/* An empty string needs quoting. */
572 	if (!*p) {
573 		xo_emit("{ea:/%s}{da:/\"\"}", name, value, name);
574 		return;
575 	}
576 
577 	/*
578 	 * The value will be surrounded by quotes if it contains
579 	 * whitespace or quotes.
580 	 */
581 	if (strchr(p, '\''))
582 		qc = '"';
583 	else if (strchr(p, '"'))
584 		qc = '\'';
585 	else {
586 		qc = 0;
587 		for (; *p; ++p)
588 			if (isspace(*p)) {
589 				qc = '"';
590 				break;
591 			}
592 	}
593 
594 	if (qc && pflags & PRINT_QUOTED)
595 		xo_emit("{P:/%c}", qc);
596 
597 	xo_emit("{a:/%s}", name, value);
598 
599 	if (qc && pflags & PRINT_QUOTED)
600 		xo_emit("{P:/%c}", qc);
601 }
602 
603 static int
special_print(int pflags,struct jailparam * param)604 special_print(int pflags, struct jailparam *param)
605 {
606 	int ip_as_list;
607 
608 	switch (xo_get_style(NULL)) {
609 	case XO_STYLE_JSON:
610 	case XO_STYLE_XML:
611 		ip_as_list = 1;
612 		break;
613 	default:
614 		ip_as_list = 0;
615 	}
616 
617 	if (!ip_as_list && param->jp_valuelen == 0) {
618 		if (pflags & PRINT_QUOTED)
619 			xo_emit("{P:\"\"}");
620 		else if (!(pflags & PRINT_NAMEVAL))
621 			xo_emit("{P:-}");
622 	} else if (ip_as_list && !strcmp(param->jp_name, JAIL_PARAM_IP4_ADDR)) {
623 		emit_ip_addr_list(AF_INET, param->jp_name, param);
624 	} else if (ip_as_list && !strcmp(param->jp_name, JAIL_PARAM_IP6_ADDR)) {
625 		emit_ip_addr_list(AF_INET6, param->jp_name, param);
626 	} else {
627 		return 0;
628 	}
629 
630 	return 1;
631 }
632 
633 static void
emit_ip_addr_list(int af_family,const char * list_name,struct jailparam * param)634 emit_ip_addr_list(int af_family, const char *list_name, struct jailparam *param)
635 {
636 	char ipbuf[INET6_ADDRSTRLEN];
637 	size_t addr_len;
638 	const char *emit_str;
639 	int ai, count;
640 
641 	switch (af_family) {
642 	case AF_INET:
643 		addr_len = sizeof(struct in_addr);
644 		emit_str = "{P:        }{ql:ipv4_addr}{P:\n}";
645 		break;
646 	case AF_INET6:
647 		addr_len = sizeof(struct in6_addr);
648 		emit_str = "{P:        }{ql:ipv6_addr}{P:\n}";
649 		break;
650 	default:
651 		xo_err(1, "unsupported af_family");
652 		return;
653 	}
654 
655 	count = param->jp_valuelen / addr_len;
656 
657 	xo_open_list(list_name);
658 	for (ai = 0; ai < count; ai++) {
659 		if (inet_ntop(af_family,
660 		    ((uint8_t *)param->jp_value) + addr_len * ai,
661 		    ipbuf, sizeof(ipbuf)) == NULL) {
662 			xo_err(1, "inet_ntop");
663 		} else {
664 			xo_emit(emit_str, ipbuf);
665 		}
666 	}
667 	xo_close_list(list_name);
668 }
669