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$");
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,
96234712Sjamie    IP_EXEC_PRESTART,
97234712Sjamie    IP__OP,
98234712Sjamie    IP_VNET_INTERFACE,
99234712Sjamie    IP_EXEC_START,
100234712Sjamie    IP_COMMAND,
101234712Sjamie    IP_EXEC_POSTSTART,
102234988Sjamie    IP__NULL
103234712Sjamie};
104192896Sjamie
105234712Sjamiestatic const enum intparam stopcommands[] = {
106234988Sjamie    IP__NULL,
107234712Sjamie    IP_EXEC_PRESTOP,
108234712Sjamie    IP_EXEC_STOP,
109234712Sjamie    IP_STOP_TIMEOUT,
110234712Sjamie    IP__OP,
111234712Sjamie    IP_EXEC_POSTSTOP,
112256387Shrs    IP_MOUNT_FDESCFS,
113234712Sjamie    IP_MOUNT_DEVFS,
114234712Sjamie    IP__MOUNT_FROM_FSTAB,
115234712Sjamie    IP_MOUNT,
116234712Sjamie#ifdef INET6
117234712Sjamie    IP__IP6_IFADDR,
118234712Sjamie#endif
119234712Sjamie#ifdef INET
120234712Sjamie    IP__IP4_IFADDR,
121234712Sjamie#endif
122234988Sjamie    IP__NULL
123234712Sjamie};
124129848Smaxim
12546155Sphkint
12646155Sphkmain(int argc, char **argv)
12746155Sphk{
128234712Sjamie	struct stat st;
129234712Sjamie	FILE *jfp;
130234712Sjamie	struct cfjail *j;
131234712Sjamie	char *JidFile;
132193929Sjamie	size_t sysvallen;
133234712Sjamie	unsigned op, pi;
134234712Sjamie	int ch, docf, error, i, oldcl, sysval;
135236198Sjamie	int dflag, Rflag;
136194869Sjamie	char enforce_statfs[4];
137234712Sjamie#if defined(INET) || defined(INET6)
138234712Sjamie	char *cs, *ncs;
139234712Sjamie#endif
140234712Sjamie#if defined(INET) && defined(INET6)
141234712Sjamie	struct in6_addr addr6;
142234712Sjamie#endif
14346155Sphk
144234712Sjamie	op = 0;
145236198Sjamie	dflag = Rflag = 0;
146234712Sjamie	docf = 1;
147234712Sjamie	cfname = CONF_FILE;
148234712Sjamie	JidFile = NULL;
149112705Smaxim
150237697Smaxim	while ((ch = getopt(argc, argv, "cdf:hiJ:lmn:p:qrRs:u:U:v")) != -1) {
151112705Smaxim		switch (ch) {
152234712Sjamie		case 'c':
153234712Sjamie			op |= JF_START;
154234712Sjamie			break;
155192896Sjamie		case 'd':
156234712Sjamie			dflag = 1;
157192896Sjamie			break;
158234712Sjamie		case 'f':
159234712Sjamie			cfname = optarg;
160234712Sjamie			break;
161185435Sbz		case 'h':
162234712Sjamie#if defined(INET) || defined(INET6)
163234712Sjamie			add_param(NULL, NULL, IP_IP_HOSTNAME, NULL);
164234712Sjamie#endif
165234712Sjamie			docf = 0;
166185435Sbz			break;
167113277Smike		case 'i':
168113277Smike			iflag = 1;
169234712Sjamie			verbose = -1;
170113277Smike			break;
171153056Sphilip		case 'J':
172153056Sphilip			JidFile = optarg;
173153056Sphilip			break;
174234712Sjamie		case 'l':
175234712Sjamie			add_param(NULL, NULL, IP_EXEC_CLEAN, NULL);
176234712Sjamie			docf = 0;
177234712Sjamie			break;
178234712Sjamie		case 'm':
179234712Sjamie			op |= JF_SET;
180234712Sjamie			break;
181185435Sbz		case 'n':
182234712Sjamie			add_param(NULL, NULL, KP_NAME, optarg);
183234712Sjamie			docf = 0;
184185435Sbz			break;
185234712Sjamie		case 'p':
186234712Sjamie			paralimit = strtol(optarg, NULL, 10);
187234712Sjamie			if (paralimit == 0)
188234712Sjamie				paralimit = -1;
189234712Sjamie			break;
190234712Sjamie		case 'q':
191234712Sjamie			verbose = -1;
192234712Sjamie			break;
193234712Sjamie		case 'r':
194234712Sjamie			op |= JF_STOP;
195234712Sjamie			break;
196234712Sjamie		case 'R':
197234712Sjamie			op |= JF_STOP;
198234712Sjamie			Rflag = 1;
199234712Sjamie			break;
200158428Smatteo		case 's':
201234712Sjamie			add_param(NULL, NULL, KP_SECURELEVEL, optarg);
202234712Sjamie			docf = 0;
203158428Smatteo			break;
204112705Smaxim		case 'u':
205234712Sjamie			add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg);
206234712Sjamie			add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER, NULL);
207234712Sjamie			docf = 0;
208112705Smaxim			break;
209129848Smaxim		case 'U':
210234712Sjamie			add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg);
211234712Sjamie			add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER,
212234712Sjamie			    "false");
213234712Sjamie			docf = 0;
214129848Smaxim			break;
215234712Sjamie		case 'v':
216234712Sjamie			verbose = 1;
217133743Smaxim			break;
218112705Smaxim		default:
219112705Smaxim			usage();
220112705Smaxim		}
221113277Smike	}
222112705Smaxim	argc -= optind;
223112705Smaxim	argv += optind;
224234712Sjamie
225234712Sjamie	/* Find out which of the four command line styles this is. */
226234712Sjamie	oldcl = 0;
227234712Sjamie	if (!op) {
228234712Sjamie		/* Old-style command line with four fixed parameters */
229234712Sjamie		if (argc < 4 || argv[0][0] != '/')
230192896Sjamie			usage();
231234712Sjamie		op = JF_START;
232234712Sjamie		docf = 0;
233234712Sjamie		oldcl = 1;
234234712Sjamie		add_param(NULL, NULL, KP_PATH, argv[0]);
235234712Sjamie		add_param(NULL, NULL, KP_HOST_HOSTNAME, argv[1]);
236234712Sjamie#if defined(INET) || defined(INET6)
237234712Sjamie		if (argv[2][0] != '\0') {
238234712Sjamie			for (cs = argv[2];; cs = ncs + 1) {
239234712Sjamie				ncs = strchr(cs, ',');
240234712Sjamie				if (ncs)
241234712Sjamie					*ncs = '\0';
242234712Sjamie				add_param(NULL, NULL,
243234712Sjamie#if defined(INET) && defined(INET6)
244234712Sjamie				    inet_pton(AF_INET6, cs, &addr6) == 1
245234712Sjamie				    ? KP_IP6_ADDR : KP_IP4_ADDR,
246234712Sjamie#elif defined(INET)
247234712Sjamie				    KP_IP4_ADDR,
248234712Sjamie#elif defined(INET6)
249234712Sjamie				    KP_IP6_ADDR,
250222465Sbz#endif
251234712Sjamie				    cs);
252234712Sjamie				if (!ncs)
253192896Sjamie					break;
254192896Sjamie			}
255192896Sjamie		}
256192896Sjamie#endif
257234712Sjamie		for (i = 3; i < argc; i++)
258234712Sjamie			add_param(NULL, NULL, IP_COMMAND, argv[i]);
259234712Sjamie		/* Emulate the defaults from security.jail.* sysctls. */
260193929Sjamie		sysvallen = sizeof(sysval);
261193929Sjamie		if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen,
262193929Sjamie		    NULL, 0) == 0 && sysval == 0) {
263193929Sjamie			for (pi = 0; pi < sizeof(perm_sysctl) /
264193929Sjamie			     sizeof(perm_sysctl[0]); pi++) {
265193929Sjamie				sysvallen = sizeof(sysval);
266234712Sjamie				if (sysctlbyname(perm_sysctl[pi].name,
267193929Sjamie				    &sysval, &sysvallen, NULL, 0) == 0)
268234712Sjamie					add_param(NULL, NULL,
269234712Sjamie					    perm_sysctl[pi].ipnum,
270234712Sjamie					    (sysval ? 1 : 0) ^
271234712Sjamie					    perm_sysctl[pi].rev
272234712Sjamie					    ? NULL : "false");
273193929Sjamie			}
274193929Sjamie			sysvallen = sizeof(sysval);
275193929Sjamie			if (sysctlbyname("security.jail.enforce_statfs",
276193929Sjamie			    &sysval, &sysvallen, NULL, 0) == 0) {
277193929Sjamie				snprintf(enforce_statfs,
278193929Sjamie				    sizeof(enforce_statfs), "%d", sysval);
279234712Sjamie				add_param(NULL, NULL, KP_ENFORCE_STATFS,
280234712Sjamie				    enforce_statfs);
281193929Sjamie			}
282193929Sjamie		}
283234712Sjamie	} else if (op == JF_STOP) {
284234712Sjamie		/* Jail remove, perhaps using the config file */
285234712Sjamie		if (!docf || argc == 0)
286234712Sjamie			usage();
287234712Sjamie		if (!Rflag)
288234712Sjamie			for (i = 0; i < argc; i++)
289234712Sjamie				if (strchr(argv[i], '='))
290234712Sjamie					usage();
291234712Sjamie		if ((docf = !Rflag &&
292234712Sjamie		     (!strcmp(cfname, "-") || stat(cfname, &st) == 0)))
293234712Sjamie			load_config();
294234712Sjamie		note_remove = docf || argc > 1 || wild_jail_name(argv[0]);
295234712Sjamie	} else if (argc > 1 || (argc == 1 && strchr(argv[0], '='))) {
296234712Sjamie		/* Single jail specified on the command line */
297234712Sjamie		if (Rflag)
298234712Sjamie			usage();
299234712Sjamie		docf = 0;
300234712Sjamie		for (i = 0; i < argc; i++) {
301234712Sjamie			if (!strncmp(argv[i], "command", 7) &&
302234712Sjamie			    (argv[i][7] == '\0' || argv[i][7] == '=')) {
303234712Sjamie				if (argv[i][7]  == '=')
304234712Sjamie					add_param(NULL, NULL, IP_COMMAND,
305234712Sjamie					    argv[i] + 8);
306234712Sjamie				for (i++; i < argc; i++)
307234712Sjamie					add_param(NULL, NULL, IP_COMMAND,
308234712Sjamie					    argv[i]);
309234712Sjamie			}
310239602Sjamie#ifdef INET
311239602Sjamie			else if (!strncmp(argv[i], "ip4.addr=", 9)) {
312239602Sjamie				for (cs = argv[i] + 9;; cs = ncs + 1) {
313239602Sjamie					ncs = strchr(cs, ',');
314239602Sjamie					if (ncs)
315239602Sjamie						*ncs = '\0';
316239602Sjamie					add_param(NULL, NULL, KP_IP4_ADDR, cs);
317239602Sjamie					if (!ncs)
318239602Sjamie						break;
319239602Sjamie				}
320239602Sjamie			}
321239602Sjamie#endif
322239602Sjamie#ifdef INET6
323239602Sjamie			else if (!strncmp(argv[i], "ip6.addr=", 9)) {
324239602Sjamie				for (cs = argv[i] + 9;; cs = ncs + 1) {
325239602Sjamie					ncs = strchr(cs, ',');
326239602Sjamie					if (ncs)
327239602Sjamie						*ncs = '\0';
328239602Sjamie					add_param(NULL, NULL, KP_IP6_ADDR, cs);
329239602Sjamie					if (!ncs)
330239602Sjamie						break;
331239602Sjamie				}
332239602Sjamie			}
333239602Sjamie#endif
334239602Sjamie			else
335239602Sjamie				add_param(NULL, NULL, 0, argv[i]);
336234712Sjamie		}
337234712Sjamie	} else {
338234712Sjamie		/* From the config file, perhaps with a specified jail */
339234712Sjamie		if (Rflag || !docf)
340234712Sjamie			usage();
341234712Sjamie		load_config();
342185435Sbz	}
343185435Sbz
344234712Sjamie	/* Find out which jails will be run. */
345234712Sjamie	dep_setup(docf);
346234712Sjamie	error = 0;
347234712Sjamie	if (op == JF_STOP) {
348234712Sjamie		for (i = 0; i < argc; i++)
349234712Sjamie			if (start_state(argv[i], docf, op, Rflag) < 0)
350234712Sjamie				error = 1;
351234712Sjamie	} else {
352234712Sjamie		if (start_state(argv[0], docf, op, 0) < 0)
353234712Sjamie			exit(1);
354153056Sphilip	}
355234712Sjamie
356234712Sjamie	jfp = NULL;
357234712Sjamie	if (JidFile != NULL) {
358234712Sjamie		jfp = fopen(JidFile, "w");
359234712Sjamie		if (jfp == NULL)
360234712Sjamie			err(1, "open %s", JidFile);
361234712Sjamie		setlinebuf(jfp);
362113804Smike	}
363234712Sjamie	setlinebuf(stdout);
364234712Sjamie
365234712Sjamie	/*
366234712Sjamie	 * The main loop: Get an available jail and perform the required
367234712Sjamie	 * operation on it.  When that is done, the jail may be finished,
368234712Sjamie	 * or it may go back for the next step.
369234712Sjamie	 */
370234712Sjamie	while ((j = next_jail()))
371234712Sjamie	{
372234712Sjamie		if (j->flags & JF_FAILED) {
373234712Sjamie			error = 1;
374234712Sjamie			if (j->comparam == NULL) {
375234712Sjamie				dep_done(j, 0);
376234712Sjamie				continue;
377234712Sjamie			}
378234712Sjamie		}
379234712Sjamie		if (!(j->flags & JF_PARAMS))
380234712Sjamie		{
381234712Sjamie			j->flags |= JF_PARAMS;
382234712Sjamie			if (dflag)
383234712Sjamie				add_param(j, NULL, IP_ALLOW_DYING, NULL);
384234712Sjamie			if (check_intparams(j) < 0)
385234712Sjamie				continue;
386234712Sjamie			if ((j->flags & (JF_START | JF_SET)) &&
387234712Sjamie			    import_params(j) < 0)
388234712Sjamie				continue;
389234712Sjamie		}
390234712Sjamie		if (!j->jid)
391234712Sjamie			running_jid(j,
392234712Sjamie			    (j->flags & (JF_SET | JF_DEPEND)) == JF_SET
393234712Sjamie			    ? dflag || bool_param(j->intparams[IP_ALLOW_DYING])
394234712Sjamie			    : 0);
395234712Sjamie		if (finish_command(j))
396234712Sjamie			continue;
397234712Sjamie
398234712Sjamie		switch (j->flags & JF_OP_MASK) {
399234712Sjamie			/*
400234712Sjamie			 * These operations just turn into a different op
401234712Sjamie			 * depending on the jail's current status.
402234712Sjamie			 */
403234712Sjamie		case JF_START_SET:
404234712Sjamie			j->flags = j->jid < 0 ? JF_START : JF_SET;
405234712Sjamie			break;
406234712Sjamie		case JF_SET_RESTART:
407234712Sjamie			if (j->jid < 0) {
408234712Sjamie				jail_quoted_warnx(j, "not found",
409234712Sjamie				    "no jail specified");
410234712Sjamie				failed(j);
411234712Sjamie				continue;
412234712Sjamie			}
413234712Sjamie			j->flags = rdtun_params(j, 0) ? JF_RESTART : JF_SET;
414234712Sjamie			if (j->flags == JF_RESTART)
415234712Sjamie				dep_reset(j);
416234712Sjamie			break;
417234712Sjamie		case JF_START_SET_RESTART:
418234712Sjamie			j->flags = j->jid < 0 ? JF_START
419234712Sjamie			    : rdtun_params(j, 0) ? JF_RESTART : JF_SET;
420234712Sjamie			if (j->flags == JF_RESTART)
421234712Sjamie				dep_reset(j);
422234712Sjamie		}
423234712Sjamie
424234712Sjamie		switch (j->flags & JF_OP_MASK) {
425234712Sjamie		case JF_START:
426234712Sjamie			if (j->comparam == NULL) {
427234712Sjamie				if (j->jid > 0 &&
428234712Sjamie				    !(j->flags & (JF_DEPEND | JF_WILD))) {
429234712Sjamie					jail_quoted_warnx(j, "already exists",
430234712Sjamie					    NULL);
431234712Sjamie					failed(j);
432234712Sjamie					continue;
433192896Sjamie				}
434234712Sjamie				if (dep_check(j))
435234712Sjamie					continue;
436234712Sjamie				if (j->jid > 0)
437234712Sjamie					goto jail_create_done;
438234712Sjamie				j->comparam = startcommands;
439234712Sjamie				j->comstring = NULL;
440234712Sjamie			}
441234712Sjamie			if (next_command(j))
442234712Sjamie				continue;
443234712Sjamie		jail_create_done:
444234712Sjamie			clear_persist(j);
445234712Sjamie			if (jfp != NULL)
446234712Sjamie				print_jail(jfp, j, oldcl);
447234712Sjamie			dep_done(j, 0);
448234712Sjamie			break;
449234712Sjamie
450234712Sjamie		case JF_SET:
451234712Sjamie			if (j->jid < 0 && !(j->flags & JF_DEPEND)) {
452234712Sjamie				jail_quoted_warnx(j, "not found",
453234712Sjamie				    "no jail specified");
454234712Sjamie				failed(j);
455234712Sjamie				continue;
456234712Sjamie			}
457234712Sjamie			if (dep_check(j))
458234712Sjamie				continue;
459234712Sjamie			if (!(j->flags & JF_DEPEND)) {
460234712Sjamie				if (rdtun_params(j, 1) < 0 ||
461234712Sjamie				    update_jail(j) < 0)
462234712Sjamie					continue;
463234712Sjamie				if (verbose >= 0 && (j->name || verbose > 0))
464234712Sjamie					jail_note(j, "updated\n");
465234712Sjamie			}
466234712Sjamie			dep_done(j, 0);
467234712Sjamie			break;
468234712Sjamie
469234712Sjamie		case JF_STOP:
470234712Sjamie		case JF_RESTART:
471234712Sjamie			if (j->comparam == NULL) {
472234712Sjamie				if (dep_check(j))
473234712Sjamie					continue;
474234712Sjamie				if (j->jid < 0) {
475256256Shrs					if (!(j->flags & (JF_DEPEND|JF_WILD))) {
476256256Shrs						if (verbose >= 0)
477256256Shrs							jail_quoted_warnx(j,
478256256Shrs							    "not found", NULL);
479256256Shrs						failed(j);
480256256Shrs					}
481234712Sjamie					goto jail_remove_done;
482234712Sjamie				}
483234712Sjamie				j->comparam = stopcommands;
484234712Sjamie				j->comstring = NULL;
485234712Sjamie			} else if ((j->flags & JF_FAILED) && j->jid > 0)
486234712Sjamie				goto jail_remove_done;
487234712Sjamie			if (next_command(j))
488234712Sjamie				continue;
489234712Sjamie		jail_remove_done:
490234712Sjamie			dep_done(j, 0);
491234712Sjamie			if ((j->flags & (JF_START | JF_FAILED)) == JF_START) {
492234712Sjamie				j->comparam = NULL;
493234712Sjamie				j->flags &= ~JF_STOP;
494234712Sjamie				dep_reset(j);
495234712Sjamie				requeue(j, j->ndeps ? &depend : &ready);
496234712Sjamie			}
497234712Sjamie			break;
498153056Sphilip		}
499153056Sphilip	}
500234712Sjamie
501234712Sjamie	if (jfp != NULL)
502234712Sjamie		fclose(jfp);
503234712Sjamie	exit(error);
504234712Sjamie}
505234712Sjamie
506234712Sjamie/*
507234712Sjamie * Mark a jail's failure for future handling.
508234712Sjamie */
509234712Sjamievoid
510234712Sjamiefailed(struct cfjail *j)
511234712Sjamie{
512234712Sjamie	j->flags |= JF_FAILED;
513234712Sjamie	TAILQ_REMOVE(j->queue, j, tq);
514234712Sjamie	TAILQ_INSERT_HEAD(&ready, j, tq);
515234712Sjamie	j->queue = &ready;
516234712Sjamie}
517234712Sjamie
518234712Sjamie/*
519234712Sjamie * Exit slightly more gracefully when out of memory.
520234712Sjamie */
521234712Sjamievoid *
522234712Sjamieemalloc(size_t size)
523234712Sjamie{
524234712Sjamie	void *p;
525234712Sjamie
526234712Sjamie	p = malloc(size);
527234712Sjamie	if (!p)
528234712Sjamie		err(1, "malloc");
529234712Sjamie	return p;
530234712Sjamie}
531234712Sjamie
532234712Sjamievoid *
533234712Sjamieerealloc(void *ptr, size_t size)
534234712Sjamie{
535234712Sjamie	void *p;
536234712Sjamie
537234712Sjamie	p = realloc(ptr, size);
538234712Sjamie	if (!p)
539234712Sjamie		err(1, "malloc");
540234712Sjamie	return p;
541234712Sjamie}
542234712Sjamie
543234712Sjamiechar *
544234712Sjamieestrdup(const char *str)
545234712Sjamie{
546234712Sjamie	char *ns;
547234712Sjamie
548234712Sjamie	ns = strdup(str);
549234712Sjamie	if (!ns)
550234712Sjamie		err(1, "malloc");
551234712Sjamie	return ns;
552234712Sjamie}
553234712Sjamie
554234712Sjamie/*
555234712Sjamie * Print a message including an optional jail name.
556234712Sjamie */
557234712Sjamievoid
558234712Sjamiejail_note(const struct cfjail *j, const char *fmt, ...)
559234712Sjamie{
560234712Sjamie	va_list ap, tap;
561234712Sjamie	char *cs;
562234712Sjamie	size_t len;
563234712Sjamie
564234712Sjamie	va_start(ap, fmt);
565234712Sjamie	va_copy(tap, ap);
566234712Sjamie	len = vsnprintf(NULL, 0, fmt, tap);
567234712Sjamie	va_end(tap);
568234712Sjamie	cs = alloca(len + 1);
569234712Sjamie	(void)vsnprintf(cs, len + 1, fmt, ap);
570234712Sjamie	va_end(ap);
571234712Sjamie	if (j->name)
572234712Sjamie		printf("%s: %s", j->name, cs);
573234712Sjamie	else
574234712Sjamie		printf("%s", cs);
575234712Sjamie}
576234712Sjamie
577234712Sjamie/*
578234712Sjamie * Print a warning message including an optional jail name.
579234712Sjamie */
580234712Sjamievoid
581234712Sjamiejail_warnx(const struct cfjail *j, const char *fmt, ...)
582234712Sjamie{
583234712Sjamie	va_list ap, tap;
584234712Sjamie	char *cs;
585234712Sjamie	size_t len;
586234712Sjamie
587234712Sjamie	va_start(ap, fmt);
588234712Sjamie	va_copy(tap, ap);
589234712Sjamie	len = vsnprintf(NULL, 0, fmt, tap);
590234712Sjamie	va_end(tap);
591234712Sjamie	cs = alloca(len + 1);
592234712Sjamie	(void)vsnprintf(cs, len + 1, fmt, ap);
593234712Sjamie	va_end(ap);
594234712Sjamie	if (j->name)
595234712Sjamie		warnx("%s: %s", j->name, cs);
596234712Sjamie	else
597234712Sjamie		warnx("%s", cs);
598234712Sjamie}
599234712Sjamie
600234712Sjamie/*
601234712Sjamie * Create a new jail.
602234712Sjamie */
603234712Sjamieint
604234712Sjamiecreate_jail(struct cfjail *j)
605234712Sjamie{
606234712Sjamie	struct iovec jiov[4];
607234712Sjamie	struct stat st;
608234712Sjamie	struct jailparam *jp, *setparams, *setparams2, *sjp;
609234712Sjamie	const char *path;
610234712Sjamie	int dopersist, ns, jid, dying, didfail;
611234712Sjamie
612234712Sjamie	/*
613234712Sjamie	 * Check the jail's path, with a better error message than jail_set
614234712Sjamie	 * gives.
615234712Sjamie	 */
616234712Sjamie	if ((path = string_param(j->intparams[KP_PATH]))) {
617234712Sjamie		if (j->name != NULL && path[0] != '/') {
618234712Sjamie			jail_warnx(j, "path %s: not an absolute pathname",
619234712Sjamie			    path);
620234712Sjamie			return -1;
621133743Smaxim		}
622234712Sjamie		if (stat(path, &st) < 0) {
623234712Sjamie			jail_warnx(j, "path %s: %s", path, strerror(errno));
624234712Sjamie			return -1;
625234712Sjamie		}
626234712Sjamie		if (!S_ISDIR(st.st_mode)) {
627234712Sjamie			jail_warnx(j, "path %s: %s", path, strerror(ENOTDIR));
628234712Sjamie			return -1;
629234712Sjamie		}
630112705Smaxim	}
631234712Sjamie
632234712Sjamie	/*
633234712Sjamie	 * Copy all the parameters, except that "persist" is always set when
634234712Sjamie	 * there are commands to run later.
635234712Sjamie	 */
636234712Sjamie	dopersist = !bool_param(j->intparams[KP_PERSIST]) &&
637234712Sjamie	    (j->intparams[IP_EXEC_START] || j->intparams[IP_COMMAND] ||
638234712Sjamie	     j->intparams[IP_EXEC_POSTSTART]);
639234712Sjamie	sjp = setparams =
640234712Sjamie	    alloca((j->njp + dopersist) * sizeof(struct jailparam));
641234712Sjamie	if (dopersist && jailparam_init(sjp++, "persist") < 0) {
642234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
643234712Sjamie		return -1;
644133743Smaxim	}
645234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
646234712Sjamie		if (!dopersist || !equalopts(jp->jp_name, "persist"))
647234712Sjamie			*sjp++ = *jp;
648234712Sjamie	ns = sjp - setparams;
649234712Sjamie
650234712Sjamie	didfail = 0;
651234712Sjamie	j->jid = jailparam_set_note(j, setparams, ns, JAIL_CREATE);
652234712Sjamie	if (j->jid < 0 && errno == EEXIST &&
653234712Sjamie	    bool_param(j->intparams[IP_ALLOW_DYING]) &&
654234712Sjamie	    int_param(j->intparams[KP_JID], &jid) && jid != 0) {
655234712Sjamie		/*
656234712Sjamie		 * The jail already exists, but may be dying.
657234712Sjamie		 * Make sure it is, in which case an update is appropriate.
658234712Sjamie		 */
659234712Sjamie		*(const void **)&jiov[0].iov_base = "jid";
660234712Sjamie		jiov[0].iov_len = sizeof("jid");
661234712Sjamie		jiov[1].iov_base = &jid;
662234712Sjamie		jiov[1].iov_len = sizeof(jid);
663234712Sjamie		*(const void **)&jiov[2].iov_base = "dying";
664234712Sjamie		jiov[2].iov_len = sizeof("dying");
665234712Sjamie		jiov[3].iov_base = &dying;
666234712Sjamie		jiov[3].iov_len = sizeof(dying);
667234712Sjamie		if (jail_get(jiov, 4, JAIL_DYING) < 0) {
668234712Sjamie			/*
669234712Sjamie			 * It could be that the jail just barely finished
670234712Sjamie			 * dying, or it could be that the jid never existed
671234712Sjamie			 * but the name does.  In either case, another try
672234712Sjamie			 * at creating the jail should do the right thing.
673234712Sjamie			 */
674234712Sjamie			if (errno == ENOENT)
675234712Sjamie				j->jid = jailparam_set_note(j, setparams, ns,
676234712Sjamie				    JAIL_CREATE);
677234712Sjamie		} else if (dying) {
678234712Sjamie			j->jid = jid;
679234712Sjamie			if (rdtun_params(j, 1) < 0) {
680234712Sjamie				j->jid = -1;
681234712Sjamie				didfail = 1;
682234712Sjamie			} else {
683234712Sjamie				sjp = setparams2 = alloca((j->njp + dopersist) *
684234712Sjamie				    sizeof(struct jailparam));
685234712Sjamie				for (jp = setparams; jp < setparams + ns; jp++)
686234712Sjamie					if (!JP_RDTUN(jp) ||
687234712Sjamie					    !strcmp(jp->jp_name, "jid"))
688234712Sjamie						*sjp++ = *jp;
689234712Sjamie				j->jid = jailparam_set_note(j, setparams2,
690234712Sjamie				    sjp - setparams2, JAIL_UPDATE | JAIL_DYING);
691234712Sjamie				/*
692234712Sjamie				 * Again, perhaps the jail just finished dying.
693234712Sjamie				 */
694234712Sjamie				if (j->jid < 0 && errno == ENOENT)
695234712Sjamie					j->jid = jailparam_set_note(j,
696234712Sjamie					    setparams, ns, JAIL_CREATE);
697234712Sjamie			}
698234712Sjamie		}
699234712Sjamie	}
700234712Sjamie	if (j->jid < 0 && !didfail) {
701234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
702234712Sjamie		failed(j);
703234712Sjamie	}
704234712Sjamie	if (dopersist) {
705234712Sjamie		jailparam_free(setparams, 1);
706234712Sjamie		if (j->jid > 0)
707234712Sjamie			j->flags |= JF_PERSIST;
708234712Sjamie	}
709234712Sjamie	return j->jid;
71046155Sphk}
711112705Smaxim
712234712Sjamie/*
713234712Sjamie * Remove a temporarily set "persist" parameter.
714234712Sjamie */
715112705Smaximstatic void
716234712Sjamieclear_persist(struct cfjail *j)
717112705Smaxim{
718234712Sjamie	struct iovec jiov[4];
719234712Sjamie	int jid;
720112705Smaxim
721234712Sjamie	if (!(j->flags & JF_PERSIST))
722234712Sjamie		return;
723234712Sjamie	j->flags &= ~JF_PERSIST;
724234712Sjamie	*(const void **)&jiov[0].iov_base = "jid";
725234712Sjamie	jiov[0].iov_len = sizeof("jid");
726234712Sjamie	jiov[1].iov_base = &j->jid;
727234712Sjamie	jiov[1].iov_len = sizeof(j->jid);
728234712Sjamie	*(const void **)&jiov[2].iov_base = "nopersist";
729234712Sjamie	jiov[2].iov_len = sizeof("nopersist");
730234712Sjamie	jiov[3].iov_base = NULL;
731234712Sjamie	jiov[3].iov_len = 0;
732234712Sjamie	jid = jail_set(jiov, 4, JAIL_UPDATE);
733234712Sjamie	if (verbose > 0)
734234712Sjamie		jail_note(j, "jail_set(JAIL_UPDATE) jid=%d nopersist%s%s\n",
735234712Sjamie		    j->jid, jid < 0 ? ": " : "",
736234712Sjamie		    jid < 0 ? strerror(errno) : "");
737234712Sjamie}
738234712Sjamie
739234712Sjamie/*
740234712Sjamie * Set a jail's parameters.
741234712Sjamie */
742234712Sjamiestatic int
743234712Sjamieupdate_jail(struct cfjail *j)
744234712Sjamie{
745234712Sjamie	struct jailparam *jp, *setparams, *sjp;
746234712Sjamie	int ns, jid;
747234712Sjamie
748234712Sjamie	ns = 0;
749234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
750234712Sjamie		if (!JP_RDTUN(jp))
751234712Sjamie			ns++;
752234712Sjamie	if (ns == 0)
753234712Sjamie		return 0;
754234712Sjamie	sjp = setparams = alloca(++ns * sizeof(struct jailparam));
755234712Sjamie	if (jailparam_init(sjp, "jid") < 0 ||
756234712Sjamie	    jailparam_import_raw(sjp, &j->jid, sizeof j->jid) < 0) {
757234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
758234712Sjamie		failed(j);
759234712Sjamie		return -1;
760192896Sjamie	}
761234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
762234712Sjamie		if (!JP_RDTUN(jp))
763234712Sjamie			*++sjp = *jp;
764234712Sjamie
765234712Sjamie	jid = jailparam_set_note(j, setparams, ns,
766234712Sjamie	    bool_param(j->intparams[IP_ALLOW_DYING])
767234712Sjamie	    ? JAIL_UPDATE | JAIL_DYING : JAIL_UPDATE);
768234712Sjamie	if (jid < 0) {
769234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
770234712Sjamie		failed(j);
771234712Sjamie	}
772234712Sjamie	jailparam_free(setparams, 1);
773234712Sjamie	return jid;
774112705Smaxim}
775185435Sbz
776234712Sjamie/*
777234712Sjamie * Return if a jail set would change any create-only parameters.
778234712Sjamie */
779234712Sjamiestatic int
780234712Sjamierdtun_params(struct cfjail *j, int dofail)
781234712Sjamie{
782234712Sjamie	struct jailparam *jp, *rtparams, *rtjp;
783234712Sjamie	int nrt, rval;
784234712Sjamie
785234712Sjamie	if (j->flags & JF_RDTUN)
786234712Sjamie		return 0;
787234712Sjamie	j->flags |= JF_RDTUN;
788234712Sjamie	nrt = 0;
789234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
790234712Sjamie		if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid"))
791234712Sjamie			nrt++;
792234712Sjamie	if (nrt == 0)
793234712Sjamie		return 0;
794234712Sjamie	rtjp = rtparams = alloca(++nrt * sizeof(struct jailparam));
795234712Sjamie	if (jailparam_init(rtjp, "jid") < 0 ||
796234712Sjamie	    jailparam_import_raw(rtjp, &j->jid, sizeof j->jid) < 0) {
797234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
798234712Sjamie		exit(1);
799234712Sjamie	}
800234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
801234712Sjamie		if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid"))
802234712Sjamie			*++rtjp = *jp;
803234712Sjamie	rval = 0;
804234712Sjamie	if (jailparam_get(rtparams, nrt,
805234712Sjamie	    bool_param(j->intparams[IP_ALLOW_DYING]) ? JAIL_DYING : 0) > 0) {
806234712Sjamie		rtjp = rtparams + 1;
807234712Sjamie		for (jp = j->jp, rtjp = rtparams + 1; rtjp < rtparams + nrt;
808234712Sjamie		     jp++) {
809234712Sjamie			if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) {
810234712Sjamie				if (!((jp->jp_flags & (JP_BOOL | JP_NOBOOL)) &&
811234712Sjamie				    jp->jp_valuelen == 0 &&
812234712Sjamie				    *(int *)jp->jp_value) &&
813234712Sjamie				    !(rtjp->jp_valuelen == jp->jp_valuelen &&
814234712Sjamie				    !memcmp(rtjp->jp_value, jp->jp_value,
815234712Sjamie				    jp->jp_valuelen))) {
816234712Sjamie					if (dofail) {
817234712Sjamie						jail_warnx(j, "%s cannot be "
818234712Sjamie						    "changed after creation",
819234712Sjamie						    jp->jp_name);
820234712Sjamie						failed(j);
821234712Sjamie						rval = -1;
822234712Sjamie					} else
823234712Sjamie						rval = 1;
824234712Sjamie					break;
825234712Sjamie				}
826234712Sjamie				rtjp++;
827234712Sjamie			}
828234712Sjamie		}
829234712Sjamie	}
830234712Sjamie	for (rtjp = rtparams + 1; rtjp < rtparams + nrt; rtjp++)
831234712Sjamie		rtjp->jp_name = NULL;
832234712Sjamie	jailparam_free(rtparams, nrt);
833234712Sjamie	return rval;
834234712Sjamie}
835234712Sjamie
836234712Sjamie/*
837234712Sjamie * Get the jail's jid if it is running.
838234712Sjamie */
839192896Sjamiestatic void
840234712Sjamierunning_jid(struct cfjail *j, int dflag)
841185435Sbz{
842234712Sjamie	struct iovec jiov[2];
843234712Sjamie	const char *pval;
844234712Sjamie	char *ep;
845234712Sjamie	int jid;
846192896Sjamie
847234712Sjamie	if ((pval = string_param(j->intparams[KP_JID]))) {
848234712Sjamie		if (!(jid = strtol(pval, &ep, 10)) || *ep) {
849234712Sjamie			j->jid = -1;
850234712Sjamie			return;
851234712Sjamie		}
852234712Sjamie		*(const void **)&jiov[0].iov_base = "jid";
853234712Sjamie		jiov[0].iov_len = sizeof("jid");
854234712Sjamie		jiov[1].iov_base = &jid;
855234712Sjamie		jiov[1].iov_len = sizeof(jid);
856234712Sjamie	} else if ((pval = string_param(j->intparams[KP_NAME]))) {
857234712Sjamie		*(const void **)&jiov[0].iov_base = "name";
858234712Sjamie		jiov[0].iov_len = sizeof("name");
859234712Sjamie		jiov[1].iov_len = strlen(pval) + 1;
860234712Sjamie		jiov[1].iov_base = alloca(jiov[1].iov_len);
861234712Sjamie		strcpy(jiov[1].iov_base, pval);
862234712Sjamie	} else {
863234712Sjamie		j->jid = -1;
864234712Sjamie		return;
865192896Sjamie	}
866234712Sjamie	j->jid = jail_get(jiov, 2, dflag ? JAIL_DYING : 0);
867192896Sjamie}
868192896Sjamie
869192896Sjamiestatic void
870234712Sjamiejail_quoted_warnx(const struct cfjail *j, const char *name_msg,
871234712Sjamie    const char *noname_msg)
872192896Sjamie{
873234712Sjamie	const char *pval;
874234712Sjamie
875234712Sjamie	if ((pval = j->name) || (pval = string_param(j->intparams[KP_JID])) ||
876234712Sjamie	    (pval = string_param(j->intparams[KP_NAME])))
877234712Sjamie		warnx("\"%s\" %s", pval, name_msg);
878234712Sjamie	else
879234712Sjamie		warnx("%s", noname_msg);
880234712Sjamie}
881234712Sjamie
882234712Sjamie/*
883234712Sjamie * Set jail parameters and possible print them out.
884234712Sjamie */
885234712Sjamiestatic int
886234712Sjamiejailparam_set_note(const struct cfjail *j, struct jailparam *jp, unsigned njp,
887234712Sjamie    int flags)
888234712Sjamie{
889234712Sjamie	char *value;
890234712Sjamie	int jid;
891234712Sjamie	unsigned i;
892234712Sjamie
893234712Sjamie	jid = jailparam_set(jp, njp, flags);
894234712Sjamie	if (verbose > 0) {
895234712Sjamie		jail_note(j, "jail_set(%s%s)",
896234712Sjamie		    (flags & (JAIL_CREATE | JAIL_UPDATE)) == JAIL_CREATE
897234712Sjamie		    ? "JAIL_CREATE" : "JAIL_UPDATE",
898234712Sjamie		    (flags & JAIL_DYING) ? " | JAIL_DYING" : "");
899234712Sjamie		for (i = 0; i < njp; i++) {
900234712Sjamie			printf(" %s", jp[i].jp_name);
901234712Sjamie			if (jp[i].jp_value == NULL)
902234712Sjamie				continue;
903234712Sjamie			putchar('=');
904234712Sjamie			value = jailparam_export(jp + i);
905234712Sjamie			if (value == NULL)
906234712Sjamie				err(1, "jailparam_export");
907234712Sjamie			quoted_print(stdout, value);
908234712Sjamie			free(value);
909234712Sjamie		}
910234712Sjamie		if (jid < 0)
911234712Sjamie			printf(": %s", strerror(errno));
912234712Sjamie		printf("\n");
913234712Sjamie	}
914234712Sjamie	return jid;
915234712Sjamie}
916234712Sjamie
917234712Sjamie/*
918234712Sjamie * Print a jail record.
919234712Sjamie */
920234712Sjamiestatic void
921234712Sjamieprint_jail(FILE *fp, struct cfjail *j, int oldcl)
922234712Sjamie{
923234712Sjamie	struct cfparam *p;
924234712Sjamie
925234712Sjamie	if (oldcl) {
926234712Sjamie		fprintf(fp, "%d\t", j->jid);
927234712Sjamie		print_param(fp, j->intparams[KP_PATH], ',', 0);
928234712Sjamie		putc('\t', fp);
929234712Sjamie		print_param(fp, j->intparams[KP_HOST_HOSTNAME], ',', 0);
930234712Sjamie		putc('\t', fp);
931222465Sbz#ifdef INET
932234712Sjamie		print_param(fp, j->intparams[KP_IP4_ADDR], ',', 0);
933185435Sbz#ifdef INET6
934234712Sjamie		if (j->intparams[KP_IP4_ADDR] &&
935234712Sjamie		    !TAILQ_EMPTY(&j->intparams[KP_IP4_ADDR]->val) &&
936234712Sjamie		    j->intparams[KP_IP6_ADDR] &&
937234712Sjamie		    !TAILQ_EMPTY(&j->intparams[KP_IP6_ADDR]->val))
938234712Sjamie		    putc(',', fp);
939185435Sbz#endif
940192896Sjamie#endif
941185435Sbz#ifdef INET6
942234712Sjamie		print_param(fp, j->intparams[KP_IP6_ADDR], ',', 0);
943185435Sbz#endif
944234712Sjamie		putc('\t', fp);
945234712Sjamie		print_param(fp, j->intparams[IP_COMMAND], ' ', 0);
946234712Sjamie	} else {
947234712Sjamie		fprintf(fp, "jid=%d", j->jid);
948234712Sjamie		TAILQ_FOREACH(p, &j->params, tq)
949234712Sjamie			if (strcmp(p->name, "jid")) {
950234712Sjamie				putc(' ', fp);
951234712Sjamie				print_param(fp, p, ',', 1);
952234712Sjamie			}
953234712Sjamie	}
954234712Sjamie	putc('\n', fp);
955185435Sbz}
956185435Sbz
957234712Sjamie/*
958234712Sjamie * Print a parameter value, or a name=value pair.
959234712Sjamie */
960192896Sjamiestatic void
961234712Sjamieprint_param(FILE *fp, const struct cfparam *p, int sep, int doname)
962234712Sjamie{
963234712Sjamie	const struct cfstring *s, *ts;
964234712Sjamie
965234712Sjamie	if (doname)
966234712Sjamie		fputs(p->name, fp);
967234712Sjamie	if (p == NULL || TAILQ_EMPTY(&p->val))
968234712Sjamie		return;
969234712Sjamie	if (doname)
970234712Sjamie		putc('=', fp);
971234712Sjamie	TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
972234712Sjamie		quoted_print(fp, s->s);
973234712Sjamie		if (ts != NULL)
974234712Sjamie			putc(sep, fp);
975234712Sjamie	}
976234712Sjamie}
977234712Sjamie
978234712Sjamie/*
979234712Sjamie * Print a string with quotes around spaces.
980234712Sjamie */
981234712Sjamiestatic void
982192896Sjamiequoted_print(FILE *fp, char *str)
983185435Sbz{
984192896Sjamie	int c, qc;
985192896Sjamie	char *p = str;
986185435Sbz
987234712Sjamie	qc = !*p ? '"'
988234712Sjamie	    : strchr(p, '\'') ? '"'
989192896Sjamie	    : strchr(p, '"') ? '\''
990192896Sjamie	    : strchr(p, ' ') || strchr(p, '\t') ? '"'
991192896Sjamie	    : 0;
992192896Sjamie	if (qc)
993192896Sjamie		putc(qc, fp);
994192896Sjamie	while ((c = *p++)) {
995192896Sjamie		if (c == '\\' || c == qc)
996192896Sjamie			putc('\\', fp);
997192896Sjamie		putc(c, fp);
998185435Sbz	}
999192896Sjamie	if (qc)
1000192896Sjamie		putc(qc, fp);
1001185435Sbz}
1002185435Sbz
1003192896Sjamiestatic void
1004192896Sjamieusage(void)
1005192896Sjamie{
1006192896Sjamie
1007192896Sjamie	(void)fprintf(stderr,
1008234712Sjamie	    "usage: jail [-dhilqv] [-J jid_file] [-u username] [-U username]\n"
1009234712Sjamie	    "            -[cmr] param=value ... [command=command ...]\n"
1010234712Sjamie	    "       jail [-dqv] [-f file] -[cmr] [jail]\n"
1011234712Sjamie	    "       jail [-qv] [-f file] -[rR] ['*' | jail ...]\n"
1012234712Sjamie	    "       jail [-dhilqv] [-J jid_file] [-u username] [-U username]\n"
1013234712Sjamie	    "            [-n jailname] [-s securelevel]\n"
1014234712Sjamie	    "            path hostname [ip[,...]] command ...\n");
1015192896Sjamie	exit(1);
1016185435Sbz}
1017