1191668Sjamie/*-
2191668Sjamie * Copyright (c) 1999 Poul-Henning Kamp.
3234712Sjamie * Copyright (c) 2009-2012 James Gritton
4191668Sjamie * All rights reserved.
5191668Sjamie *
6191668Sjamie * Redistribution and use in source and binary forms, with or without
7191668Sjamie * modification, are permitted provided that the following conditions
8191668Sjamie * are met:
9191668Sjamie * 1. Redistributions of source code must retain the above copyright
10191668Sjamie *    notice, this list of conditions and the following disclaimer.
11191668Sjamie * 2. Redistributions in binary form must reproduce the above copyright
12191668Sjamie *    notice, this list of conditions and the following disclaimer in the
13191668Sjamie *    documentation and/or other materials provided with the distribution.
14191668Sjamie *
15191668Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16191668Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17191668Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18191668Sjamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19191668Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20191668Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21191668Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22191668Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23191668Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24191668Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25191668Sjamie * SUCH DAMAGE.
2646432Sphk */
2746432Sphk
28117280Scharnier#include <sys/cdefs.h>
29117280Scharnier__FBSDID("$FreeBSD: releng/10.3/usr.sbin/jail/jail.c 278484 2015-02-10 01:05:51Z jamie $");
30117280Scharnier
31234712Sjamie#include <sys/types.h>
32234712Sjamie#include <sys/stat.h>
33185435Sbz#include <sys/socket.h>
34158428Smatteo#include <sys/sysctl.h>
3578723Sdd
36192896Sjamie#include <arpa/inet.h>
3746155Sphk#include <netinet/in.h>
3846155Sphk
3978723Sdd#include <err.h>
40129848Smaxim#include <errno.h>
41234712Sjamie#include <stdarg.h>
4278723Sdd#include <stdio.h>
4378723Sdd#include <stdlib.h>
4478723Sdd#include <string.h>
4578723Sdd#include <unistd.h>
4678723Sdd
47234712Sjamie#include "jailp.h"
48192896Sjamie
49234712Sjamie#define JP_RDTUN(jp)	(((jp)->jp_ctltype & CTLFLAG_RDTUN) == CTLFLAG_RDTUN)
50185435Sbz
51234712Sjamiestruct permspec {
52234712Sjamie	const char	*name;
53234712Sjamie	enum intparam	ipnum;
54234712Sjamie	int		rev;
55234712Sjamie};
56234712Sjamie
57234712Sjamieconst char *cfname;
58236198Sjamieint iflag;
59234712Sjamieint note_remove;
60234712Sjamieint verbose;
61234712Sjamie
62234712Sjamiestatic void clear_persist(struct cfjail *j);
63234712Sjamiestatic int update_jail(struct cfjail *j);
64234712Sjamiestatic int rdtun_params(struct cfjail *j, int dofail);
65234712Sjamiestatic void running_jid(struct cfjail *j, int dflag);
66234712Sjamiestatic void jail_quoted_warnx(const struct cfjail *j, const char *name_msg,
67234712Sjamie    const char *noname_msg);
68234712Sjamiestatic int jailparam_set_note(const struct cfjail *j, struct jailparam *jp,
69234712Sjamie    unsigned njp, int flags);
70234712Sjamiestatic void print_jail(FILE *fp, struct cfjail *j, int oldcl);
71234712Sjamiestatic void print_param(FILE *fp, const struct cfparam *p, int sep, int doname);
72192896Sjamiestatic void quoted_print(FILE *fp, char *str);
73192896Sjamiestatic void usage(void);
74185435Sbz
75234712Sjamiestatic struct permspec perm_sysctl[] = {
76234712Sjamie    { "security.jail.set_hostname_allowed", KP_ALLOW_SET_HOSTNAME, 0 },
77234712Sjamie    { "security.jail.sysvipc_allowed", KP_ALLOW_SYSVIPC, 0 },
78234712Sjamie    { "security.jail.allow_raw_sockets", KP_ALLOW_RAW_SOCKETS, 0 },
79234712Sjamie    { "security.jail.chflags_allowed", KP_ALLOW_CHFLAGS, 0 },
80234712Sjamie    { "security.jail.mount_allowed", KP_ALLOW_MOUNT, 0 },
81234712Sjamie    { "security.jail.socket_unixiproute_only", KP_ALLOW_SOCKET_AF, 1 },
82193929Sjamie};
83193929Sjamie
84234712Sjamiestatic const enum intparam startcommands[] = {
85234988Sjamie    IP__NULL,
86234712Sjamie#ifdef INET
87234712Sjamie    IP__IP4_IFADDR,
88234712Sjamie#endif
89234712Sjamie#ifdef INET6
90234712Sjamie    IP__IP6_IFADDR,
91234712Sjamie#endif
92234712Sjamie    IP_MOUNT,
93234712Sjamie    IP__MOUNT_FROM_FSTAB,
94234712Sjamie    IP_MOUNT_DEVFS,
95256387Shrs    IP_MOUNT_FDESCFS,
96278484Sjamie    IP_MOUNT_PROCFS,
97234712Sjamie    IP_EXEC_PRESTART,
98234712Sjamie    IP__OP,
99234712Sjamie    IP_VNET_INTERFACE,
100234712Sjamie    IP_EXEC_START,
101234712Sjamie    IP_COMMAND,
102234712Sjamie    IP_EXEC_POSTSTART,
103234988Sjamie    IP__NULL
104234712Sjamie};
105192896Sjamie
106234712Sjamiestatic const enum intparam stopcommands[] = {
107234988Sjamie    IP__NULL,
108234712Sjamie    IP_EXEC_PRESTOP,
109234712Sjamie    IP_EXEC_STOP,
110234712Sjamie    IP_STOP_TIMEOUT,
111234712Sjamie    IP__OP,
112234712Sjamie    IP_EXEC_POSTSTOP,
113278484Sjamie    IP_MOUNT_PROCFS,
114256387Shrs    IP_MOUNT_FDESCFS,
115234712Sjamie    IP_MOUNT_DEVFS,
116234712Sjamie    IP__MOUNT_FROM_FSTAB,
117234712Sjamie    IP_MOUNT,
118234712Sjamie#ifdef INET6
119234712Sjamie    IP__IP6_IFADDR,
120234712Sjamie#endif
121234712Sjamie#ifdef INET
122234712Sjamie    IP__IP4_IFADDR,
123234712Sjamie#endif
124234988Sjamie    IP__NULL
125234712Sjamie};
126129848Smaxim
12746155Sphkint
12846155Sphkmain(int argc, char **argv)
12946155Sphk{
130234712Sjamie	struct stat st;
131234712Sjamie	FILE *jfp;
132234712Sjamie	struct cfjail *j;
133234712Sjamie	char *JidFile;
134193929Sjamie	size_t sysvallen;
135234712Sjamie	unsigned op, pi;
136234712Sjamie	int ch, docf, error, i, oldcl, sysval;
137236198Sjamie	int dflag, Rflag;
138194869Sjamie	char enforce_statfs[4];
139234712Sjamie#if defined(INET) || defined(INET6)
140234712Sjamie	char *cs, *ncs;
141234712Sjamie#endif
142234712Sjamie#if defined(INET) && defined(INET6)
143234712Sjamie	struct in6_addr addr6;
144234712Sjamie#endif
14546155Sphk
146234712Sjamie	op = 0;
147236198Sjamie	dflag = Rflag = 0;
148234712Sjamie	docf = 1;
149234712Sjamie	cfname = CONF_FILE;
150234712Sjamie	JidFile = NULL;
151112705Smaxim
152237697Smaxim	while ((ch = getopt(argc, argv, "cdf:hiJ:lmn:p:qrRs:u:U:v")) != -1) {
153112705Smaxim		switch (ch) {
154234712Sjamie		case 'c':
155234712Sjamie			op |= JF_START;
156234712Sjamie			break;
157192896Sjamie		case 'd':
158234712Sjamie			dflag = 1;
159192896Sjamie			break;
160234712Sjamie		case 'f':
161234712Sjamie			cfname = optarg;
162234712Sjamie			break;
163185435Sbz		case 'h':
164234712Sjamie#if defined(INET) || defined(INET6)
165234712Sjamie			add_param(NULL, NULL, IP_IP_HOSTNAME, NULL);
166234712Sjamie#endif
167234712Sjamie			docf = 0;
168185435Sbz			break;
169113277Smike		case 'i':
170113277Smike			iflag = 1;
171234712Sjamie			verbose = -1;
172113277Smike			break;
173153056Sphilip		case 'J':
174153056Sphilip			JidFile = optarg;
175153056Sphilip			break;
176234712Sjamie		case 'l':
177234712Sjamie			add_param(NULL, NULL, IP_EXEC_CLEAN, NULL);
178234712Sjamie			docf = 0;
179234712Sjamie			break;
180234712Sjamie		case 'm':
181234712Sjamie			op |= JF_SET;
182234712Sjamie			break;
183185435Sbz		case 'n':
184234712Sjamie			add_param(NULL, NULL, KP_NAME, optarg);
185234712Sjamie			docf = 0;
186185435Sbz			break;
187234712Sjamie		case 'p':
188234712Sjamie			paralimit = strtol(optarg, NULL, 10);
189234712Sjamie			if (paralimit == 0)
190234712Sjamie				paralimit = -1;
191234712Sjamie			break;
192234712Sjamie		case 'q':
193234712Sjamie			verbose = -1;
194234712Sjamie			break;
195234712Sjamie		case 'r':
196234712Sjamie			op |= JF_STOP;
197234712Sjamie			break;
198234712Sjamie		case 'R':
199234712Sjamie			op |= JF_STOP;
200234712Sjamie			Rflag = 1;
201234712Sjamie			break;
202158428Smatteo		case 's':
203234712Sjamie			add_param(NULL, NULL, KP_SECURELEVEL, optarg);
204234712Sjamie			docf = 0;
205158428Smatteo			break;
206112705Smaxim		case 'u':
207234712Sjamie			add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg);
208234712Sjamie			add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER, NULL);
209234712Sjamie			docf = 0;
210112705Smaxim			break;
211129848Smaxim		case 'U':
212234712Sjamie			add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg);
213234712Sjamie			add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER,
214234712Sjamie			    "false");
215234712Sjamie			docf = 0;
216129848Smaxim			break;
217234712Sjamie		case 'v':
218234712Sjamie			verbose = 1;
219133743Smaxim			break;
220112705Smaxim		default:
221112705Smaxim			usage();
222112705Smaxim		}
223113277Smike	}
224112705Smaxim	argc -= optind;
225112705Smaxim	argv += optind;
226234712Sjamie
227234712Sjamie	/* Find out which of the four command line styles this is. */
228234712Sjamie	oldcl = 0;
229234712Sjamie	if (!op) {
230234712Sjamie		/* Old-style command line with four fixed parameters */
231234712Sjamie		if (argc < 4 || argv[0][0] != '/')
232192896Sjamie			usage();
233234712Sjamie		op = JF_START;
234234712Sjamie		docf = 0;
235234712Sjamie		oldcl = 1;
236234712Sjamie		add_param(NULL, NULL, KP_PATH, argv[0]);
237234712Sjamie		add_param(NULL, NULL, KP_HOST_HOSTNAME, argv[1]);
238234712Sjamie#if defined(INET) || defined(INET6)
239234712Sjamie		if (argv[2][0] != '\0') {
240234712Sjamie			for (cs = argv[2];; cs = ncs + 1) {
241234712Sjamie				ncs = strchr(cs, ',');
242234712Sjamie				if (ncs)
243234712Sjamie					*ncs = '\0';
244234712Sjamie				add_param(NULL, NULL,
245234712Sjamie#if defined(INET) && defined(INET6)
246234712Sjamie				    inet_pton(AF_INET6, cs, &addr6) == 1
247234712Sjamie				    ? KP_IP6_ADDR : KP_IP4_ADDR,
248234712Sjamie#elif defined(INET)
249234712Sjamie				    KP_IP4_ADDR,
250234712Sjamie#elif defined(INET6)
251234712Sjamie				    KP_IP6_ADDR,
252222465Sbz#endif
253234712Sjamie				    cs);
254234712Sjamie				if (!ncs)
255192896Sjamie					break;
256192896Sjamie			}
257192896Sjamie		}
258192896Sjamie#endif
259234712Sjamie		for (i = 3; i < argc; i++)
260234712Sjamie			add_param(NULL, NULL, IP_COMMAND, argv[i]);
261234712Sjamie		/* Emulate the defaults from security.jail.* sysctls. */
262193929Sjamie		sysvallen = sizeof(sysval);
263193929Sjamie		if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen,
264193929Sjamie		    NULL, 0) == 0 && sysval == 0) {
265193929Sjamie			for (pi = 0; pi < sizeof(perm_sysctl) /
266193929Sjamie			     sizeof(perm_sysctl[0]); pi++) {
267193929Sjamie				sysvallen = sizeof(sysval);
268234712Sjamie				if (sysctlbyname(perm_sysctl[pi].name,
269193929Sjamie				    &sysval, &sysvallen, NULL, 0) == 0)
270234712Sjamie					add_param(NULL, NULL,
271234712Sjamie					    perm_sysctl[pi].ipnum,
272234712Sjamie					    (sysval ? 1 : 0) ^
273234712Sjamie					    perm_sysctl[pi].rev
274234712Sjamie					    ? NULL : "false");
275193929Sjamie			}
276193929Sjamie			sysvallen = sizeof(sysval);
277193929Sjamie			if (sysctlbyname("security.jail.enforce_statfs",
278193929Sjamie			    &sysval, &sysvallen, NULL, 0) == 0) {
279193929Sjamie				snprintf(enforce_statfs,
280193929Sjamie				    sizeof(enforce_statfs), "%d", sysval);
281234712Sjamie				add_param(NULL, NULL, KP_ENFORCE_STATFS,
282234712Sjamie				    enforce_statfs);
283193929Sjamie			}
284193929Sjamie		}
285234712Sjamie	} else if (op == JF_STOP) {
286234712Sjamie		/* Jail remove, perhaps using the config file */
287234712Sjamie		if (!docf || argc == 0)
288234712Sjamie			usage();
289234712Sjamie		if (!Rflag)
290234712Sjamie			for (i = 0; i < argc; i++)
291234712Sjamie				if (strchr(argv[i], '='))
292234712Sjamie					usage();
293234712Sjamie		if ((docf = !Rflag &&
294234712Sjamie		     (!strcmp(cfname, "-") || stat(cfname, &st) == 0)))
295234712Sjamie			load_config();
296234712Sjamie		note_remove = docf || argc > 1 || wild_jail_name(argv[0]);
297234712Sjamie	} else if (argc > 1 || (argc == 1 && strchr(argv[0], '='))) {
298234712Sjamie		/* Single jail specified on the command line */
299234712Sjamie		if (Rflag)
300234712Sjamie			usage();
301234712Sjamie		docf = 0;
302234712Sjamie		for (i = 0; i < argc; i++) {
303234712Sjamie			if (!strncmp(argv[i], "command", 7) &&
304234712Sjamie			    (argv[i][7] == '\0' || argv[i][7] == '=')) {
305234712Sjamie				if (argv[i][7]  == '=')
306234712Sjamie					add_param(NULL, NULL, IP_COMMAND,
307234712Sjamie					    argv[i] + 8);
308234712Sjamie				for (i++; i < argc; i++)
309234712Sjamie					add_param(NULL, NULL, IP_COMMAND,
310234712Sjamie					    argv[i]);
311234712Sjamie			}
312239602Sjamie#ifdef INET
313239602Sjamie			else if (!strncmp(argv[i], "ip4.addr=", 9)) {
314239602Sjamie				for (cs = argv[i] + 9;; cs = ncs + 1) {
315239602Sjamie					ncs = strchr(cs, ',');
316239602Sjamie					if (ncs)
317239602Sjamie						*ncs = '\0';
318239602Sjamie					add_param(NULL, NULL, KP_IP4_ADDR, cs);
319239602Sjamie					if (!ncs)
320239602Sjamie						break;
321239602Sjamie				}
322239602Sjamie			}
323239602Sjamie#endif
324239602Sjamie#ifdef INET6
325239602Sjamie			else if (!strncmp(argv[i], "ip6.addr=", 9)) {
326239602Sjamie				for (cs = argv[i] + 9;; cs = ncs + 1) {
327239602Sjamie					ncs = strchr(cs, ',');
328239602Sjamie					if (ncs)
329239602Sjamie						*ncs = '\0';
330239602Sjamie					add_param(NULL, NULL, KP_IP6_ADDR, cs);
331239602Sjamie					if (!ncs)
332239602Sjamie						break;
333239602Sjamie				}
334239602Sjamie			}
335239602Sjamie#endif
336239602Sjamie			else
337239602Sjamie				add_param(NULL, NULL, 0, argv[i]);
338234712Sjamie		}
339234712Sjamie	} else {
340234712Sjamie		/* From the config file, perhaps with a specified jail */
341234712Sjamie		if (Rflag || !docf)
342234712Sjamie			usage();
343234712Sjamie		load_config();
344185435Sbz	}
345185435Sbz
346234712Sjamie	/* Find out which jails will be run. */
347234712Sjamie	dep_setup(docf);
348234712Sjamie	error = 0;
349234712Sjamie	if (op == JF_STOP) {
350234712Sjamie		for (i = 0; i < argc; i++)
351234712Sjamie			if (start_state(argv[i], docf, op, Rflag) < 0)
352234712Sjamie				error = 1;
353234712Sjamie	} else {
354234712Sjamie		if (start_state(argv[0], docf, op, 0) < 0)
355234712Sjamie			exit(1);
356153056Sphilip	}
357234712Sjamie
358234712Sjamie	jfp = NULL;
359234712Sjamie	if (JidFile != NULL) {
360234712Sjamie		jfp = fopen(JidFile, "w");
361234712Sjamie		if (jfp == NULL)
362234712Sjamie			err(1, "open %s", JidFile);
363234712Sjamie		setlinebuf(jfp);
364113804Smike	}
365234712Sjamie	setlinebuf(stdout);
366234712Sjamie
367234712Sjamie	/*
368234712Sjamie	 * The main loop: Get an available jail and perform the required
369234712Sjamie	 * operation on it.  When that is done, the jail may be finished,
370234712Sjamie	 * or it may go back for the next step.
371234712Sjamie	 */
372234712Sjamie	while ((j = next_jail()))
373234712Sjamie	{
374234712Sjamie		if (j->flags & JF_FAILED) {
375234712Sjamie			error = 1;
376234712Sjamie			if (j->comparam == NULL) {
377234712Sjamie				dep_done(j, 0);
378234712Sjamie				continue;
379234712Sjamie			}
380234712Sjamie		}
381234712Sjamie		if (!(j->flags & JF_PARAMS))
382234712Sjamie		{
383234712Sjamie			j->flags |= JF_PARAMS;
384234712Sjamie			if (dflag)
385234712Sjamie				add_param(j, NULL, IP_ALLOW_DYING, NULL);
386234712Sjamie			if (check_intparams(j) < 0)
387234712Sjamie				continue;
388234712Sjamie			if ((j->flags & (JF_START | JF_SET)) &&
389234712Sjamie			    import_params(j) < 0)
390234712Sjamie				continue;
391234712Sjamie		}
392234712Sjamie		if (!j->jid)
393234712Sjamie			running_jid(j,
394234712Sjamie			    (j->flags & (JF_SET | JF_DEPEND)) == JF_SET
395234712Sjamie			    ? dflag || bool_param(j->intparams[IP_ALLOW_DYING])
396234712Sjamie			    : 0);
397234712Sjamie		if (finish_command(j))
398234712Sjamie			continue;
399234712Sjamie
400234712Sjamie		switch (j->flags & JF_OP_MASK) {
401234712Sjamie			/*
402234712Sjamie			 * These operations just turn into a different op
403234712Sjamie			 * depending on the jail's current status.
404234712Sjamie			 */
405234712Sjamie		case JF_START_SET:
406234712Sjamie			j->flags = j->jid < 0 ? JF_START : JF_SET;
407234712Sjamie			break;
408234712Sjamie		case JF_SET_RESTART:
409234712Sjamie			if (j->jid < 0) {
410234712Sjamie				jail_quoted_warnx(j, "not found",
411234712Sjamie				    "no jail specified");
412234712Sjamie				failed(j);
413234712Sjamie				continue;
414234712Sjamie			}
415234712Sjamie			j->flags = rdtun_params(j, 0) ? JF_RESTART : JF_SET;
416234712Sjamie			if (j->flags == JF_RESTART)
417234712Sjamie				dep_reset(j);
418234712Sjamie			break;
419234712Sjamie		case JF_START_SET_RESTART:
420234712Sjamie			j->flags = j->jid < 0 ? JF_START
421234712Sjamie			    : rdtun_params(j, 0) ? JF_RESTART : JF_SET;
422234712Sjamie			if (j->flags == JF_RESTART)
423234712Sjamie				dep_reset(j);
424234712Sjamie		}
425234712Sjamie
426234712Sjamie		switch (j->flags & JF_OP_MASK) {
427234712Sjamie		case JF_START:
428234712Sjamie			if (j->comparam == NULL) {
429234712Sjamie				if (j->jid > 0 &&
430234712Sjamie				    !(j->flags & (JF_DEPEND | JF_WILD))) {
431234712Sjamie					jail_quoted_warnx(j, "already exists",
432234712Sjamie					    NULL);
433234712Sjamie					failed(j);
434234712Sjamie					continue;
435192896Sjamie				}
436234712Sjamie				if (dep_check(j))
437234712Sjamie					continue;
438234712Sjamie				if (j->jid > 0)
439234712Sjamie					goto jail_create_done;
440234712Sjamie				j->comparam = startcommands;
441234712Sjamie				j->comstring = NULL;
442234712Sjamie			}
443234712Sjamie			if (next_command(j))
444234712Sjamie				continue;
445234712Sjamie		jail_create_done:
446234712Sjamie			clear_persist(j);
447234712Sjamie			if (jfp != NULL)
448234712Sjamie				print_jail(jfp, j, oldcl);
449234712Sjamie			dep_done(j, 0);
450234712Sjamie			break;
451234712Sjamie
452234712Sjamie		case JF_SET:
453234712Sjamie			if (j->jid < 0 && !(j->flags & JF_DEPEND)) {
454234712Sjamie				jail_quoted_warnx(j, "not found",
455234712Sjamie				    "no jail specified");
456234712Sjamie				failed(j);
457234712Sjamie				continue;
458234712Sjamie			}
459234712Sjamie			if (dep_check(j))
460234712Sjamie				continue;
461234712Sjamie			if (!(j->flags & JF_DEPEND)) {
462234712Sjamie				if (rdtun_params(j, 1) < 0 ||
463234712Sjamie				    update_jail(j) < 0)
464234712Sjamie					continue;
465234712Sjamie				if (verbose >= 0 && (j->name || verbose > 0))
466234712Sjamie					jail_note(j, "updated\n");
467234712Sjamie			}
468234712Sjamie			dep_done(j, 0);
469234712Sjamie			break;
470234712Sjamie
471234712Sjamie		case JF_STOP:
472234712Sjamie		case JF_RESTART:
473234712Sjamie			if (j->comparam == NULL) {
474234712Sjamie				if (dep_check(j))
475234712Sjamie					continue;
476234712Sjamie				if (j->jid < 0) {
477256256Shrs					if (!(j->flags & (JF_DEPEND|JF_WILD))) {
478256256Shrs						if (verbose >= 0)
479256256Shrs							jail_quoted_warnx(j,
480256256Shrs							    "not found", NULL);
481256256Shrs						failed(j);
482256256Shrs					}
483234712Sjamie					goto jail_remove_done;
484234712Sjamie				}
485234712Sjamie				j->comparam = stopcommands;
486234712Sjamie				j->comstring = NULL;
487234712Sjamie			} else if ((j->flags & JF_FAILED) && j->jid > 0)
488234712Sjamie				goto jail_remove_done;
489234712Sjamie			if (next_command(j))
490234712Sjamie				continue;
491234712Sjamie		jail_remove_done:
492234712Sjamie			dep_done(j, 0);
493234712Sjamie			if ((j->flags & (JF_START | JF_FAILED)) == JF_START) {
494234712Sjamie				j->comparam = NULL;
495234712Sjamie				j->flags &= ~JF_STOP;
496234712Sjamie				dep_reset(j);
497234712Sjamie				requeue(j, j->ndeps ? &depend : &ready);
498234712Sjamie			}
499234712Sjamie			break;
500153056Sphilip		}
501153056Sphilip	}
502234712Sjamie
503234712Sjamie	if (jfp != NULL)
504234712Sjamie		fclose(jfp);
505234712Sjamie	exit(error);
506234712Sjamie}
507234712Sjamie
508234712Sjamie/*
509234712Sjamie * Mark a jail's failure for future handling.
510234712Sjamie */
511234712Sjamievoid
512234712Sjamiefailed(struct cfjail *j)
513234712Sjamie{
514234712Sjamie	j->flags |= JF_FAILED;
515234712Sjamie	TAILQ_REMOVE(j->queue, j, tq);
516234712Sjamie	TAILQ_INSERT_HEAD(&ready, j, tq);
517234712Sjamie	j->queue = &ready;
518234712Sjamie}
519234712Sjamie
520234712Sjamie/*
521234712Sjamie * Exit slightly more gracefully when out of memory.
522234712Sjamie */
523234712Sjamievoid *
524234712Sjamieemalloc(size_t size)
525234712Sjamie{
526234712Sjamie	void *p;
527234712Sjamie
528234712Sjamie	p = malloc(size);
529234712Sjamie	if (!p)
530234712Sjamie		err(1, "malloc");
531234712Sjamie	return p;
532234712Sjamie}
533234712Sjamie
534234712Sjamievoid *
535234712Sjamieerealloc(void *ptr, size_t size)
536234712Sjamie{
537234712Sjamie	void *p;
538234712Sjamie
539234712Sjamie	p = realloc(ptr, size);
540234712Sjamie	if (!p)
541234712Sjamie		err(1, "malloc");
542234712Sjamie	return p;
543234712Sjamie}
544234712Sjamie
545234712Sjamiechar *
546234712Sjamieestrdup(const char *str)
547234712Sjamie{
548234712Sjamie	char *ns;
549234712Sjamie
550234712Sjamie	ns = strdup(str);
551234712Sjamie	if (!ns)
552234712Sjamie		err(1, "malloc");
553234712Sjamie	return ns;
554234712Sjamie}
555234712Sjamie
556234712Sjamie/*
557234712Sjamie * Print a message including an optional jail name.
558234712Sjamie */
559234712Sjamievoid
560234712Sjamiejail_note(const struct cfjail *j, const char *fmt, ...)
561234712Sjamie{
562234712Sjamie	va_list ap, tap;
563234712Sjamie	char *cs;
564234712Sjamie	size_t len;
565234712Sjamie
566234712Sjamie	va_start(ap, fmt);
567234712Sjamie	va_copy(tap, ap);
568234712Sjamie	len = vsnprintf(NULL, 0, fmt, tap);
569234712Sjamie	va_end(tap);
570234712Sjamie	cs = alloca(len + 1);
571234712Sjamie	(void)vsnprintf(cs, len + 1, fmt, ap);
572234712Sjamie	va_end(ap);
573234712Sjamie	if (j->name)
574234712Sjamie		printf("%s: %s", j->name, cs);
575234712Sjamie	else
576234712Sjamie		printf("%s", cs);
577234712Sjamie}
578234712Sjamie
579234712Sjamie/*
580234712Sjamie * Print a warning message including an optional jail name.
581234712Sjamie */
582234712Sjamievoid
583234712Sjamiejail_warnx(const struct cfjail *j, const char *fmt, ...)
584234712Sjamie{
585234712Sjamie	va_list ap, tap;
586234712Sjamie	char *cs;
587234712Sjamie	size_t len;
588234712Sjamie
589234712Sjamie	va_start(ap, fmt);
590234712Sjamie	va_copy(tap, ap);
591234712Sjamie	len = vsnprintf(NULL, 0, fmt, tap);
592234712Sjamie	va_end(tap);
593234712Sjamie	cs = alloca(len + 1);
594234712Sjamie	(void)vsnprintf(cs, len + 1, fmt, ap);
595234712Sjamie	va_end(ap);
596234712Sjamie	if (j->name)
597234712Sjamie		warnx("%s: %s", j->name, cs);
598234712Sjamie	else
599234712Sjamie		warnx("%s", cs);
600234712Sjamie}
601234712Sjamie
602234712Sjamie/*
603234712Sjamie * Create a new jail.
604234712Sjamie */
605234712Sjamieint
606234712Sjamiecreate_jail(struct cfjail *j)
607234712Sjamie{
608234712Sjamie	struct iovec jiov[4];
609234712Sjamie	struct stat st;
610234712Sjamie	struct jailparam *jp, *setparams, *setparams2, *sjp;
611234712Sjamie	const char *path;
612234712Sjamie	int dopersist, ns, jid, dying, didfail;
613234712Sjamie
614234712Sjamie	/*
615234712Sjamie	 * Check the jail's path, with a better error message than jail_set
616234712Sjamie	 * gives.
617234712Sjamie	 */
618234712Sjamie	if ((path = string_param(j->intparams[KP_PATH]))) {
619234712Sjamie		if (j->name != NULL && path[0] != '/') {
620234712Sjamie			jail_warnx(j, "path %s: not an absolute pathname",
621234712Sjamie			    path);
622234712Sjamie			return -1;
623133743Smaxim		}
624234712Sjamie		if (stat(path, &st) < 0) {
625234712Sjamie			jail_warnx(j, "path %s: %s", path, strerror(errno));
626234712Sjamie			return -1;
627234712Sjamie		}
628234712Sjamie		if (!S_ISDIR(st.st_mode)) {
629234712Sjamie			jail_warnx(j, "path %s: %s", path, strerror(ENOTDIR));
630234712Sjamie			return -1;
631234712Sjamie		}
632112705Smaxim	}
633234712Sjamie
634234712Sjamie	/*
635234712Sjamie	 * Copy all the parameters, except that "persist" is always set when
636234712Sjamie	 * there are commands to run later.
637234712Sjamie	 */
638234712Sjamie	dopersist = !bool_param(j->intparams[KP_PERSIST]) &&
639234712Sjamie	    (j->intparams[IP_EXEC_START] || j->intparams[IP_COMMAND] ||
640234712Sjamie	     j->intparams[IP_EXEC_POSTSTART]);
641234712Sjamie	sjp = setparams =
642234712Sjamie	    alloca((j->njp + dopersist) * sizeof(struct jailparam));
643234712Sjamie	if (dopersist && jailparam_init(sjp++, "persist") < 0) {
644234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
645234712Sjamie		return -1;
646133743Smaxim	}
647234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
648234712Sjamie		if (!dopersist || !equalopts(jp->jp_name, "persist"))
649234712Sjamie			*sjp++ = *jp;
650234712Sjamie	ns = sjp - setparams;
651234712Sjamie
652234712Sjamie	didfail = 0;
653234712Sjamie	j->jid = jailparam_set_note(j, setparams, ns, JAIL_CREATE);
654234712Sjamie	if (j->jid < 0 && errno == EEXIST &&
655234712Sjamie	    bool_param(j->intparams[IP_ALLOW_DYING]) &&
656234712Sjamie	    int_param(j->intparams[KP_JID], &jid) && jid != 0) {
657234712Sjamie		/*
658234712Sjamie		 * The jail already exists, but may be dying.
659234712Sjamie		 * Make sure it is, in which case an update is appropriate.
660234712Sjamie		 */
661234712Sjamie		*(const void **)&jiov[0].iov_base = "jid";
662234712Sjamie		jiov[0].iov_len = sizeof("jid");
663234712Sjamie		jiov[1].iov_base = &jid;
664234712Sjamie		jiov[1].iov_len = sizeof(jid);
665234712Sjamie		*(const void **)&jiov[2].iov_base = "dying";
666234712Sjamie		jiov[2].iov_len = sizeof("dying");
667234712Sjamie		jiov[3].iov_base = &dying;
668234712Sjamie		jiov[3].iov_len = sizeof(dying);
669234712Sjamie		if (jail_get(jiov, 4, JAIL_DYING) < 0) {
670234712Sjamie			/*
671234712Sjamie			 * It could be that the jail just barely finished
672234712Sjamie			 * dying, or it could be that the jid never existed
673234712Sjamie			 * but the name does.  In either case, another try
674234712Sjamie			 * at creating the jail should do the right thing.
675234712Sjamie			 */
676234712Sjamie			if (errno == ENOENT)
677234712Sjamie				j->jid = jailparam_set_note(j, setparams, ns,
678234712Sjamie				    JAIL_CREATE);
679234712Sjamie		} else if (dying) {
680234712Sjamie			j->jid = jid;
681234712Sjamie			if (rdtun_params(j, 1) < 0) {
682234712Sjamie				j->jid = -1;
683234712Sjamie				didfail = 1;
684234712Sjamie			} else {
685234712Sjamie				sjp = setparams2 = alloca((j->njp + dopersist) *
686234712Sjamie				    sizeof(struct jailparam));
687234712Sjamie				for (jp = setparams; jp < setparams + ns; jp++)
688234712Sjamie					if (!JP_RDTUN(jp) ||
689234712Sjamie					    !strcmp(jp->jp_name, "jid"))
690234712Sjamie						*sjp++ = *jp;
691234712Sjamie				j->jid = jailparam_set_note(j, setparams2,
692234712Sjamie				    sjp - setparams2, JAIL_UPDATE | JAIL_DYING);
693234712Sjamie				/*
694234712Sjamie				 * Again, perhaps the jail just finished dying.
695234712Sjamie				 */
696234712Sjamie				if (j->jid < 0 && errno == ENOENT)
697234712Sjamie					j->jid = jailparam_set_note(j,
698234712Sjamie					    setparams, ns, JAIL_CREATE);
699234712Sjamie			}
700234712Sjamie		}
701234712Sjamie	}
702234712Sjamie	if (j->jid < 0 && !didfail) {
703234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
704234712Sjamie		failed(j);
705234712Sjamie	}
706234712Sjamie	if (dopersist) {
707234712Sjamie		jailparam_free(setparams, 1);
708234712Sjamie		if (j->jid > 0)
709234712Sjamie			j->flags |= JF_PERSIST;
710234712Sjamie	}
711234712Sjamie	return j->jid;
71246155Sphk}
713112705Smaxim
714234712Sjamie/*
715234712Sjamie * Remove a temporarily set "persist" parameter.
716234712Sjamie */
717112705Smaximstatic void
718234712Sjamieclear_persist(struct cfjail *j)
719112705Smaxim{
720234712Sjamie	struct iovec jiov[4];
721234712Sjamie	int jid;
722112705Smaxim
723234712Sjamie	if (!(j->flags & JF_PERSIST))
724234712Sjamie		return;
725234712Sjamie	j->flags &= ~JF_PERSIST;
726234712Sjamie	*(const void **)&jiov[0].iov_base = "jid";
727234712Sjamie	jiov[0].iov_len = sizeof("jid");
728234712Sjamie	jiov[1].iov_base = &j->jid;
729234712Sjamie	jiov[1].iov_len = sizeof(j->jid);
730234712Sjamie	*(const void **)&jiov[2].iov_base = "nopersist";
731234712Sjamie	jiov[2].iov_len = sizeof("nopersist");
732234712Sjamie	jiov[3].iov_base = NULL;
733234712Sjamie	jiov[3].iov_len = 0;
734234712Sjamie	jid = jail_set(jiov, 4, JAIL_UPDATE);
735234712Sjamie	if (verbose > 0)
736234712Sjamie		jail_note(j, "jail_set(JAIL_UPDATE) jid=%d nopersist%s%s\n",
737234712Sjamie		    j->jid, jid < 0 ? ": " : "",
738234712Sjamie		    jid < 0 ? strerror(errno) : "");
739234712Sjamie}
740234712Sjamie
741234712Sjamie/*
742234712Sjamie * Set a jail's parameters.
743234712Sjamie */
744234712Sjamiestatic int
745234712Sjamieupdate_jail(struct cfjail *j)
746234712Sjamie{
747234712Sjamie	struct jailparam *jp, *setparams, *sjp;
748234712Sjamie	int ns, jid;
749234712Sjamie
750234712Sjamie	ns = 0;
751234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
752234712Sjamie		if (!JP_RDTUN(jp))
753234712Sjamie			ns++;
754234712Sjamie	if (ns == 0)
755234712Sjamie		return 0;
756234712Sjamie	sjp = setparams = alloca(++ns * sizeof(struct jailparam));
757234712Sjamie	if (jailparam_init(sjp, "jid") < 0 ||
758234712Sjamie	    jailparam_import_raw(sjp, &j->jid, sizeof j->jid) < 0) {
759234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
760234712Sjamie		failed(j);
761234712Sjamie		return -1;
762192896Sjamie	}
763234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
764234712Sjamie		if (!JP_RDTUN(jp))
765234712Sjamie			*++sjp = *jp;
766234712Sjamie
767234712Sjamie	jid = jailparam_set_note(j, setparams, ns,
768234712Sjamie	    bool_param(j->intparams[IP_ALLOW_DYING])
769234712Sjamie	    ? JAIL_UPDATE | JAIL_DYING : JAIL_UPDATE);
770234712Sjamie	if (jid < 0) {
771234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
772234712Sjamie		failed(j);
773234712Sjamie	}
774234712Sjamie	jailparam_free(setparams, 1);
775234712Sjamie	return jid;
776112705Smaxim}
777185435Sbz
778234712Sjamie/*
779234712Sjamie * Return if a jail set would change any create-only parameters.
780234712Sjamie */
781234712Sjamiestatic int
782234712Sjamierdtun_params(struct cfjail *j, int dofail)
783234712Sjamie{
784234712Sjamie	struct jailparam *jp, *rtparams, *rtjp;
785234712Sjamie	int nrt, rval;
786234712Sjamie
787234712Sjamie	if (j->flags & JF_RDTUN)
788234712Sjamie		return 0;
789234712Sjamie	j->flags |= JF_RDTUN;
790234712Sjamie	nrt = 0;
791234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
792234712Sjamie		if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid"))
793234712Sjamie			nrt++;
794234712Sjamie	if (nrt == 0)
795234712Sjamie		return 0;
796234712Sjamie	rtjp = rtparams = alloca(++nrt * sizeof(struct jailparam));
797234712Sjamie	if (jailparam_init(rtjp, "jid") < 0 ||
798234712Sjamie	    jailparam_import_raw(rtjp, &j->jid, sizeof j->jid) < 0) {
799234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
800234712Sjamie		exit(1);
801234712Sjamie	}
802234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
803234712Sjamie		if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid"))
804234712Sjamie			*++rtjp = *jp;
805234712Sjamie	rval = 0;
806234712Sjamie	if (jailparam_get(rtparams, nrt,
807234712Sjamie	    bool_param(j->intparams[IP_ALLOW_DYING]) ? JAIL_DYING : 0) > 0) {
808234712Sjamie		rtjp = rtparams + 1;
809234712Sjamie		for (jp = j->jp, rtjp = rtparams + 1; rtjp < rtparams + nrt;
810234712Sjamie		     jp++) {
811234712Sjamie			if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) {
812234712Sjamie				if (!((jp->jp_flags & (JP_BOOL | JP_NOBOOL)) &&
813234712Sjamie				    jp->jp_valuelen == 0 &&
814234712Sjamie				    *(int *)jp->jp_value) &&
815234712Sjamie				    !(rtjp->jp_valuelen == jp->jp_valuelen &&
816234712Sjamie				    !memcmp(rtjp->jp_value, jp->jp_value,
817234712Sjamie				    jp->jp_valuelen))) {
818234712Sjamie					if (dofail) {
819234712Sjamie						jail_warnx(j, "%s cannot be "
820234712Sjamie						    "changed after creation",
821234712Sjamie						    jp->jp_name);
822234712Sjamie						failed(j);
823234712Sjamie						rval = -1;
824234712Sjamie					} else
825234712Sjamie						rval = 1;
826234712Sjamie					break;
827234712Sjamie				}
828234712Sjamie				rtjp++;
829234712Sjamie			}
830234712Sjamie		}
831234712Sjamie	}
832234712Sjamie	for (rtjp = rtparams + 1; rtjp < rtparams + nrt; rtjp++)
833234712Sjamie		rtjp->jp_name = NULL;
834234712Sjamie	jailparam_free(rtparams, nrt);
835234712Sjamie	return rval;
836234712Sjamie}
837234712Sjamie
838234712Sjamie/*
839234712Sjamie * Get the jail's jid if it is running.
840234712Sjamie */
841192896Sjamiestatic void
842234712Sjamierunning_jid(struct cfjail *j, int dflag)
843185435Sbz{
844234712Sjamie	struct iovec jiov[2];
845234712Sjamie	const char *pval;
846234712Sjamie	char *ep;
847234712Sjamie	int jid;
848192896Sjamie
849234712Sjamie	if ((pval = string_param(j->intparams[KP_JID]))) {
850234712Sjamie		if (!(jid = strtol(pval, &ep, 10)) || *ep) {
851234712Sjamie			j->jid = -1;
852234712Sjamie			return;
853234712Sjamie		}
854234712Sjamie		*(const void **)&jiov[0].iov_base = "jid";
855234712Sjamie		jiov[0].iov_len = sizeof("jid");
856234712Sjamie		jiov[1].iov_base = &jid;
857234712Sjamie		jiov[1].iov_len = sizeof(jid);
858234712Sjamie	} else if ((pval = string_param(j->intparams[KP_NAME]))) {
859234712Sjamie		*(const void **)&jiov[0].iov_base = "name";
860234712Sjamie		jiov[0].iov_len = sizeof("name");
861234712Sjamie		jiov[1].iov_len = strlen(pval) + 1;
862234712Sjamie		jiov[1].iov_base = alloca(jiov[1].iov_len);
863234712Sjamie		strcpy(jiov[1].iov_base, pval);
864234712Sjamie	} else {
865234712Sjamie		j->jid = -1;
866234712Sjamie		return;
867192896Sjamie	}
868234712Sjamie	j->jid = jail_get(jiov, 2, dflag ? JAIL_DYING : 0);
869192896Sjamie}
870192896Sjamie
871192896Sjamiestatic void
872234712Sjamiejail_quoted_warnx(const struct cfjail *j, const char *name_msg,
873234712Sjamie    const char *noname_msg)
874192896Sjamie{
875234712Sjamie	const char *pval;
876234712Sjamie
877234712Sjamie	if ((pval = j->name) || (pval = string_param(j->intparams[KP_JID])) ||
878234712Sjamie	    (pval = string_param(j->intparams[KP_NAME])))
879234712Sjamie		warnx("\"%s\" %s", pval, name_msg);
880234712Sjamie	else
881234712Sjamie		warnx("%s", noname_msg);
882234712Sjamie}
883234712Sjamie
884234712Sjamie/*
885234712Sjamie * Set jail parameters and possible print them out.
886234712Sjamie */
887234712Sjamiestatic int
888234712Sjamiejailparam_set_note(const struct cfjail *j, struct jailparam *jp, unsigned njp,
889234712Sjamie    int flags)
890234712Sjamie{
891234712Sjamie	char *value;
892234712Sjamie	int jid;
893234712Sjamie	unsigned i;
894234712Sjamie
895234712Sjamie	jid = jailparam_set(jp, njp, flags);
896234712Sjamie	if (verbose > 0) {
897234712Sjamie		jail_note(j, "jail_set(%s%s)",
898234712Sjamie		    (flags & (JAIL_CREATE | JAIL_UPDATE)) == JAIL_CREATE
899234712Sjamie		    ? "JAIL_CREATE" : "JAIL_UPDATE",
900234712Sjamie		    (flags & JAIL_DYING) ? " | JAIL_DYING" : "");
901234712Sjamie		for (i = 0; i < njp; i++) {
902234712Sjamie			printf(" %s", jp[i].jp_name);
903234712Sjamie			if (jp[i].jp_value == NULL)
904234712Sjamie				continue;
905234712Sjamie			putchar('=');
906234712Sjamie			value = jailparam_export(jp + i);
907234712Sjamie			if (value == NULL)
908234712Sjamie				err(1, "jailparam_export");
909234712Sjamie			quoted_print(stdout, value);
910234712Sjamie			free(value);
911234712Sjamie		}
912234712Sjamie		if (jid < 0)
913234712Sjamie			printf(": %s", strerror(errno));
914234712Sjamie		printf("\n");
915234712Sjamie	}
916234712Sjamie	return jid;
917234712Sjamie}
918234712Sjamie
919234712Sjamie/*
920234712Sjamie * Print a jail record.
921234712Sjamie */
922234712Sjamiestatic void
923234712Sjamieprint_jail(FILE *fp, struct cfjail *j, int oldcl)
924234712Sjamie{
925234712Sjamie	struct cfparam *p;
926234712Sjamie
927234712Sjamie	if (oldcl) {
928234712Sjamie		fprintf(fp, "%d\t", j->jid);
929234712Sjamie		print_param(fp, j->intparams[KP_PATH], ',', 0);
930234712Sjamie		putc('\t', fp);
931234712Sjamie		print_param(fp, j->intparams[KP_HOST_HOSTNAME], ',', 0);
932234712Sjamie		putc('\t', fp);
933222465Sbz#ifdef INET
934234712Sjamie		print_param(fp, j->intparams[KP_IP4_ADDR], ',', 0);
935185435Sbz#ifdef INET6
936234712Sjamie		if (j->intparams[KP_IP4_ADDR] &&
937234712Sjamie		    !TAILQ_EMPTY(&j->intparams[KP_IP4_ADDR]->val) &&
938234712Sjamie		    j->intparams[KP_IP6_ADDR] &&
939234712Sjamie		    !TAILQ_EMPTY(&j->intparams[KP_IP6_ADDR]->val))
940234712Sjamie		    putc(',', fp);
941185435Sbz#endif
942192896Sjamie#endif
943185435Sbz#ifdef INET6
944234712Sjamie		print_param(fp, j->intparams[KP_IP6_ADDR], ',', 0);
945185435Sbz#endif
946234712Sjamie		putc('\t', fp);
947234712Sjamie		print_param(fp, j->intparams[IP_COMMAND], ' ', 0);
948234712Sjamie	} else {
949234712Sjamie		fprintf(fp, "jid=%d", j->jid);
950234712Sjamie		TAILQ_FOREACH(p, &j->params, tq)
951234712Sjamie			if (strcmp(p->name, "jid")) {
952234712Sjamie				putc(' ', fp);
953234712Sjamie				print_param(fp, p, ',', 1);
954234712Sjamie			}
955234712Sjamie	}
956234712Sjamie	putc('\n', fp);
957185435Sbz}
958185435Sbz
959234712Sjamie/*
960234712Sjamie * Print a parameter value, or a name=value pair.
961234712Sjamie */
962192896Sjamiestatic void
963234712Sjamieprint_param(FILE *fp, const struct cfparam *p, int sep, int doname)
964234712Sjamie{
965234712Sjamie	const struct cfstring *s, *ts;
966234712Sjamie
967234712Sjamie	if (doname)
968234712Sjamie		fputs(p->name, fp);
969234712Sjamie	if (p == NULL || TAILQ_EMPTY(&p->val))
970234712Sjamie		return;
971234712Sjamie	if (doname)
972234712Sjamie		putc('=', fp);
973234712Sjamie	TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
974234712Sjamie		quoted_print(fp, s->s);
975234712Sjamie		if (ts != NULL)
976234712Sjamie			putc(sep, fp);
977234712Sjamie	}
978234712Sjamie}
979234712Sjamie
980234712Sjamie/*
981234712Sjamie * Print a string with quotes around spaces.
982234712Sjamie */
983234712Sjamiestatic void
984192896Sjamiequoted_print(FILE *fp, char *str)
985185435Sbz{
986192896Sjamie	int c, qc;
987192896Sjamie	char *p = str;
988185435Sbz
989234712Sjamie	qc = !*p ? '"'
990234712Sjamie	    : strchr(p, '\'') ? '"'
991192896Sjamie	    : strchr(p, '"') ? '\''
992192896Sjamie	    : strchr(p, ' ') || strchr(p, '\t') ? '"'
993192896Sjamie	    : 0;
994192896Sjamie	if (qc)
995192896Sjamie		putc(qc, fp);
996192896Sjamie	while ((c = *p++)) {
997192896Sjamie		if (c == '\\' || c == qc)
998192896Sjamie			putc('\\', fp);
999192896Sjamie		putc(c, fp);
1000185435Sbz	}
1001192896Sjamie	if (qc)
1002192896Sjamie		putc(qc, fp);
1003185435Sbz}
1004185435Sbz
1005192896Sjamiestatic void
1006192896Sjamieusage(void)
1007192896Sjamie{
1008192896Sjamie
1009192896Sjamie	(void)fprintf(stderr,
1010234712Sjamie	    "usage: jail [-dhilqv] [-J jid_file] [-u username] [-U username]\n"
1011234712Sjamie	    "            -[cmr] param=value ... [command=command ...]\n"
1012234712Sjamie	    "       jail [-dqv] [-f file] -[cmr] [jail]\n"
1013234712Sjamie	    "       jail [-qv] [-f file] -[rR] ['*' | jail ...]\n"
1014234712Sjamie	    "       jail [-dhilqv] [-J jid_file] [-u username] [-U username]\n"
1015234712Sjamie	    "            [-n jailname] [-s securelevel]\n"
1016234712Sjamie	    "            path hostname [ip[,...]] command ...\n");
1017192896Sjamie	exit(1);
1018185435Sbz}
1019