1191668Sjamie/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
4191668Sjamie * Copyright (c) 1999 Poul-Henning Kamp.
5234712Sjamie * Copyright (c) 2009-2012 James Gritton
6191668Sjamie * All rights reserved.
7191668Sjamie *
8191668Sjamie * Redistribution and use in source and binary forms, with or without
9191668Sjamie * modification, are permitted provided that the following conditions
10191668Sjamie * are met:
11191668Sjamie * 1. Redistributions of source code must retain the above copyright
12191668Sjamie *    notice, this list of conditions and the following disclaimer.
13191668Sjamie * 2. Redistributions in binary form must reproduce the above copyright
14191668Sjamie *    notice, this list of conditions and the following disclaimer in the
15191668Sjamie *    documentation and/or other materials provided with the distribution.
16191668Sjamie *
17191668Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18191668Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19191668Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20191668Sjamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21191668Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22191668Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23191668Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24191668Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25191668Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26191668Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27191668Sjamie * SUCH DAMAGE.
2846432Sphk */
2946432Sphk
30117280Scharnier#include <sys/cdefs.h>
31117280Scharnier__FBSDID("$FreeBSD: stable/11/usr.sbin/jail/jail.c 369582 2021-04-09 03:28:33Z jamie $");
32117280Scharnier
33234712Sjamie#include <sys/types.h>
34234712Sjamie#include <sys/stat.h>
35185435Sbz#include <sys/socket.h>
36158428Smatteo#include <sys/sysctl.h>
3778723Sdd
38192896Sjamie#include <arpa/inet.h>
3946155Sphk#include <netinet/in.h>
4046155Sphk
4178723Sdd#include <err.h>
42129848Smaxim#include <errno.h>
43234712Sjamie#include <stdarg.h>
4478723Sdd#include <stdio.h>
4578723Sdd#include <stdlib.h>
4678723Sdd#include <string.h>
4778723Sdd#include <unistd.h>
4878723Sdd
49234712Sjamie#include "jailp.h"
50192896Sjamie
51234712Sjamie#define JP_RDTUN(jp)	(((jp)->jp_ctltype & CTLFLAG_RDTUN) == CTLFLAG_RDTUN)
52185435Sbz
53234712Sjamiestruct permspec {
54234712Sjamie	const char	*name;
55234712Sjamie	enum intparam	ipnum;
56234712Sjamie	int		rev;
57234712Sjamie};
58234712Sjamie
59234712Sjamieconst char *cfname;
60236198Sjamieint iflag;
61234712Sjamieint note_remove;
62234712Sjamieint verbose;
63341790Seugenconst char *separator = "\t";
64234712Sjamie
65234712Sjamiestatic void clear_persist(struct cfjail *j);
66234712Sjamiestatic int update_jail(struct cfjail *j);
67234712Sjamiestatic int rdtun_params(struct cfjail *j, int dofail);
68234712Sjamiestatic void running_jid(struct cfjail *j, int dflag);
69234712Sjamiestatic void jail_quoted_warnx(const struct cfjail *j, const char *name_msg,
70234712Sjamie    const char *noname_msg);
71234712Sjamiestatic int jailparam_set_note(const struct cfjail *j, struct jailparam *jp,
72234712Sjamie    unsigned njp, int flags);
73341790Seugenstatic void print_jail(FILE *fp, struct cfjail *j, int oldcl, int running);
74234712Sjamiestatic void print_param(FILE *fp, const struct cfparam *p, int sep, int doname);
75341790Seugenstatic void show_jails(void);
76192896Sjamiestatic void quoted_print(FILE *fp, char *str);
77192896Sjamiestatic void usage(void);
78185435Sbz
79234712Sjamiestatic struct permspec perm_sysctl[] = {
80234712Sjamie    { "security.jail.set_hostname_allowed", KP_ALLOW_SET_HOSTNAME, 0 },
81234712Sjamie    { "security.jail.sysvipc_allowed", KP_ALLOW_SYSVIPC, 0 },
82234712Sjamie    { "security.jail.allow_raw_sockets", KP_ALLOW_RAW_SOCKETS, 0 },
83234712Sjamie    { "security.jail.chflags_allowed", KP_ALLOW_CHFLAGS, 0 },
84234712Sjamie    { "security.jail.mount_allowed", KP_ALLOW_MOUNT, 0 },
85234712Sjamie    { "security.jail.socket_unixiproute_only", KP_ALLOW_SOCKET_AF, 1 },
86193929Sjamie};
87193929Sjamie
88234712Sjamiestatic const enum intparam startcommands[] = {
89234988Sjamie    IP__NULL,
90234712Sjamie#ifdef INET
91234712Sjamie    IP__IP4_IFADDR,
92234712Sjamie#endif
93234712Sjamie#ifdef INET6
94234712Sjamie    IP__IP6_IFADDR,
95234712Sjamie#endif
96234712Sjamie    IP_MOUNT,
97234712Sjamie    IP__MOUNT_FROM_FSTAB,
98234712Sjamie    IP_MOUNT_DEVFS,
99256385Shrs    IP_MOUNT_FDESCFS,
100278323Sjamie    IP_MOUNT_PROCFS,
101234712Sjamie    IP_EXEC_PRESTART,
102234712Sjamie    IP__OP,
103234712Sjamie    IP_VNET_INTERFACE,
104234712Sjamie    IP_EXEC_START,
105234712Sjamie    IP_COMMAND,
106234712Sjamie    IP_EXEC_POSTSTART,
107234988Sjamie    IP__NULL
108234712Sjamie};
109192896Sjamie
110234712Sjamiestatic const enum intparam stopcommands[] = {
111234988Sjamie    IP__NULL,
112234712Sjamie    IP_EXEC_PRESTOP,
113234712Sjamie    IP_EXEC_STOP,
114234712Sjamie    IP_STOP_TIMEOUT,
115234712Sjamie    IP__OP,
116234712Sjamie    IP_EXEC_POSTSTOP,
117278323Sjamie    IP_MOUNT_PROCFS,
118256385Shrs    IP_MOUNT_FDESCFS,
119234712Sjamie    IP_MOUNT_DEVFS,
120234712Sjamie    IP__MOUNT_FROM_FSTAB,
121234712Sjamie    IP_MOUNT,
122234712Sjamie#ifdef INET6
123234712Sjamie    IP__IP6_IFADDR,
124234712Sjamie#endif
125234712Sjamie#ifdef INET
126234712Sjamie    IP__IP4_IFADDR,
127234712Sjamie#endif
128234988Sjamie    IP__NULL
129234712Sjamie};
130129848Smaxim
13146155Sphkint
13246155Sphkmain(int argc, char **argv)
13346155Sphk{
134234712Sjamie	struct stat st;
135234712Sjamie	FILE *jfp;
136234712Sjamie	struct cfjail *j;
137234712Sjamie	char *JidFile;
138193929Sjamie	size_t sysvallen;
139234712Sjamie	unsigned op, pi;
140234712Sjamie	int ch, docf, error, i, oldcl, sysval;
141236198Sjamie	int dflag, Rflag;
142194869Sjamie	char enforce_statfs[4];
143234712Sjamie#if defined(INET) || defined(INET6)
144234712Sjamie	char *cs, *ncs;
145234712Sjamie#endif
146234712Sjamie#if defined(INET) && defined(INET6)
147234712Sjamie	struct in6_addr addr6;
148234712Sjamie#endif
14946155Sphk
150234712Sjamie	op = 0;
151236198Sjamie	dflag = Rflag = 0;
152234712Sjamie	docf = 1;
153234712Sjamie	cfname = CONF_FILE;
154234712Sjamie	JidFile = NULL;
155112705Smaxim
156341790Seugen	while ((ch = getopt(argc, argv, "cde:f:hiJ:lmn:p:qrRs:u:U:v")) != -1) {
157112705Smaxim		switch (ch) {
158234712Sjamie		case 'c':
159234712Sjamie			op |= JF_START;
160234712Sjamie			break;
161192896Sjamie		case 'd':
162234712Sjamie			dflag = 1;
163192896Sjamie			break;
164341790Seugen		case 'e':
165341790Seugen			op |= JF_SHOW;
166341790Seugen			separator = optarg;
167341790Seugen			break;
168234712Sjamie		case 'f':
169234712Sjamie			cfname = optarg;
170234712Sjamie			break;
171185435Sbz		case 'h':
172234712Sjamie#if defined(INET) || defined(INET6)
173234712Sjamie			add_param(NULL, NULL, IP_IP_HOSTNAME, NULL);
174234712Sjamie#endif
175234712Sjamie			docf = 0;
176185435Sbz			break;
177113277Smike		case 'i':
178113277Smike			iflag = 1;
179234712Sjamie			verbose = -1;
180113277Smike			break;
181153056Sphilip		case 'J':
182153056Sphilip			JidFile = optarg;
183153056Sphilip			break;
184234712Sjamie		case 'l':
185234712Sjamie			add_param(NULL, NULL, IP_EXEC_CLEAN, NULL);
186234712Sjamie			docf = 0;
187234712Sjamie			break;
188234712Sjamie		case 'm':
189234712Sjamie			op |= JF_SET;
190234712Sjamie			break;
191185435Sbz		case 'n':
192234712Sjamie			add_param(NULL, NULL, KP_NAME, optarg);
193234712Sjamie			docf = 0;
194185435Sbz			break;
195234712Sjamie		case 'p':
196234712Sjamie			paralimit = strtol(optarg, NULL, 10);
197234712Sjamie			if (paralimit == 0)
198234712Sjamie				paralimit = -1;
199234712Sjamie			break;
200234712Sjamie		case 'q':
201234712Sjamie			verbose = -1;
202234712Sjamie			break;
203234712Sjamie		case 'r':
204234712Sjamie			op |= JF_STOP;
205234712Sjamie			break;
206234712Sjamie		case 'R':
207234712Sjamie			op |= JF_STOP;
208234712Sjamie			Rflag = 1;
209234712Sjamie			break;
210158428Smatteo		case 's':
211234712Sjamie			add_param(NULL, NULL, KP_SECURELEVEL, optarg);
212234712Sjamie			docf = 0;
213158428Smatteo			break;
214112705Smaxim		case 'u':
215234712Sjamie			add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg);
216234712Sjamie			add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER, NULL);
217234712Sjamie			docf = 0;
218112705Smaxim			break;
219129848Smaxim		case 'U':
220234712Sjamie			add_param(NULL, NULL, IP_EXEC_JAIL_USER, optarg);
221234712Sjamie			add_param(NULL, NULL, IP_EXEC_SYSTEM_JAIL_USER,
222234712Sjamie			    "false");
223234712Sjamie			docf = 0;
224129848Smaxim			break;
225234712Sjamie		case 'v':
226234712Sjamie			verbose = 1;
227133743Smaxim			break;
228112705Smaxim		default:
229112705Smaxim			usage();
230112705Smaxim		}
231113277Smike	}
232112705Smaxim	argc -= optind;
233112705Smaxim	argv += optind;
234234712Sjamie
235234712Sjamie	/* Find out which of the four command line styles this is. */
236234712Sjamie	oldcl = 0;
237234712Sjamie	if (!op) {
238234712Sjamie		/* Old-style command line with four fixed parameters */
239234712Sjamie		if (argc < 4 || argv[0][0] != '/')
240192896Sjamie			usage();
241234712Sjamie		op = JF_START;
242234712Sjamie		docf = 0;
243234712Sjamie		oldcl = 1;
244234712Sjamie		add_param(NULL, NULL, KP_PATH, argv[0]);
245234712Sjamie		add_param(NULL, NULL, KP_HOST_HOSTNAME, argv[1]);
246234712Sjamie#if defined(INET) || defined(INET6)
247234712Sjamie		if (argv[2][0] != '\0') {
248234712Sjamie			for (cs = argv[2];; cs = ncs + 1) {
249234712Sjamie				ncs = strchr(cs, ',');
250234712Sjamie				if (ncs)
251234712Sjamie					*ncs = '\0';
252234712Sjamie				add_param(NULL, NULL,
253234712Sjamie#if defined(INET) && defined(INET6)
254234712Sjamie				    inet_pton(AF_INET6, cs, &addr6) == 1
255234712Sjamie				    ? KP_IP6_ADDR : KP_IP4_ADDR,
256234712Sjamie#elif defined(INET)
257234712Sjamie				    KP_IP4_ADDR,
258234712Sjamie#elif defined(INET6)
259234712Sjamie				    KP_IP6_ADDR,
260222465Sbz#endif
261234712Sjamie				    cs);
262234712Sjamie				if (!ncs)
263192896Sjamie					break;
264192896Sjamie			}
265192896Sjamie		}
266192896Sjamie#endif
267234712Sjamie		for (i = 3; i < argc; i++)
268234712Sjamie			add_param(NULL, NULL, IP_COMMAND, argv[i]);
269234712Sjamie		/* Emulate the defaults from security.jail.* sysctls. */
270193929Sjamie		sysvallen = sizeof(sysval);
271193929Sjamie		if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen,
272193929Sjamie		    NULL, 0) == 0 && sysval == 0) {
273193929Sjamie			for (pi = 0; pi < sizeof(perm_sysctl) /
274193929Sjamie			     sizeof(perm_sysctl[0]); pi++) {
275193929Sjamie				sysvallen = sizeof(sysval);
276234712Sjamie				if (sysctlbyname(perm_sysctl[pi].name,
277193929Sjamie				    &sysval, &sysvallen, NULL, 0) == 0)
278234712Sjamie					add_param(NULL, NULL,
279234712Sjamie					    perm_sysctl[pi].ipnum,
280234712Sjamie					    (sysval ? 1 : 0) ^
281234712Sjamie					    perm_sysctl[pi].rev
282234712Sjamie					    ? NULL : "false");
283193929Sjamie			}
284193929Sjamie			sysvallen = sizeof(sysval);
285193929Sjamie			if (sysctlbyname("security.jail.enforce_statfs",
286193929Sjamie			    &sysval, &sysvallen, NULL, 0) == 0) {
287193929Sjamie				snprintf(enforce_statfs,
288193929Sjamie				    sizeof(enforce_statfs), "%d", sysval);
289234712Sjamie				add_param(NULL, NULL, KP_ENFORCE_STATFS,
290234712Sjamie				    enforce_statfs);
291193929Sjamie			}
292193929Sjamie		}
293341790Seugen	} else if (op == JF_STOP || op == JF_SHOW) {
294341790Seugen		/* Just print list of all configured non-wildcard jails */
295341790Seugen		if (op == JF_SHOW) {
296341790Seugen			load_config();
297341790Seugen			show_jails();
298341790Seugen			exit(0);
299341790Seugen		}
300234712Sjamie		/* Jail remove, perhaps using the config file */
301234712Sjamie		if (!docf || argc == 0)
302234712Sjamie			usage();
303234712Sjamie		if (!Rflag)
304234712Sjamie			for (i = 0; i < argc; i++)
305234712Sjamie				if (strchr(argv[i], '='))
306234712Sjamie					usage();
307234712Sjamie		if ((docf = !Rflag &&
308234712Sjamie		     (!strcmp(cfname, "-") || stat(cfname, &st) == 0)))
309234712Sjamie			load_config();
310234712Sjamie		note_remove = docf || argc > 1 || wild_jail_name(argv[0]);
311234712Sjamie	} else if (argc > 1 || (argc == 1 && strchr(argv[0], '='))) {
312234712Sjamie		/* Single jail specified on the command line */
313234712Sjamie		if (Rflag)
314234712Sjamie			usage();
315234712Sjamie		docf = 0;
316234712Sjamie		for (i = 0; i < argc; i++) {
317234712Sjamie			if (!strncmp(argv[i], "command", 7) &&
318234712Sjamie			    (argv[i][7] == '\0' || argv[i][7] == '=')) {
319234712Sjamie				if (argv[i][7]  == '=')
320234712Sjamie					add_param(NULL, NULL, IP_COMMAND,
321234712Sjamie					    argv[i] + 8);
322234712Sjamie				for (i++; i < argc; i++)
323234712Sjamie					add_param(NULL, NULL, IP_COMMAND,
324234712Sjamie					    argv[i]);
325234712Sjamie			}
326239602Sjamie#ifdef INET
327239602Sjamie			else if (!strncmp(argv[i], "ip4.addr=", 9)) {
328239602Sjamie				for (cs = argv[i] + 9;; cs = ncs + 1) {
329239602Sjamie					ncs = strchr(cs, ',');
330239602Sjamie					if (ncs)
331239602Sjamie						*ncs = '\0';
332239602Sjamie					add_param(NULL, NULL, KP_IP4_ADDR, cs);
333239602Sjamie					if (!ncs)
334239602Sjamie						break;
335239602Sjamie				}
336239602Sjamie			}
337239602Sjamie#endif
338239602Sjamie#ifdef INET6
339239602Sjamie			else if (!strncmp(argv[i], "ip6.addr=", 9)) {
340239602Sjamie				for (cs = argv[i] + 9;; cs = ncs + 1) {
341239602Sjamie					ncs = strchr(cs, ',');
342239602Sjamie					if (ncs)
343239602Sjamie						*ncs = '\0';
344239602Sjamie					add_param(NULL, NULL, KP_IP6_ADDR, cs);
345239602Sjamie					if (!ncs)
346239602Sjamie						break;
347239602Sjamie				}
348239602Sjamie			}
349239602Sjamie#endif
350239602Sjamie			else
351239602Sjamie				add_param(NULL, NULL, 0, argv[i]);
352234712Sjamie		}
353234712Sjamie	} else {
354234712Sjamie		/* From the config file, perhaps with a specified jail */
355234712Sjamie		if (Rflag || !docf)
356234712Sjamie			usage();
357234712Sjamie		load_config();
358185435Sbz	}
359185435Sbz
360234712Sjamie	/* Find out which jails will be run. */
361234712Sjamie	dep_setup(docf);
362234712Sjamie	error = 0;
363234712Sjamie	if (op == JF_STOP) {
364234712Sjamie		for (i = 0; i < argc; i++)
365234712Sjamie			if (start_state(argv[i], docf, op, Rflag) < 0)
366234712Sjamie				error = 1;
367234712Sjamie	} else {
368234712Sjamie		if (start_state(argv[0], docf, op, 0) < 0)
369234712Sjamie			exit(1);
370153056Sphilip	}
371234712Sjamie
372234712Sjamie	jfp = NULL;
373234712Sjamie	if (JidFile != NULL) {
374234712Sjamie		jfp = fopen(JidFile, "w");
375234712Sjamie		if (jfp == NULL)
376234712Sjamie			err(1, "open %s", JidFile);
377234712Sjamie		setlinebuf(jfp);
378113804Smike	}
379234712Sjamie	setlinebuf(stdout);
380234712Sjamie
381234712Sjamie	/*
382234712Sjamie	 * The main loop: Get an available jail and perform the required
383234712Sjamie	 * operation on it.  When that is done, the jail may be finished,
384234712Sjamie	 * or it may go back for the next step.
385234712Sjamie	 */
386234712Sjamie	while ((j = next_jail()))
387234712Sjamie	{
388234712Sjamie		if (j->flags & JF_FAILED) {
389234712Sjamie			error = 1;
390234712Sjamie			if (j->comparam == NULL) {
391234712Sjamie				dep_done(j, 0);
392234712Sjamie				continue;
393234712Sjamie			}
394234712Sjamie		}
395234712Sjamie		if (!(j->flags & JF_PARAMS))
396234712Sjamie		{
397234712Sjamie			j->flags |= JF_PARAMS;
398234712Sjamie			if (dflag)
399234712Sjamie				add_param(j, NULL, IP_ALLOW_DYING, NULL);
400234712Sjamie			if (check_intparams(j) < 0)
401234712Sjamie				continue;
402234712Sjamie			if ((j->flags & (JF_START | JF_SET)) &&
403234712Sjamie			    import_params(j) < 0)
404234712Sjamie				continue;
405234712Sjamie		}
406234712Sjamie		if (!j->jid)
407234712Sjamie			running_jid(j,
408234712Sjamie			    (j->flags & (JF_SET | JF_DEPEND)) == JF_SET
409234712Sjamie			    ? dflag || bool_param(j->intparams[IP_ALLOW_DYING])
410234712Sjamie			    : 0);
411234712Sjamie		if (finish_command(j))
412234712Sjamie			continue;
413234712Sjamie
414234712Sjamie		switch (j->flags & JF_OP_MASK) {
415234712Sjamie			/*
416234712Sjamie			 * These operations just turn into a different op
417234712Sjamie			 * depending on the jail's current status.
418234712Sjamie			 */
419234712Sjamie		case JF_START_SET:
420234712Sjamie			j->flags = j->jid < 0 ? JF_START : JF_SET;
421234712Sjamie			break;
422234712Sjamie		case JF_SET_RESTART:
423234712Sjamie			if (j->jid < 0) {
424234712Sjamie				jail_quoted_warnx(j, "not found",
425234712Sjamie				    "no jail specified");
426234712Sjamie				failed(j);
427234712Sjamie				continue;
428234712Sjamie			}
429234712Sjamie			j->flags = rdtun_params(j, 0) ? JF_RESTART : JF_SET;
430234712Sjamie			if (j->flags == JF_RESTART)
431234712Sjamie				dep_reset(j);
432234712Sjamie			break;
433234712Sjamie		case JF_START_SET_RESTART:
434234712Sjamie			j->flags = j->jid < 0 ? JF_START
435234712Sjamie			    : rdtun_params(j, 0) ? JF_RESTART : JF_SET;
436234712Sjamie			if (j->flags == JF_RESTART)
437234712Sjamie				dep_reset(j);
438234712Sjamie		}
439234712Sjamie
440234712Sjamie		switch (j->flags & JF_OP_MASK) {
441234712Sjamie		case JF_START:
442234712Sjamie			if (j->comparam == NULL) {
443234712Sjamie				if (j->jid > 0 &&
444234712Sjamie				    !(j->flags & (JF_DEPEND | JF_WILD))) {
445234712Sjamie					jail_quoted_warnx(j, "already exists",
446234712Sjamie					    NULL);
447234712Sjamie					failed(j);
448234712Sjamie					continue;
449192896Sjamie				}
450234712Sjamie				if (dep_check(j))
451234712Sjamie					continue;
452234712Sjamie				if (j->jid > 0)
453234712Sjamie					goto jail_create_done;
454234712Sjamie				j->comparam = startcommands;
455234712Sjamie				j->comstring = NULL;
456234712Sjamie			}
457234712Sjamie			if (next_command(j))
458234712Sjamie				continue;
459234712Sjamie		jail_create_done:
460234712Sjamie			clear_persist(j);
461234712Sjamie			if (jfp != NULL)
462341790Seugen				print_jail(jfp, j, oldcl, 1);
463234712Sjamie			dep_done(j, 0);
464234712Sjamie			break;
465234712Sjamie
466234712Sjamie		case JF_SET:
467234712Sjamie			if (j->jid < 0 && !(j->flags & JF_DEPEND)) {
468234712Sjamie				jail_quoted_warnx(j, "not found",
469234712Sjamie				    "no jail specified");
470234712Sjamie				failed(j);
471234712Sjamie				continue;
472234712Sjamie			}
473234712Sjamie			if (dep_check(j))
474234712Sjamie				continue;
475234712Sjamie			if (!(j->flags & JF_DEPEND)) {
476234712Sjamie				if (rdtun_params(j, 1) < 0 ||
477234712Sjamie				    update_jail(j) < 0)
478234712Sjamie					continue;
479234712Sjamie				if (verbose >= 0 && (j->name || verbose > 0))
480234712Sjamie					jail_note(j, "updated\n");
481234712Sjamie			}
482234712Sjamie			dep_done(j, 0);
483234712Sjamie			break;
484234712Sjamie
485234712Sjamie		case JF_STOP:
486234712Sjamie		case JF_RESTART:
487234712Sjamie			if (j->comparam == NULL) {
488234712Sjamie				if (dep_check(j))
489234712Sjamie					continue;
490234712Sjamie				if (j->jid < 0) {
491256256Shrs					if (!(j->flags & (JF_DEPEND|JF_WILD))) {
492256256Shrs						if (verbose >= 0)
493256256Shrs							jail_quoted_warnx(j,
494256256Shrs							    "not found", NULL);
495256256Shrs						failed(j);
496256256Shrs					}
497234712Sjamie					goto jail_remove_done;
498234712Sjamie				}
499234712Sjamie				j->comparam = stopcommands;
500234712Sjamie				j->comstring = NULL;
501234712Sjamie			} else if ((j->flags & JF_FAILED) && j->jid > 0)
502234712Sjamie				goto jail_remove_done;
503234712Sjamie			if (next_command(j))
504234712Sjamie				continue;
505234712Sjamie		jail_remove_done:
506234712Sjamie			dep_done(j, 0);
507234712Sjamie			if ((j->flags & (JF_START | JF_FAILED)) == JF_START) {
508234712Sjamie				j->comparam = NULL;
509234712Sjamie				j->flags &= ~JF_STOP;
510234712Sjamie				dep_reset(j);
511234712Sjamie				requeue(j, j->ndeps ? &depend : &ready);
512234712Sjamie			}
513234712Sjamie			break;
514153056Sphilip		}
515153056Sphilip	}
516234712Sjamie
517234712Sjamie	if (jfp != NULL)
518234712Sjamie		fclose(jfp);
519234712Sjamie	exit(error);
520234712Sjamie}
521234712Sjamie
522234712Sjamie/*
523234712Sjamie * Mark a jail's failure for future handling.
524234712Sjamie */
525234712Sjamievoid
526234712Sjamiefailed(struct cfjail *j)
527234712Sjamie{
528234712Sjamie	j->flags |= JF_FAILED;
529234712Sjamie	TAILQ_REMOVE(j->queue, j, tq);
530234712Sjamie	TAILQ_INSERT_HEAD(&ready, j, tq);
531234712Sjamie	j->queue = &ready;
532234712Sjamie}
533234712Sjamie
534234712Sjamie/*
535234712Sjamie * Exit slightly more gracefully when out of memory.
536234712Sjamie */
537234712Sjamievoid *
538234712Sjamieemalloc(size_t size)
539234712Sjamie{
540234712Sjamie	void *p;
541234712Sjamie
542234712Sjamie	p = malloc(size);
543234712Sjamie	if (!p)
544234712Sjamie		err(1, "malloc");
545234712Sjamie	return p;
546234712Sjamie}
547234712Sjamie
548234712Sjamievoid *
549234712Sjamieerealloc(void *ptr, size_t size)
550234712Sjamie{
551234712Sjamie	void *p;
552234712Sjamie
553234712Sjamie	p = realloc(ptr, size);
554234712Sjamie	if (!p)
555234712Sjamie		err(1, "malloc");
556234712Sjamie	return p;
557234712Sjamie}
558234712Sjamie
559234712Sjamiechar *
560234712Sjamieestrdup(const char *str)
561234712Sjamie{
562234712Sjamie	char *ns;
563234712Sjamie
564234712Sjamie	ns = strdup(str);
565234712Sjamie	if (!ns)
566234712Sjamie		err(1, "malloc");
567234712Sjamie	return ns;
568234712Sjamie}
569234712Sjamie
570234712Sjamie/*
571234712Sjamie * Print a message including an optional jail name.
572234712Sjamie */
573234712Sjamievoid
574234712Sjamiejail_note(const struct cfjail *j, const char *fmt, ...)
575234712Sjamie{
576234712Sjamie	va_list ap, tap;
577234712Sjamie	char *cs;
578234712Sjamie	size_t len;
579234712Sjamie
580234712Sjamie	va_start(ap, fmt);
581234712Sjamie	va_copy(tap, ap);
582234712Sjamie	len = vsnprintf(NULL, 0, fmt, tap);
583234712Sjamie	va_end(tap);
584234712Sjamie	cs = alloca(len + 1);
585234712Sjamie	(void)vsnprintf(cs, len + 1, fmt, ap);
586234712Sjamie	va_end(ap);
587234712Sjamie	if (j->name)
588234712Sjamie		printf("%s: %s", j->name, cs);
589234712Sjamie	else
590234712Sjamie		printf("%s", cs);
591234712Sjamie}
592234712Sjamie
593234712Sjamie/*
594234712Sjamie * Print a warning message including an optional jail name.
595234712Sjamie */
596234712Sjamievoid
597234712Sjamiejail_warnx(const struct cfjail *j, const char *fmt, ...)
598234712Sjamie{
599234712Sjamie	va_list ap, tap;
600234712Sjamie	char *cs;
601234712Sjamie	size_t len;
602234712Sjamie
603234712Sjamie	va_start(ap, fmt);
604234712Sjamie	va_copy(tap, ap);
605234712Sjamie	len = vsnprintf(NULL, 0, fmt, tap);
606234712Sjamie	va_end(tap);
607234712Sjamie	cs = alloca(len + 1);
608234712Sjamie	(void)vsnprintf(cs, len + 1, fmt, ap);
609234712Sjamie	va_end(ap);
610234712Sjamie	if (j->name)
611234712Sjamie		warnx("%s: %s", j->name, cs);
612234712Sjamie	else
613234712Sjamie		warnx("%s", cs);
614234712Sjamie}
615234712Sjamie
616234712Sjamie/*
617234712Sjamie * Create a new jail.
618234712Sjamie */
619234712Sjamieint
620234712Sjamiecreate_jail(struct cfjail *j)
621234712Sjamie{
622234712Sjamie	struct iovec jiov[4];
623234712Sjamie	struct stat st;
624234712Sjamie	struct jailparam *jp, *setparams, *setparams2, *sjp;
625234712Sjamie	const char *path;
626234712Sjamie	int dopersist, ns, jid, dying, didfail;
627234712Sjamie
628234712Sjamie	/*
629234712Sjamie	 * Check the jail's path, with a better error message than jail_set
630234712Sjamie	 * gives.
631234712Sjamie	 */
632234712Sjamie	if ((path = string_param(j->intparams[KP_PATH]))) {
633234712Sjamie		if (j->name != NULL && path[0] != '/') {
634234712Sjamie			jail_warnx(j, "path %s: not an absolute pathname",
635234712Sjamie			    path);
636234712Sjamie			return -1;
637133743Smaxim		}
638234712Sjamie		if (stat(path, &st) < 0) {
639234712Sjamie			jail_warnx(j, "path %s: %s", path, strerror(errno));
640234712Sjamie			return -1;
641234712Sjamie		}
642234712Sjamie		if (!S_ISDIR(st.st_mode)) {
643234712Sjamie			jail_warnx(j, "path %s: %s", path, strerror(ENOTDIR));
644234712Sjamie			return -1;
645234712Sjamie		}
646112705Smaxim	}
647234712Sjamie
648234712Sjamie	/*
649234712Sjamie	 * Copy all the parameters, except that "persist" is always set when
650234712Sjamie	 * there are commands to run later.
651234712Sjamie	 */
652234712Sjamie	dopersist = !bool_param(j->intparams[KP_PERSIST]) &&
653234712Sjamie	    (j->intparams[IP_EXEC_START] || j->intparams[IP_COMMAND] ||
654234712Sjamie	     j->intparams[IP_EXEC_POSTSTART]);
655234712Sjamie	sjp = setparams =
656234712Sjamie	    alloca((j->njp + dopersist) * sizeof(struct jailparam));
657234712Sjamie	if (dopersist && jailparam_init(sjp++, "persist") < 0) {
658234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
659234712Sjamie		return -1;
660133743Smaxim	}
661234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
662234712Sjamie		if (!dopersist || !equalopts(jp->jp_name, "persist"))
663234712Sjamie			*sjp++ = *jp;
664234712Sjamie	ns = sjp - setparams;
665234712Sjamie
666234712Sjamie	didfail = 0;
667234712Sjamie	j->jid = jailparam_set_note(j, setparams, ns, JAIL_CREATE);
668234712Sjamie	if (j->jid < 0 && errno == EEXIST &&
669234712Sjamie	    bool_param(j->intparams[IP_ALLOW_DYING]) &&
670234712Sjamie	    int_param(j->intparams[KP_JID], &jid) && jid != 0) {
671234712Sjamie		/*
672234712Sjamie		 * The jail already exists, but may be dying.
673234712Sjamie		 * Make sure it is, in which case an update is appropriate.
674234712Sjamie		 */
675275073Sjamie		jiov[0].iov_base = __DECONST(char *, "jid");
676234712Sjamie		jiov[0].iov_len = sizeof("jid");
677234712Sjamie		jiov[1].iov_base = &jid;
678234712Sjamie		jiov[1].iov_len = sizeof(jid);
679275073Sjamie		jiov[2].iov_base = __DECONST(char *, "dying");
680234712Sjamie		jiov[2].iov_len = sizeof("dying");
681234712Sjamie		jiov[3].iov_base = &dying;
682234712Sjamie		jiov[3].iov_len = sizeof(dying);
683234712Sjamie		if (jail_get(jiov, 4, JAIL_DYING) < 0) {
684234712Sjamie			/*
685234712Sjamie			 * It could be that the jail just barely finished
686234712Sjamie			 * dying, or it could be that the jid never existed
687234712Sjamie			 * but the name does.  In either case, another try
688234712Sjamie			 * at creating the jail should do the right thing.
689234712Sjamie			 */
690234712Sjamie			if (errno == ENOENT)
691234712Sjamie				j->jid = jailparam_set_note(j, setparams, ns,
692234712Sjamie				    JAIL_CREATE);
693234712Sjamie		} else if (dying) {
694234712Sjamie			j->jid = jid;
695234712Sjamie			if (rdtun_params(j, 1) < 0) {
696234712Sjamie				j->jid = -1;
697234712Sjamie				didfail = 1;
698234712Sjamie			} else {
699234712Sjamie				sjp = setparams2 = alloca((j->njp + dopersist) *
700234712Sjamie				    sizeof(struct jailparam));
701234712Sjamie				for (jp = setparams; jp < setparams + ns; jp++)
702234712Sjamie					if (!JP_RDTUN(jp) ||
703234712Sjamie					    !strcmp(jp->jp_name, "jid"))
704234712Sjamie						*sjp++ = *jp;
705234712Sjamie				j->jid = jailparam_set_note(j, setparams2,
706234712Sjamie				    sjp - setparams2, JAIL_UPDATE | JAIL_DYING);
707234712Sjamie				/*
708234712Sjamie				 * Again, perhaps the jail just finished dying.
709234712Sjamie				 */
710234712Sjamie				if (j->jid < 0 && errno == ENOENT)
711234712Sjamie					j->jid = jailparam_set_note(j,
712234712Sjamie					    setparams, ns, JAIL_CREATE);
713234712Sjamie			}
714234712Sjamie		}
715234712Sjamie	}
716234712Sjamie	if (j->jid < 0 && !didfail) {
717234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
718234712Sjamie		failed(j);
719234712Sjamie	}
720234712Sjamie	if (dopersist) {
721234712Sjamie		jailparam_free(setparams, 1);
722234712Sjamie		if (j->jid > 0)
723234712Sjamie			j->flags |= JF_PERSIST;
724234712Sjamie	}
725234712Sjamie	return j->jid;
72646155Sphk}
727112705Smaxim
728234712Sjamie/*
729234712Sjamie * Remove a temporarily set "persist" parameter.
730234712Sjamie */
731112705Smaximstatic void
732234712Sjamieclear_persist(struct cfjail *j)
733112705Smaxim{
734234712Sjamie	struct iovec jiov[4];
735234712Sjamie	int jid;
736112705Smaxim
737234712Sjamie	if (!(j->flags & JF_PERSIST))
738234712Sjamie		return;
739234712Sjamie	j->flags &= ~JF_PERSIST;
740275073Sjamie	jiov[0].iov_base = __DECONST(char *, "jid");
741234712Sjamie	jiov[0].iov_len = sizeof("jid");
742234712Sjamie	jiov[1].iov_base = &j->jid;
743234712Sjamie	jiov[1].iov_len = sizeof(j->jid);
744275073Sjamie	jiov[2].iov_base = __DECONST(char *, "nopersist");
745234712Sjamie	jiov[2].iov_len = sizeof("nopersist");
746234712Sjamie	jiov[3].iov_base = NULL;
747234712Sjamie	jiov[3].iov_len = 0;
748234712Sjamie	jid = jail_set(jiov, 4, JAIL_UPDATE);
749234712Sjamie	if (verbose > 0)
750234712Sjamie		jail_note(j, "jail_set(JAIL_UPDATE) jid=%d nopersist%s%s\n",
751234712Sjamie		    j->jid, jid < 0 ? ": " : "",
752234712Sjamie		    jid < 0 ? strerror(errno) : "");
753234712Sjamie}
754234712Sjamie
755234712Sjamie/*
756234712Sjamie * Set a jail's parameters.
757234712Sjamie */
758234712Sjamiestatic int
759234712Sjamieupdate_jail(struct cfjail *j)
760234712Sjamie{
761234712Sjamie	struct jailparam *jp, *setparams, *sjp;
762234712Sjamie	int ns, jid;
763234712Sjamie
764234712Sjamie	ns = 0;
765234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
766234712Sjamie		if (!JP_RDTUN(jp))
767234712Sjamie			ns++;
768234712Sjamie	if (ns == 0)
769234712Sjamie		return 0;
770234712Sjamie	sjp = setparams = alloca(++ns * sizeof(struct jailparam));
771234712Sjamie	if (jailparam_init(sjp, "jid") < 0 ||
772234712Sjamie	    jailparam_import_raw(sjp, &j->jid, sizeof j->jid) < 0) {
773234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
774234712Sjamie		failed(j);
775234712Sjamie		return -1;
776192896Sjamie	}
777234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
778234712Sjamie		if (!JP_RDTUN(jp))
779234712Sjamie			*++sjp = *jp;
780234712Sjamie
781234712Sjamie	jid = jailparam_set_note(j, setparams, ns,
782234712Sjamie	    bool_param(j->intparams[IP_ALLOW_DYING])
783234712Sjamie	    ? JAIL_UPDATE | JAIL_DYING : JAIL_UPDATE);
784234712Sjamie	if (jid < 0) {
785234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
786234712Sjamie		failed(j);
787234712Sjamie	}
788234712Sjamie	jailparam_free(setparams, 1);
789234712Sjamie	return jid;
790112705Smaxim}
791185435Sbz
792234712Sjamie/*
793234712Sjamie * Return if a jail set would change any create-only parameters.
794234712Sjamie */
795234712Sjamiestatic int
796234712Sjamierdtun_params(struct cfjail *j, int dofail)
797234712Sjamie{
798234712Sjamie	struct jailparam *jp, *rtparams, *rtjp;
799234712Sjamie	int nrt, rval;
800234712Sjamie
801234712Sjamie	if (j->flags & JF_RDTUN)
802234712Sjamie		return 0;
803234712Sjamie	j->flags |= JF_RDTUN;
804234712Sjamie	nrt = 0;
805234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
806234712Sjamie		if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid"))
807234712Sjamie			nrt++;
808234712Sjamie	if (nrt == 0)
809234712Sjamie		return 0;
810234712Sjamie	rtjp = rtparams = alloca(++nrt * sizeof(struct jailparam));
811234712Sjamie	if (jailparam_init(rtjp, "jid") < 0 ||
812234712Sjamie	    jailparam_import_raw(rtjp, &j->jid, sizeof j->jid) < 0) {
813234712Sjamie		jail_warnx(j, "%s", jail_errmsg);
814234712Sjamie		exit(1);
815234712Sjamie	}
816234712Sjamie	for (jp = j->jp; jp < j->jp + j->njp; jp++)
817338090Sjamie		if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) {
818234712Sjamie			*++rtjp = *jp;
819338090Sjamie			rtjp->jp_value = NULL;
820338090Sjamie		}
821234712Sjamie	rval = 0;
822234712Sjamie	if (jailparam_get(rtparams, nrt,
823234712Sjamie	    bool_param(j->intparams[IP_ALLOW_DYING]) ? JAIL_DYING : 0) > 0) {
824234712Sjamie		rtjp = rtparams + 1;
825311755Sdelphij		for (jp = j->jp; rtjp < rtparams + nrt; jp++) {
826234712Sjamie			if (JP_RDTUN(jp) && strcmp(jp->jp_name, "jid")) {
827234712Sjamie				if (!((jp->jp_flags & (JP_BOOL | JP_NOBOOL)) &&
828234712Sjamie				    jp->jp_valuelen == 0 &&
829234712Sjamie				    *(int *)jp->jp_value) &&
830234712Sjamie				    !(rtjp->jp_valuelen == jp->jp_valuelen &&
831338090Sjamie				    !((jp->jp_ctltype & CTLTYPE) ==
832338090Sjamie				    CTLTYPE_STRING ? strncmp(rtjp->jp_value,
833338090Sjamie				    jp->jp_value, jp->jp_valuelen) :
834338090Sjamie				    memcmp(rtjp->jp_value, jp->jp_value,
835338090Sjamie				    jp->jp_valuelen)))) {
836234712Sjamie					if (dofail) {
837234712Sjamie						jail_warnx(j, "%s cannot be "
838234712Sjamie						    "changed after creation",
839234712Sjamie						    jp->jp_name);
840234712Sjamie						failed(j);
841234712Sjamie						rval = -1;
842234712Sjamie					} else
843234712Sjamie						rval = 1;
844234712Sjamie					break;
845234712Sjamie				}
846234712Sjamie				rtjp++;
847234712Sjamie			}
848234712Sjamie		}
849234712Sjamie	}
850234712Sjamie	for (rtjp = rtparams + 1; rtjp < rtparams + nrt; rtjp++)
851234712Sjamie		rtjp->jp_name = NULL;
852234712Sjamie	jailparam_free(rtparams, nrt);
853234712Sjamie	return rval;
854234712Sjamie}
855234712Sjamie
856234712Sjamie/*
857234712Sjamie * Get the jail's jid if it is running.
858234712Sjamie */
859192896Sjamiestatic void
860234712Sjamierunning_jid(struct cfjail *j, int dflag)
861185435Sbz{
862234712Sjamie	struct iovec jiov[2];
863234712Sjamie	const char *pval;
864234712Sjamie	char *ep;
865234712Sjamie	int jid;
866192896Sjamie
867234712Sjamie	if ((pval = string_param(j->intparams[KP_JID]))) {
868234712Sjamie		if (!(jid = strtol(pval, &ep, 10)) || *ep) {
869234712Sjamie			j->jid = -1;
870234712Sjamie			return;
871234712Sjamie		}
872275073Sjamie		jiov[0].iov_base = __DECONST(char *, "jid");
873234712Sjamie		jiov[0].iov_len = sizeof("jid");
874234712Sjamie		jiov[1].iov_base = &jid;
875234712Sjamie		jiov[1].iov_len = sizeof(jid);
876234712Sjamie	} else if ((pval = string_param(j->intparams[KP_NAME]))) {
877275073Sjamie		jiov[0].iov_base = __DECONST(char *, "name");
878234712Sjamie		jiov[0].iov_len = sizeof("name");
879234712Sjamie		jiov[1].iov_len = strlen(pval) + 1;
880234712Sjamie		jiov[1].iov_base = alloca(jiov[1].iov_len);
881234712Sjamie		strcpy(jiov[1].iov_base, pval);
882234712Sjamie	} else {
883234712Sjamie		j->jid = -1;
884234712Sjamie		return;
885192896Sjamie	}
886234712Sjamie	j->jid = jail_get(jiov, 2, dflag ? JAIL_DYING : 0);
887192896Sjamie}
888192896Sjamie
889192896Sjamiestatic void
890234712Sjamiejail_quoted_warnx(const struct cfjail *j, const char *name_msg,
891234712Sjamie    const char *noname_msg)
892192896Sjamie{
893234712Sjamie	const char *pval;
894234712Sjamie
895234712Sjamie	if ((pval = j->name) || (pval = string_param(j->intparams[KP_JID])) ||
896234712Sjamie	    (pval = string_param(j->intparams[KP_NAME])))
897234712Sjamie		warnx("\"%s\" %s", pval, name_msg);
898234712Sjamie	else
899234712Sjamie		warnx("%s", noname_msg);
900234712Sjamie}
901234712Sjamie
902234712Sjamie/*
903275073Sjamie * Set jail parameters and possibly print them out.
904234712Sjamie */
905234712Sjamiestatic int
906234712Sjamiejailparam_set_note(const struct cfjail *j, struct jailparam *jp, unsigned njp,
907234712Sjamie    int flags)
908234712Sjamie{
909234712Sjamie	char *value;
910234712Sjamie	int jid;
911234712Sjamie	unsigned i;
912234712Sjamie
913234712Sjamie	jid = jailparam_set(jp, njp, flags);
914234712Sjamie	if (verbose > 0) {
915234712Sjamie		jail_note(j, "jail_set(%s%s)",
916234712Sjamie		    (flags & (JAIL_CREATE | JAIL_UPDATE)) == JAIL_CREATE
917234712Sjamie		    ? "JAIL_CREATE" : "JAIL_UPDATE",
918234712Sjamie		    (flags & JAIL_DYING) ? " | JAIL_DYING" : "");
919234712Sjamie		for (i = 0; i < njp; i++) {
920234712Sjamie			printf(" %s", jp[i].jp_name);
921234712Sjamie			if (jp[i].jp_value == NULL)
922234712Sjamie				continue;
923234712Sjamie			putchar('=');
924234712Sjamie			value = jailparam_export(jp + i);
925234712Sjamie			if (value == NULL)
926234712Sjamie				err(1, "jailparam_export");
927234712Sjamie			quoted_print(stdout, value);
928234712Sjamie			free(value);
929234712Sjamie		}
930234712Sjamie		if (jid < 0)
931234712Sjamie			printf(": %s", strerror(errno));
932234712Sjamie		printf("\n");
933234712Sjamie	}
934234712Sjamie	return jid;
935234712Sjamie}
936234712Sjamie
937234712Sjamie/*
938234712Sjamie * Print a jail record.
939234712Sjamie */
940234712Sjamiestatic void
941341790Seugenprint_jail(FILE *fp, struct cfjail *j, int oldcl, int running)
942234712Sjamie{
943234712Sjamie	struct cfparam *p;
944341790Seugen	int printsep;
945234712Sjamie
946234712Sjamie	if (oldcl) {
947341790Seugen		if (running)
948341790Seugen			fprintf(fp, "%d%s", j->jid, separator);
949234712Sjamie		print_param(fp, j->intparams[KP_PATH], ',', 0);
950341790Seugen		fputs(separator, fp);
951234712Sjamie		print_param(fp, j->intparams[KP_HOST_HOSTNAME], ',', 0);
952341790Seugen		fputs(separator, fp);
953222465Sbz#ifdef INET
954234712Sjamie		print_param(fp, j->intparams[KP_IP4_ADDR], ',', 0);
955185435Sbz#ifdef INET6
956234712Sjamie		if (j->intparams[KP_IP4_ADDR] &&
957234712Sjamie		    !TAILQ_EMPTY(&j->intparams[KP_IP4_ADDR]->val) &&
958234712Sjamie		    j->intparams[KP_IP6_ADDR] &&
959234712Sjamie		    !TAILQ_EMPTY(&j->intparams[KP_IP6_ADDR]->val))
960234712Sjamie		    putc(',', fp);
961185435Sbz#endif
962192896Sjamie#endif
963185435Sbz#ifdef INET6
964234712Sjamie		print_param(fp, j->intparams[KP_IP6_ADDR], ',', 0);
965185435Sbz#endif
966341790Seugen		fputs(separator, fp);
967234712Sjamie		print_param(fp, j->intparams[IP_COMMAND], ' ', 0);
968234712Sjamie	} else {
969341790Seugen		printsep = 0;
970341790Seugen		if (running) {
971341790Seugen			fprintf(fp, "jid=%d", j->jid);
972341790Seugen			printsep = 1;
973341790Seugen		}
974234712Sjamie		TAILQ_FOREACH(p, &j->params, tq)
975234712Sjamie			if (strcmp(p->name, "jid")) {
976341790Seugen				if (printsep)
977341790Seugen					fputs(separator, fp);
978341790Seugen				else
979341790Seugen					printsep = 1;
980234712Sjamie				print_param(fp, p, ',', 1);
981234712Sjamie			}
982234712Sjamie	}
983234712Sjamie	putc('\n', fp);
984185435Sbz}
985185435Sbz
986234712Sjamie/*
987341790Seugen * Exhibit list of all configured non-wildcard jails
988341790Seugen */
989341790Seugenstatic void
990341790Seugenshow_jails(void)
991341790Seugen{
992341790Seugen	struct cfjail *j;
993341790Seugen
994341790Seugen	TAILQ_FOREACH(j, &cfjails, tq)
995341790Seugen		print_jail(stdout, j, 0, 0);
996341790Seugen}
997341790Seugen
998341790Seugen/*
999234712Sjamie * Print a parameter value, or a name=value pair.
1000234712Sjamie */
1001192896Sjamiestatic void
1002234712Sjamieprint_param(FILE *fp, const struct cfparam *p, int sep, int doname)
1003234712Sjamie{
1004234712Sjamie	const struct cfstring *s, *ts;
1005234712Sjamie
1006234712Sjamie	if (doname)
1007234712Sjamie		fputs(p->name, fp);
1008234712Sjamie	if (p == NULL || TAILQ_EMPTY(&p->val))
1009234712Sjamie		return;
1010234712Sjamie	if (doname)
1011234712Sjamie		putc('=', fp);
1012234712Sjamie	TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) {
1013234712Sjamie		quoted_print(fp, s->s);
1014234712Sjamie		if (ts != NULL)
1015234712Sjamie			putc(sep, fp);
1016234712Sjamie	}
1017234712Sjamie}
1018234712Sjamie
1019234712Sjamie/*
1020234712Sjamie * Print a string with quotes around spaces.
1021234712Sjamie */
1022234712Sjamiestatic void
1023192896Sjamiequoted_print(FILE *fp, char *str)
1024185435Sbz{
1025192896Sjamie	int c, qc;
1026192896Sjamie	char *p = str;
1027185435Sbz
1028234712Sjamie	qc = !*p ? '"'
1029234712Sjamie	    : strchr(p, '\'') ? '"'
1030192896Sjamie	    : strchr(p, '"') ? '\''
1031192896Sjamie	    : strchr(p, ' ') || strchr(p, '\t') ? '"'
1032192896Sjamie	    : 0;
1033192896Sjamie	if (qc)
1034192896Sjamie		putc(qc, fp);
1035192896Sjamie	while ((c = *p++)) {
1036192896Sjamie		if (c == '\\' || c == qc)
1037192896Sjamie			putc('\\', fp);
1038192896Sjamie		putc(c, fp);
1039185435Sbz	}
1040192896Sjamie	if (qc)
1041192896Sjamie		putc(qc, fp);
1042185435Sbz}
1043185435Sbz
1044192896Sjamiestatic void
1045192896Sjamieusage(void)
1046192896Sjamie{
1047192896Sjamie
1048192896Sjamie	(void)fprintf(stderr,
1049234712Sjamie	    "usage: jail [-dhilqv] [-J jid_file] [-u username] [-U username]\n"
1050234712Sjamie	    "            -[cmr] param=value ... [command=command ...]\n"
1051360112Seugen	    "       jail [-dqv] [-f file] -[cmr] [jail]\n"
1052234712Sjamie	    "       jail [-qv] [-f file] -[rR] ['*' | jail ...]\n"
1053234712Sjamie	    "       jail [-dhilqv] [-J jid_file] [-u username] [-U username]\n"
1054234712Sjamie	    "            [-n jailname] [-s securelevel]\n"
1055369582Sjamie	    "            path hostname ip[,...] command ...\n"
1056360112Seugen	    "       jail [-f file] -e separator\n");
1057192896Sjamie	exit(1);
1058185435Sbz}
1059