diff options
Diffstat (limited to 'usr.sbin/jls')
| -rw-r--r-- | usr.sbin/jls/Makefile | 16 | ||||
| -rw-r--r-- | usr.sbin/jls/Makefile.depend | 18 | ||||
| -rw-r--r-- | usr.sbin/jls/jls.8 | 142 | ||||
| -rw-r--r-- | usr.sbin/jls/jls.c | 659 | 
4 files changed, 835 insertions, 0 deletions
| diff --git a/usr.sbin/jls/Makefile b/usr.sbin/jls/Makefile new file mode 100644 index 000000000000..ee9665356c79 --- /dev/null +++ b/usr.sbin/jls/Makefile @@ -0,0 +1,16 @@ +.include <src.opts.mk> + +PROG=	jls +MAN=	jls.8 +LIBADD=	jail xo + +PACKAGE=jail + +.if ${MK_INET6_SUPPORT} != "no" +CFLAGS+= -DINET6 +.endif +.if ${MK_INET_SUPPORT} != "no" +CFLAGS+= -DINET +.endif + +.include <bsd.prog.mk> diff --git a/usr.sbin/jls/Makefile.depend b/usr.sbin/jls/Makefile.depend new file mode 100644 index 000000000000..845d2b50241d --- /dev/null +++ b/usr.sbin/jls/Makefile.depend @@ -0,0 +1,18 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ +	include \ +	include/arpa \ +	include/xlocale \ +	lib/${CSU_DIR} \ +	lib/libc \ +	lib/libcompiler_rt \ +	lib/libjail \ +	lib/libxo/libxo \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/jls/jls.8 b/usr.sbin/jls/jls.8 new file mode 100644 index 000000000000..715033082963 --- /dev/null +++ b/usr.sbin/jls/jls.8 @@ -0,0 +1,142 @@ +.\" +.\" Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd July 25, 2025 +.Dt JLS 8 +.Os +.Sh NAME +.Nm jls +.Nd "list jails" +.Sh SYNOPSIS +.Nm +.Op Fl -libxo +.Op Fl dhNnqsv +.Op Fl j Ar jail +.Op Ar parameter ... +.Nm +.Fl c +.Op Fl d +.Fl j Ar jail +.Sh DESCRIPTION +The +.Nm +utility lists all active jails, or the specified jail. +Each jail is represented by space-separated values of the listed +.Ar parameters , +one jail per line (unless the parameters themselves contain newlines). +The pseudo-parameter +.Va all +will show all available jail parameters. +A list of available parameters can be retrieved via +.Dq Nm sysctl Fl d Va security.jail.param . +See +.Xr jail 8 +for a description of some core parameters. +.Pp +If no +.Ar parameters +or any of the options +.Fl chns +are given, the following four columns will be printed: +jail identifier (jid), IP address (ip4.addr), hostname (host.hostname), +and path (path). +.Pp +When the +.Fl c +option is used, +.Nm +will not emit any output except for usage errors. +This mode is intended solely to check for a single jail's existence, and it does +not accept any +.Ar parameter +or print-option flags. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl -libxo +Generate output via +.Xr libxo 3 +in a selection of different human and machine readable formats. +See +.Xr xo_options 7 +for details on command line arguments. +.It Fl c +Only check for the jail's existence. +.It Fl d +List +.Va dying +as well as active jails. +.It Fl h +Print a header line containing the parameters listed. +If no parameters are given on the command line, +.Va all +is assumed. +.It Fl N +In the standard display mode, print each jail's name instead of its +numeric ID. +If the jail does not have a name, the numeric ID is printed instead. +.It Fl n +Print parameters in +.Dq name=value +format, where each parameter is preceded by its name. +If no parameters are given on the command line, +.Va all +is assumed. +.It Fl q +Put quotes around parameters if they contain spaces or quotes, or are +the empty string. +.It Fl s +Print parameters suitable for passing to +.Xr jail 8 , +skipping read-only and unused parameters. +Implies +.Fl nq . +.It Fl v +Extend the standard display with a multiple-line summary per jail, +containing the following parameters: +jail identifier (jid), hostname (host.hostname), path (path), +jail name (name), jail state (dying), cpuset ID (cpuset), +IP address(es) (ip4.addr and ip6.addr). +.It Fl j Ar jail +The jid or name of the +.Ar jail +to list. +Without this option, all active jails will be listed. +.El +.Sh SEE ALSO +.Xr jail_get 2 , +.Xr libxo 3 , +.Xr xo_options 7 , +.Xr jail 8 , +.Xr jexec 8 +.Sh HISTORY +The +.Nm +utility was added in +.Fx 5.1 . +Extensible jail parameters were introduced in +.Fx 8.0 . +libxo support was added in +.Fx 11.0 . diff --git a/usr.sbin/jls/jls.c b/usr.sbin/jls/jls.c new file mode 100644 index 000000000000..4f697a5bb382 --- /dev/null +++ b/usr.sbin/jls/jls.c @@ -0,0 +1,659 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org> + * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org> + * Copyright (c) 2009 James Gritton <jamie@FreeBSD.org> + * Copyright (c) 2015 Emmanuel Vadot <manu@bocal.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/jail.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <jail.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libxo/xo.h> + +#define	JP_USER		0x01000000 +#define	JP_OPT		0x02000000 + +#define JLS_XO_VERSION	"2" + +#define	PRINT_DEFAULT	0x01 +#define	PRINT_HEADER	0x02 +#define	PRINT_NAMEVAL	0x04 +#define	PRINT_QUOTED	0x08 +#define	PRINT_SKIP	0x10 +#define	PRINT_VERBOSE	0x20 +#define	PRINT_JAIL_NAME	0x40 +#define	PRINT_EXISTS	0x80 + +static struct jailparam *params; +static int *param_parent; +static int nparams; +#ifdef INET6 +static int ip6_ok; +#endif +#ifdef INET +static int ip4_ok; +#endif + +static int add_param(const char *name, void *value, size_t valuelen, +		struct jailparam *source, unsigned flags); +static int sort_param(const void *a, const void *b); +static char *noname(const char *name); +static char *nononame(const char *name); +static int print_jail(int pflags, int jflags); +static int special_print(int pflags, struct jailparam *param); +static void quoted_print(int pflags, char *name, char *value); +static void emit_ip_addr_list(int af_family, const char *list_name, +		struct jailparam *param); + +static void +usage(void) +{ +	xo_errx(1, +	    "usage: jls [-dhNnqv] [-j jail] [param ...]\n" +	    "            jls -c [-d] -j jail"); +} + +int +main(int argc, char **argv) +{ +	char *dot, *ep, *jname, *pname; +	int c, i, jflags, jid, lastjid, pflags, spc; + +	argc = xo_parse_args(argc, argv); +	if (argc < 0) +		exit(1); + +        xo_set_version(JLS_XO_VERSION); +	jname = NULL; +	pflags = jflags = jid = 0; +	while ((c = getopt(argc, argv, "acdj:hNnqsv")) >= 0) +		switch (c) { +		case 'a': +		case 'd': +			jflags |= JAIL_DYING; +			break; +		case 'c': +			pflags |= PRINT_EXISTS; +			break; +		case 'j': +			jid = strtoul(optarg, &ep, 10); +			if (!jid || *ep) { +				jid = 0; +				jname = optarg; +			} +			break; +		case 'h': +			pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) | +			    PRINT_HEADER; +			break; +		case 'N': +			pflags |= PRINT_JAIL_NAME; +			break; +		case 'n': +			pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL; +			break; +		case 'q': +			pflags |= PRINT_QUOTED; +			break; +		case 's': +			pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) | +			    PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP; +			break; +		case 'v': +			pflags = (pflags & +			    ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) | +			    PRINT_VERBOSE; +			break; +		default: +			usage(); +		} + +#ifdef INET6 +	ip6_ok = feature_present("inet6"); +#endif +#ifdef INET +	ip4_ok = feature_present("inet"); +#endif + +	argc -= optind; +	argv += optind; + +	/* Add the parameters to print. */ +	if ((pflags & PRINT_EXISTS) != 0) { +		if ((pflags & ~PRINT_EXISTS) != 0) { +			xo_warnx("-c is incompatible with other print options"); +			usage(); +		} else if (argc != 0) { +			xo_warnx("-c does not accept non-option arguments"); +			usage(); +		} else if (jid == 0 && jname == NULL) { +			xo_warnx("-j jail to check must be provided for -c"); +			usage(); +		} + +		/* +		 * Force libxo to be silent, as well -- we're only wanting our +		 * exit status. +		 */ +		xo_set_style(NULL, XO_STYLE_TEXT); +	} else if (argc == 0) { +		if (pflags & (PRINT_HEADER | PRINT_NAMEVAL)) +			add_param("all", NULL, (size_t)0, NULL, JP_USER); +		else if (pflags & PRINT_VERBOSE) { +			add_param("jid", NULL, (size_t)0, NULL, JP_USER); +			add_param("host.hostname", NULL, (size_t)0, NULL, +			    JP_USER); +			add_param("path", NULL, (size_t)0, NULL, JP_USER); +			add_param("name", NULL, (size_t)0, NULL, JP_USER); +			add_param("dying", NULL, (size_t)0, NULL, JP_USER); +			add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER); +#ifdef INET +			if (ip4_ok) +				add_param("ip4.addr", NULL, (size_t)0, NULL, +				    JP_USER); +#endif +#ifdef INET6 +			if (ip6_ok) +				add_param("ip6.addr", NULL, (size_t)0, NULL, +				    JP_USER | JP_OPT); +#endif +		} else { +			pflags |= PRINT_DEFAULT; +			if (pflags & PRINT_JAIL_NAME) +				add_param("name", NULL, (size_t)0, NULL, JP_USER); +			else +				add_param("jid", NULL, (size_t)0, NULL, JP_USER); +#ifdef INET +			if (ip4_ok) +				add_param("ip4.addr", NULL, (size_t)0, NULL, +				    JP_USER); +#endif +			add_param("host.hostname", NULL, (size_t)0, NULL, +			    JP_USER); +			add_param("path", NULL, (size_t)0, NULL, JP_USER); +		} +	} else { +		pflags &= ~PRINT_VERBOSE; +		for (i = 0; i < argc; i++) +			add_param(argv[i], NULL, (size_t)0, NULL, JP_USER); +	} + +	if (pflags & PRINT_SKIP) { +		/* Check for parameters with jailsys parents. */ +		for (i = 0; i < nparams; i++) { +			if ((params[i].jp_flags & JP_USER) && +			    (dot = strchr(params[i].jp_name, '.'))) { +				pname = alloca((dot - params[i].jp_name) + 1); +				strlcpy(pname, params[i].jp_name, +				    (dot - params[i].jp_name) + 1); +				param_parent[i] = add_param(pname, +				    NULL, (size_t)0, NULL, JP_OPT); +			} +		} +	} + +	/* Add the index key parameters. */ +	if (jid != 0) +		add_param("jid", &jid, sizeof(jid), NULL, 0); +	else if (jname != NULL) +		add_param("name", jname, strlen(jname), NULL, 0); +	else +		add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0); + +	/* Print a header line if requested. */ +	if (pflags & PRINT_VERBOSE) { +		xo_emit("{T:/%3s}{T:JID}{P:  }{T:Hostname}{Pd:/%22s}{T:Path}\n", +		        "", ""); +		xo_emit("{P:/%8s}{T:Name}{Pd:/%26s}{T:State}\n", "", ""); +		xo_emit("{P:/%8s}{T:CPUSetID}\n", ""); +		xo_emit("{P:/%8s}{T:IP Address(es)}\n", ""); +	} +	else if (pflags & PRINT_DEFAULT) +		if (pflags & PRINT_JAIL_NAME) +			xo_emit("{P: }{T:JID/%-15s}{P: }{T:IP Address/%-15s}" +			        "{P: }{T:Hostname/%-29s}{P: }{T:Path}\n"); +		else +			xo_emit("{T:JID/%6s}{P:  }{T:IP Address}{P:/%6s}" +			        "{T:Hostname}{P:/%22s}{T:Path}\n", "", ""); +	else if (pflags & PRINT_HEADER) { +		for (i = spc = 0; i < nparams; i++) +			if (params[i].jp_flags & JP_USER) { +				if (spc) +					xo_emit("{P: }"); +				else +					spc = 1; +				xo_emit(params[i].jp_name); +			} +		xo_emit("{P:\n}"); +	} + +	xo_open_container("jail-information"); +	xo_open_list("jail"); +	/* Fetch the jail(s) and print the parameters. */ +	if (jid != 0 || jname != NULL) { +		if (print_jail(pflags, jflags) < 0) { +			/* +			 * We omit errors from existential issues if we're just +			 * doing a -c check that the jail exists. +			 */ +			if (pflags & PRINT_EXISTS) +				exit(1); +			xo_errx(1, "%s", jail_errmsg); +		} +	} else { +		assert((pflags & PRINT_EXISTS) == 0); +		for (lastjid = 0; +		     (lastjid = print_jail(pflags, jflags)) >= 0; ) +			; +		if (errno != 0 && errno != ENOENT) +			xo_errx(1, "%s", jail_errmsg); +	} +	xo_close_list("jail"); +	xo_close_container("jail-information"); +	if (xo_finish() < 0) +		xo_err(1, "stdout"); +	exit(0); +} + +static int +add_param(const char *name, void *value, size_t valuelen, +    struct jailparam *source, unsigned flags) +{ +	struct jailparam *param, *tparams; +	int i, tnparams; + +	static int paramlistsize; + +	/* The pseudo-parameter "all" scans the list of available parameters. */ +	if (!strcmp(name, "all")) { +		tnparams = jailparam_all(&tparams); +		if (tnparams < 0) +			xo_errx(1, "%s", jail_errmsg); +		qsort(tparams, (size_t)tnparams, sizeof(struct jailparam), +		    sort_param); +		for (i = 0; i < tnparams; i++) +			add_param(tparams[i].jp_name, NULL, (size_t)0, +			    tparams + i, flags); +		free(tparams); +		return -1; +	} + +	/* Check for repeat parameters. */ +	for (i = 0; i < nparams; i++) +		if (!strcmp(name, params[i].jp_name)) { +			if (value != NULL && jailparam_import_raw(params + i, +			    value, valuelen) < 0) +				xo_errx(1, "%s", jail_errmsg); +			params[i].jp_flags |= flags; +			if (source != NULL) +				jailparam_free(source, 1); +			return i; +		} + +	/* Make sure there is room for the new param record. */ +	if (!nparams) { +		paramlistsize = 32; +		params = malloc(paramlistsize * sizeof(*params)); +		param_parent = malloc(paramlistsize * sizeof(*param_parent)); +		if (params == NULL || param_parent == NULL) +			xo_err(1, "malloc"); +	} else if (nparams >= paramlistsize) { +		paramlistsize *= 2; +		params = realloc(params, paramlistsize * sizeof(*params)); +		param_parent = realloc(param_parent, +		    paramlistsize * sizeof(*param_parent)); +		if (params == NULL || param_parent == NULL) +			xo_err(1, "realloc"); +	} + +	/* Look up the parameter. */ +	param_parent[nparams] = -1; +	param = params + nparams++; +	if (source != NULL) { +		*param = *source; +		param->jp_flags |= flags; +		return param - params; +	} +	if (jailparam_init(param, name) < 0 || +	    (value != NULL ? jailparam_import_raw(param, value, valuelen) +	     : jailparam_import(param, value)) < 0) { +		if (flags & JP_OPT) { +			nparams--; +			return (-1); +		} +		xo_errx(1, "%s", jail_errmsg); +	} +	param->jp_flags |= flags; +	return param - params; +} + +static int +sort_param(const void *a, const void *b) +{ +	const struct jailparam *parama, *paramb; +	char *ap, *bp; + +	/* Put top-level parameters first. */ +	parama = a; +	paramb = b; +	ap = strchr(parama->jp_name, '.'); +	bp = strchr(paramb->jp_name, '.'); +	if (ap && !bp) +		return (1); +	if (bp && !ap) +		return (-1); +	return (strcmp(parama->jp_name, paramb->jp_name)); +} + +static char * +noname(const char *name) +{ +	char *nname, *p; + +	nname = malloc(strlen(name) + 3); +	if (nname == NULL) +		xo_err(1, "malloc"); +	p = strrchr(name, '.'); +	if (p != NULL) +		sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1); +	else +		sprintf(nname, "no%s", name); +	return nname; +} + +static char * +nononame(const char *name) +{ +	char *nname, *p; + +	p = strrchr(name, '.'); +	if (strncmp(p ? p + 1 : name, "no", 2)) +		return NULL; +	nname = malloc(strlen(name) - 1); +	if (nname == NULL) +		xo_err(1, "malloc"); +	if (p != NULL) +		sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3); +	else +		strcpy(nname, name + 2); +	return nname; +} + +static int +print_jail(int pflags, int jflags) +{ +	char *nname, *xo_nname; +	char **param_values; +	int i, jid, spc; +#if (defined INET || defined INET6) +	int n; +#endif + +	jid = jailparam_get(params, nparams, jflags); +	if (jid < 0) +		return jid; +	else if (pflags & PRINT_EXISTS) +		return 0; + +	xo_open_instance("jail"); + +	if (pflags & PRINT_VERBOSE) { +		xo_emit("{:jid/%6d}{P:  }{:hostname/%-29.29s/%s}{P: }" +		    "{:path/%.74s/%s}\n", +		    *(int *)params[0].jp_value, +		    (char *)params[1].jp_value, +		    (char *)params[2].jp_value); +		xo_emit("{P:        }{:name/%-29.29s/%s}{P: }{:state/%.74s}\n", +		    (char *)params[3].jp_value, +		    *(int *)params[4].jp_value ? "DYING" : "ACTIVE"); +		xo_emit("{P:        }{:cpusetid/%d}\n", *(int *)params[5].jp_value); +#if (defined INET || defined INET6) +		n = 6; +#endif +#ifdef INET +		if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) { +			emit_ip_addr_list(AF_INET, "ipv4_addrs", params + n); +			n++; +		} +#endif +#ifdef INET6 +		if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) { +			emit_ip_addr_list(AF_INET6, "ipv6_addrs", params + n); +			n++; +		} +#endif +	} else if (pflags & PRINT_DEFAULT) { +		if (pflags & PRINT_JAIL_NAME) +			xo_emit("{P: }{:name/%-15s/%s}{P: }", +			    (char *)params[0].jp_value); +		else +			xo_emit("{:jid/%6d}{P:  }", *(int *)params[0].jp_value); +		xo_emit("{:ipv4/%-15.15s/%s}{P: }{:hostname/%-29.29s/%s}{P: }{:path/%.74s/%s}\n", +#ifdef INET +		    (!ip4_ok || params[1].jp_valuelen == 0) ? "" +		    : inet_ntoa(*(struct in_addr *)params[1].jp_value), +		    (char *)params[2-!ip4_ok].jp_value, +		    (char *)params[3-!ip4_ok].jp_value); +#else +		    "-", +		    (char *)params[1].jp_value, +		    (char *)params[2].jp_value); +#endif +	} else { +		param_values = alloca(nparams * sizeof(*param_values)); +		for (i = 0; i < nparams; i++) { +			if (!(params[i].jp_flags & JP_USER)) +				continue; +			param_values[i] = jailparam_export(params + i); +			if (param_values[i] == NULL) +				xo_errx(1, "%s", jail_errmsg); +		} +		for (i = spc = 0; i < nparams; i++) { +			if (!(params[i].jp_flags & JP_USER)) +				continue; +			if ((pflags & PRINT_SKIP) && +			    !(params[i].jp_flags & JP_KEYVALUE) && +			    ((!(params[i].jp_ctltype & +				(CTLFLAG_WR | CTLFLAG_TUN))) || +			     (param_parent[i] >= 0 && +			      *(int *)params[param_parent[i]].jp_value != +			      JAIL_SYS_NEW))) +				continue; +			if (spc) +				xo_emit("{P: }"); +			else +				spc = 1; +			if ((params[i].jp_flags & JP_KEYVALUE) && +			    params[i].jp_valuelen == 0) { +				/* Communicate back a missing key. */ +				if (pflags & PRINT_NAMEVAL) +					xo_emit("{d:%s}", params[i].jp_name); +				continue; +			} +			if (pflags & PRINT_NAMEVAL) { +				/* +				 * Generally "name=value", but for booleans +				 * either "name" or "noname". +				 */ +				if (params[i].jp_flags & +				    (JP_BOOL | JP_NOBOOL)) { +					if (*(int *)params[i].jp_value) { +						asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name); +						xo_emit(xo_nname); +						xo_emit("{d:/%s}", params[i].jp_name); +					} +					else { +						nname = (params[i].jp_flags & +						    JP_NOBOOL) ? +						    nononame(params[i].jp_name) +						    : noname(params[i].jp_name); +						if (params[i].jp_flags & JP_NOBOOL) { +							asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name); +							xo_emit(xo_nname); +						} else { +							asprintf(&xo_nname, "{en:%s/false}", params[i].jp_name); +							xo_emit(xo_nname); +						} +						xo_emit("{d:/%s}", nname); +						free(nname); +					} +					free(xo_nname); +					continue; +				} +				xo_emit("{d:%s}=", params[i].jp_name); +			} +			if (!special_print(pflags, params + i)) +				quoted_print(pflags, params[i].jp_name, param_values[i]); +		} +		xo_emit("{P:\n}"); +		for (i = 0; i < nparams; i++) +			if (params[i].jp_flags & JP_USER) +				free(param_values[i]); +	} + +	xo_close_instance("jail"); +	return (jid); +} + +static void +quoted_print(int pflags, char *name, char *value) +{ +	int qc; +	char *p = value; + +	/* An empty string needs quoting. */ +	if (!*p) { +		xo_emit("{ea:/%s}{da:/\"\"}", name, value, name); +		return; +	} + +	/* +	 * The value will be surrounded by quotes if it contains +	 * whitespace or quotes. +	 */ +	if (strchr(p, '\'')) +		qc = '"'; +	else if (strchr(p, '"')) +		qc = '\''; +	else { +		qc = 0; +		for (; *p; ++p) +			if (isspace(*p)) { +				qc = '"'; +				break; +			} +	} + +	if (qc && pflags & PRINT_QUOTED) +		xo_emit("{P:/%c}", qc); + +	xo_emit("{a:/%s}", name, value); + +	if (qc && pflags & PRINT_QUOTED) +		xo_emit("{P:/%c}", qc); +} + +static int +special_print(int pflags, struct jailparam *param) +{ +	int ip_as_list; + +	switch (xo_get_style(NULL)) { +	case XO_STYLE_JSON: +	case XO_STYLE_XML: +		ip_as_list = 1; +		break; +	default: +		ip_as_list = 0; +	} + +	if (!ip_as_list && param->jp_valuelen == 0) { +		if (pflags & PRINT_QUOTED) +			xo_emit("{P:\"\"}"); +		else if (!(pflags & PRINT_NAMEVAL)) +			xo_emit("{P:-}"); +	} else if (ip_as_list && !strcmp(param->jp_name, "ip4.addr")) { +		emit_ip_addr_list(AF_INET, param->jp_name, param); +	} else if (ip_as_list && !strcmp(param->jp_name, "ip6.addr")) { +		emit_ip_addr_list(AF_INET6, param->jp_name, param); +	} else { +		return 0; +	} + +	return 1; +} + +static void +emit_ip_addr_list(int af_family, const char *list_name, struct jailparam *param) +{ +	char ipbuf[INET6_ADDRSTRLEN]; +	size_t addr_len; +	const char *emit_str; +	int ai, count; + +	switch (af_family) { +	case AF_INET: +		addr_len = sizeof(struct in_addr); +		emit_str = "{P:        }{ql:ipv4_addr}{P:\n}"; +		break; +	case AF_INET6: +		addr_len = sizeof(struct in6_addr); +		emit_str = "{P:        }{ql:ipv6_addr}{P:\n}"; +		break; +	default: +		xo_err(1, "unsupported af_family"); +		return; +	} + +	count = param->jp_valuelen / addr_len; + +	xo_open_list(list_name); +	for (ai = 0; ai < count; ai++) { +		if (inet_ntop(af_family, +		    ((uint8_t *)param->jp_value) + addr_len * ai, +		    ipbuf, sizeof(ipbuf)) == NULL) { +			xo_err(1, "inet_ntop"); +		} else { +			xo_emit(emit_str, ipbuf); +		} +	} +	xo_close_list(list_name); +} | 
