1214117Sjamie/*-
2223190Sjamie * Copyright (c) 2011 James Gritton
3214117Sjamie * All rights reserved.
4214117Sjamie *
5214117Sjamie * Redistribution and use in source and binary forms, with or without
6214117Sjamie * modification, are permitted provided that the following conditions
7214117Sjamie * are met:
8214117Sjamie * 1. Redistributions of source code must retain the above copyright
9214117Sjamie *    notice, this list of conditions and the following disclaimer.
10214117Sjamie * 2. Redistributions in binary form must reproduce the above copyright
11214117Sjamie *    notice, this list of conditions and the following disclaimer in the
12214117Sjamie *    documentation and/or other materials provided with the distribution.
13214117Sjamie *
14214117Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15214117Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16214117Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17214117Sjamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18214117Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19214117Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20214117Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21214117Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22214117Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23214117Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24214117Sjamie * SUCH DAMAGE.
25214117Sjamie */
26214117Sjamie
27214117Sjamie#include <sys/cdefs.h>
28214117Sjamie__FBSDID("$FreeBSD$");
29214117Sjamie
30214117Sjamie#include <sys/uio.h>
31214117Sjamie
32214117Sjamie#include <err.h>
33214117Sjamie#include <stdlib.h>
34214117Sjamie#include <string.h>
35214117Sjamie
36214117Sjamie#include "jailp.h"
37214117Sjamie
38214117Sjamiestruct cfjails ready = TAILQ_HEAD_INITIALIZER(ready);
39216367Sjamiestruct cfjails depend = TAILQ_HEAD_INITIALIZER(depend);
40214117Sjamie
41214117Sjamiestatic void dep_add(struct cfjail *from, struct cfjail *to, unsigned flags);
42214117Sjamiestatic int cmp_jailptr(const void *a, const void *b);
43214117Sjamiestatic int cmp_jailptr_name(const void *a, const void *b);
44214117Sjamiestatic struct cfjail *find_jail(const char *name);
45214117Sjamiestatic int running_jid(const char *name, int flags);
46214117Sjamie
47214117Sjamiestatic struct cfjail **jails_byname;
48214117Sjamiestatic size_t njails;
49214117Sjamie
50214117Sjamie/*
51214117Sjamie * Set up jail dependency lists.
52214117Sjamie */
53214117Sjamievoid
54214117Sjamiedep_setup(int docf)
55214117Sjamie{
56214117Sjamie	struct cfjail *j, *dj;
57214117Sjamie	struct cfparam *p;
58214117Sjamie	struct cfstring *s;
59214117Sjamie	struct cfdepend *d;
60214117Sjamie	const char *cs;
61214117Sjamie	char *pname;
62214117Sjamie	size_t plen;
63214117Sjamie	int error, deps, ldeps;
64214117Sjamie
65214117Sjamie	if (!docf) {
66214117Sjamie		/*
67214117Sjamie		 * With no config file, let "depend" for a single jail
68214117Sjamie		 * look at currently running jails.
69214117Sjamie		 */
70214117Sjamie		if ((j = TAILQ_FIRST(&cfjails)) &&
71214117Sjamie		    (p = j->intparams[IP_DEPEND])) {
72223188Sjamie			TAILQ_FOREACH(s, &p->val, tq) {
73214117Sjamie				if (running_jid(s->s, 0) < 0) {
74214117Sjamie					warnx("depends on nonexistent jail "
75214117Sjamie					    "\"%s\"", s->s);
76214117Sjamie					j->flags |= JF_FAILED;
77214117Sjamie				}
78214117Sjamie			}
79214117Sjamie		}
80214117Sjamie		return;
81214117Sjamie	}
82214117Sjamie
83214117Sjamie	njails = 0;
84214117Sjamie	TAILQ_FOREACH(j, &cfjails, tq)
85214117Sjamie		njails++;
86214117Sjamie	jails_byname = emalloc(njails * sizeof(struct cfjail *));
87214117Sjamie	njails = 0;
88214117Sjamie	TAILQ_FOREACH(j, &cfjails, tq)
89214117Sjamie		jails_byname[njails++] = j;
90214117Sjamie	qsort(jails_byname, njails, sizeof(struct cfjail *), cmp_jailptr);
91214117Sjamie	error = 0;
92214117Sjamie	deps = 0;
93214117Sjamie	ldeps = 0;
94214117Sjamie	plen = 0;
95214117Sjamie	pname = NULL;
96214117Sjamie	TAILQ_FOREACH(j, &cfjails, tq) {
97214117Sjamie		if (j->flags & JF_FAILED)
98214117Sjamie			continue;
99214117Sjamie		if ((p = j->intparams[IP_DEPEND])) {
100223188Sjamie			TAILQ_FOREACH(s, &p->val, tq) {
101214117Sjamie				dj = find_jail(s->s);
102214117Sjamie				if (dj != NULL) {
103214117Sjamie					deps++;
104214117Sjamie					dep_add(j, dj, 0);
105214117Sjamie				} else {
106214117Sjamie					jail_warnx(j,
107214117Sjamie					    "depends on undefined jail \"%s\"",
108214117Sjamie					    s->s);
109214117Sjamie					j->flags |= JF_FAILED;
110214117Sjamie				}
111214117Sjamie			}
112214117Sjamie		}
113214117Sjamie		/* A jail has an implied dependency on its parent. */
114214117Sjamie		if ((cs = strrchr(j->name, '.')))
115214117Sjamie		{
116214117Sjamie			if (plen < (size_t)(cs - j->name + 1)) {
117214117Sjamie				plen = (cs - j->name) + 1;
118214117Sjamie				pname = erealloc(pname, plen);
119214117Sjamie			}
120214117Sjamie			strlcpy(pname, j->name, plen);
121214117Sjamie			dj = find_jail(pname);
122214117Sjamie			if (dj != NULL) {
123214117Sjamie				ldeps++;
124214117Sjamie				dep_add(j, dj, DF_LIGHT);
125214117Sjamie			}
126214117Sjamie		}
127214117Sjamie	}
128214117Sjamie
129214117Sjamie	/* Look for dependency loops. */
130214117Sjamie	if (deps && (deps > 1 || ldeps)) {
131231238Sjamie		(void)start_state(NULL, 0, 0, 0);
132214117Sjamie		while ((j = TAILQ_FIRST(&ready))) {
133214117Sjamie			requeue(j, &cfjails);
134214117Sjamie			dep_done(j, DF_NOFAIL);
135214117Sjamie		}
136216367Sjamie		while ((j = TAILQ_FIRST(&depend)) != NULL) {
137214117Sjamie			jail_warnx(j, "dependency loop");
138214117Sjamie			j->flags |= JF_FAILED;
139214117Sjamie			do {
140214117Sjamie				requeue(j, &cfjails);
141214117Sjamie				dep_done(j, DF_NOFAIL);
142214117Sjamie			} while ((j = TAILQ_FIRST(&ready)));
143214117Sjamie		}
144214117Sjamie		TAILQ_FOREACH(j, &cfjails, tq)
145214117Sjamie			STAILQ_FOREACH(d, &j->dep[DEP_FROM], tq[DEP_FROM])
146214117Sjamie				d->flags &= ~DF_SEEN;
147214117Sjamie	}
148214117Sjamie	if (pname != NULL)
149214117Sjamie		free(pname);
150214117Sjamie}
151214117Sjamie
152214117Sjamie/*
153214117Sjamie * Return if a jail has dependencies.
154214117Sjamie */
155214117Sjamieint
156214117Sjamiedep_check(struct cfjail *j)
157214117Sjamie{
158214117Sjamie	int reset, depfrom, depto, ndeps, rev;
159214117Sjamie	struct cfjail *dj;
160214117Sjamie	struct cfdepend *d;
161214117Sjamie
162214117Sjamie	static int bits[] = { 0, 1, 1, 2, 1, 2, 2, 3 };
163214117Sjamie
164214117Sjamie	if (j->ndeps == 0)
165214117Sjamie		return 0;
166214117Sjamie	ndeps = 0;
167214117Sjamie	if ((rev = JF_DO_STOP(j->flags))) {
168214117Sjamie		depfrom = DEP_TO;
169214117Sjamie		depto = DEP_FROM;
170214117Sjamie	} else {
171214117Sjamie		depfrom = DEP_FROM;
172214117Sjamie		depto = DEP_TO;
173214117Sjamie	}
174214117Sjamie	STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom]) {
175214117Sjamie		if (d->flags & DF_SEEN)
176214117Sjamie			continue;
177214117Sjamie		dj = d->j[depto];
178214117Sjamie		if (dj->flags & JF_FAILED) {
179214117Sjamie			if (!(j->flags & (JF_DEPEND | JF_FAILED)) &&
180214117Sjamie			    verbose >= 0)
181214117Sjamie				jail_warnx(j, "skipped");
182214117Sjamie			j->flags |= JF_FAILED;
183214117Sjamie			continue;
184214117Sjamie		}
185214117Sjamie		/*
186214117Sjamie		 * The dependee's state may be set (or changed) as a result of
187214117Sjamie		 * being in a dependency it wasn't in earlier.
188214117Sjamie		 */
189214117Sjamie		reset = 0;
190214117Sjamie		if (bits[dj->flags & JF_OP_MASK] <= 1) {
191214117Sjamie			if (!(dj->flags & JF_OP_MASK)) {
192214117Sjamie				reset = 1;
193214117Sjamie				dj->flags |= JF_DEPEND;
194214117Sjamie				requeue(dj, &ready);
195214117Sjamie			}
196214117Sjamie			/* Set or change the dependee's state. */
197214117Sjamie			switch (j->flags & JF_OP_MASK) {
198214117Sjamie			case JF_START:
199214117Sjamie				dj->flags |= JF_START;
200214117Sjamie				break;
201214117Sjamie			case JF_SET:
202214117Sjamie				if (!(dj->flags & JF_OP_MASK))
203214117Sjamie					dj->flags |= JF_SET;
204214117Sjamie				else if (dj->flags & JF_STOP)
205214117Sjamie					dj->flags |= JF_START;
206214117Sjamie				break;
207214117Sjamie			case JF_STOP:
208214117Sjamie			case JF_RESTART:
209214117Sjamie				if (!(dj->flags & JF_STOP))
210214117Sjamie					reset = 1;
211214117Sjamie				dj->flags |= JF_STOP;
212214117Sjamie				if (dj->flags & JF_SET)
213214117Sjamie					dj->flags ^= (JF_START | JF_SET);
214214117Sjamie				break;
215214117Sjamie			}
216214117Sjamie		}
217214117Sjamie		if (reset)
218214117Sjamie			dep_reset(dj);
219214117Sjamie		if (!((d->flags & DF_LIGHT) &&
220214117Sjamie		    (rev ? dj->jid < 0 : dj->jid > 0)))
221214117Sjamie			ndeps++;
222214117Sjamie	}
223214117Sjamie	if (ndeps == 0)
224214117Sjamie		return 0;
225216367Sjamie	requeue(j, &depend);
226214117Sjamie	return 1;
227214117Sjamie}
228214117Sjamie
229214117Sjamie/*
230214117Sjamie * Resolve any dependencies from a finished jail.
231214117Sjamie */
232214117Sjamievoid
233214117Sjamiedep_done(struct cfjail *j, unsigned flags)
234214117Sjamie{
235214117Sjamie	struct cfjail *dj;
236214117Sjamie	struct cfdepend *d;
237214117Sjamie	int depfrom, depto;
238214117Sjamie
239214117Sjamie	if (JF_DO_STOP(j->flags)) {
240214117Sjamie		depfrom = DEP_TO;
241214117Sjamie		depto = DEP_FROM;
242214117Sjamie	} else {
243214117Sjamie		depfrom = DEP_FROM;
244214117Sjamie		depto = DEP_TO;
245214117Sjamie	}
246214117Sjamie	STAILQ_FOREACH(d, &j->dep[depto], tq[depto]) {
247214117Sjamie		if ((d->flags & DF_SEEN) | (flags & ~d->flags & DF_LIGHT))
248214117Sjamie			continue;
249214117Sjamie		d->flags |= DF_SEEN;
250214117Sjamie		dj = d->j[depfrom];
251214117Sjamie		if (!(flags & DF_NOFAIL) && (j->flags & JF_FAILED) &&
252214117Sjamie		    (j->flags & (JF_OP_MASK | JF_DEPEND)) !=
253214117Sjamie		    (JF_SET | JF_DEPEND)) {
254214117Sjamie			if (!(dj->flags & (JF_DEPEND | JF_FAILED)) &&
255214117Sjamie			    verbose >= 0)
256214117Sjamie				jail_warnx(dj, "skipped");
257214117Sjamie			dj->flags |= JF_FAILED;
258214117Sjamie		}
259216367Sjamie		if (!--dj->ndeps && dj->queue == &depend)
260214117Sjamie			requeue(dj, &ready);
261214117Sjamie	}
262214117Sjamie}
263214117Sjamie
264214117Sjamie/*
265214117Sjamie * Count a jail's dependencies and mark them as unseen.
266214117Sjamie */
267214117Sjamievoid
268214117Sjamiedep_reset(struct cfjail *j)
269214117Sjamie{
270214117Sjamie	int depfrom;
271214117Sjamie	struct cfdepend *d;
272214117Sjamie
273214117Sjamie	depfrom = JF_DO_STOP(j->flags) ? DEP_TO : DEP_FROM;
274214117Sjamie	j->ndeps = 0;
275214117Sjamie	STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom])
276214117Sjamie		j->ndeps++;
277214117Sjamie}
278214117Sjamie
279214117Sjamie/*
280214117Sjamie * Find the next jail ready to do something.
281214117Sjamie */
282214117Sjamiestruct cfjail *
283214117Sjamienext_jail(void)
284214117Sjamie{
285214117Sjamie	struct cfjail *j;
286214117Sjamie
287214117Sjamie	if (!(j = next_proc(!TAILQ_EMPTY(&ready))) &&
288214117Sjamie	    (j = TAILQ_FIRST(&ready)) && JF_DO_STOP(j->flags) &&
289214117Sjamie	    (j = TAILQ_LAST(&ready, cfjails)) && !JF_DO_STOP(j->flags)) {
290214117Sjamie		TAILQ_FOREACH_REVERSE(j, &ready, cfjails, tq)
291214117Sjamie			if (JF_DO_STOP(j->flags))
292214117Sjamie				break;
293214117Sjamie	}
294214117Sjamie	if (j != NULL)
295214117Sjamie		requeue(j, &cfjails);
296214117Sjamie	return j;
297214117Sjamie}
298214117Sjamie
299214117Sjamie/*
300214117Sjamie * Set jails to the proper start state.
301214117Sjamie */
302214117Sjamieint
303231238Sjamiestart_state(const char *target, int docf, unsigned state, int running)
304214117Sjamie{
305214117Sjamie	struct iovec jiov[6];
306214117Sjamie	struct cfjail *j, *tj;
307214117Sjamie	int jid;
308214117Sjamie	char namebuf[MAXHOSTNAMELEN];
309214117Sjamie
310231238Sjamie	if (!target || (!docf && state != JF_STOP) ||
311231238Sjamie	    (!running && !strcmp(target, "*"))) {
312214117Sjamie		/*
313231238Sjamie		 * For a global wildcard (including no target specified),
314231238Sjamie		 * set the state on all jails and start with those that
315231238Sjamie		 * have no dependencies.
316214117Sjamie		 */
317214117Sjamie		TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
318231238Sjamie			j->flags = (j->flags & JF_FAILED) | state |
319231238Sjamie			    (docf ? JF_WILD : 0);
320214117Sjamie			dep_reset(j);
321216367Sjamie			requeue(j, j->ndeps ? &depend : &ready);
322214117Sjamie		}
323214117Sjamie	} else if (wild_jail_name(target)) {
324214117Sjamie		/*
325214117Sjamie		 * For targets specified singly, or with a non-global wildcard,
326214117Sjamie		 * set their state and call them ready (even if there are
327214117Sjamie		 * dependencies).  Leave everything else unqueued for now.
328214117Sjamie		 */
329214117Sjamie		if (running) {
330214117Sjamie			/*
331214117Sjamie			 * -R matches its wildcards against currently running
332214117Sjamie			 * jails, not against the config file.
333214117Sjamie			 */
334214117Sjamie			*(const void **)&jiov[0].iov_base = "lastjid";
335214117Sjamie			jiov[0].iov_len = sizeof("lastjid");
336214117Sjamie			jiov[1].iov_base = &jid;
337214117Sjamie			jiov[1].iov_len = sizeof(jid);
338214117Sjamie			*(const void **)&jiov[2].iov_base = "jid";
339214117Sjamie			jiov[2].iov_len = sizeof("jid");
340214117Sjamie			jiov[3].iov_base = &jid;
341214117Sjamie			jiov[3].iov_len = sizeof(jid);
342214117Sjamie			*(const void **)&jiov[4].iov_base = "name";
343214117Sjamie			jiov[4].iov_len = sizeof("name");
344214117Sjamie			jiov[5].iov_base = &namebuf;
345214117Sjamie			jiov[5].iov_len = sizeof(namebuf);
346214117Sjamie			for (jid = 0; jail_get(jiov, 6, 0) > 0; ) {
347214117Sjamie				if (wild_jail_match(namebuf, target)) {
348214117Sjamie					j = add_jail();
349214117Sjamie					j->name = estrdup(namebuf);
350214117Sjamie					j->jid = jid;
351214117Sjamie					j->flags = (j->flags & JF_FAILED) |
352214117Sjamie					    state | JF_WILD;
353214117Sjamie					dep_reset(j);
354214117Sjamie					requeue(j, &ready);
355214117Sjamie				}
356214117Sjamie			}
357214117Sjamie		} else {
358214117Sjamie			TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
359214117Sjamie				if (wild_jail_match(j->name, target)) {
360214117Sjamie					j->flags = (j->flags & JF_FAILED) |
361214117Sjamie					    state | JF_WILD;
362214117Sjamie					dep_reset(j);
363214117Sjamie					requeue(j, &ready);
364214117Sjamie				}
365214117Sjamie			}
366214117Sjamie		}
367214117Sjamie	} else {
368214117Sjamie		j = find_jail(target);
369214117Sjamie		if (j == NULL && state == JF_STOP) {
370214117Sjamie			/* Allow -[rR] to specify a currently running jail. */
371214117Sjamie			if ((jid = running_jid(target, JAIL_DYING)) > 0) {
372214117Sjamie				j = add_jail();
373214117Sjamie				j->name = estrdup(target);
374214117Sjamie				j->jid = jid;
375214117Sjamie			}
376214117Sjamie		}
377214117Sjamie		if (j == NULL) {
378214117Sjamie			warnx("\"%s\" not found", target);
379214117Sjamie			return -1;
380214117Sjamie		}
381214117Sjamie		j->flags = (j->flags & JF_FAILED) | state;
382214117Sjamie		dep_reset(j);
383214117Sjamie		requeue(j, &ready);
384214117Sjamie	}
385214117Sjamie	return 0;
386214117Sjamie}
387214117Sjamie
388214117Sjamie/*
389214117Sjamie * Move a jail to a new list.
390214117Sjamie */
391214117Sjamievoid
392214117Sjamierequeue(struct cfjail *j, struct cfjails *queue)
393214117Sjamie{
394214117Sjamie	if (j->queue != queue) {
395214117Sjamie		TAILQ_REMOVE(j->queue, j, tq);
396214117Sjamie		TAILQ_INSERT_TAIL(queue, j, tq);
397214117Sjamie		j->queue = queue;
398214117Sjamie	}
399214117Sjamie}
400214117Sjamie
401214117Sjamie/*
402214117Sjamie * Add a dependency edge between two jails.
403214117Sjamie */
404214117Sjamiestatic void
405214117Sjamiedep_add(struct cfjail *from, struct cfjail *to, unsigned flags)
406214117Sjamie{
407214117Sjamie	struct cfdepend *d;
408214117Sjamie
409214117Sjamie	d = emalloc(sizeof(struct cfdepend));
410214117Sjamie	d->flags = flags;
411214117Sjamie	d->j[DEP_FROM] = from;
412214117Sjamie	d->j[DEP_TO] = to;
413214117Sjamie	STAILQ_INSERT_TAIL(&from->dep[DEP_FROM], d, tq[DEP_FROM]);
414214117Sjamie	STAILQ_INSERT_TAIL(&to->dep[DEP_TO], d, tq[DEP_TO]);
415214117Sjamie}
416214117Sjamie
417214117Sjamie/*
418214117Sjamie * Compare jail pointers for qsort/bsearch.
419214117Sjamie */
420214117Sjamiestatic int
421214117Sjamiecmp_jailptr(const void *a, const void *b)
422214117Sjamie{
423214117Sjamie	return strcmp((*((struct cfjail * const *)a))->name,
424214117Sjamie	    ((*(struct cfjail * const *)b))->name);
425214117Sjamie}
426214117Sjamie
427214117Sjamiestatic int
428214117Sjamiecmp_jailptr_name(const void *a, const void *b)
429214117Sjamie{
430214117Sjamie	return strcmp((const char *)a, ((*(struct cfjail * const *)b))->name);
431214117Sjamie}
432214117Sjamie
433214117Sjamie/*
434214117Sjamie * Find a jail object by name.
435214117Sjamie */
436214117Sjamiestatic struct cfjail *
437214117Sjamiefind_jail(const char *name)
438214117Sjamie{
439214117Sjamie	struct cfjail **jp;
440214117Sjamie
441214117Sjamie	jp = bsearch(name, jails_byname, njails, sizeof(struct cfjail *),
442214117Sjamie	    cmp_jailptr_name);
443214117Sjamie	return jp ? *jp : NULL;
444214117Sjamie}
445214117Sjamie
446214117Sjamie/*
447214117Sjamie * Return the named jail's jid if it is running, and -1 if it isn't.
448214117Sjamie */
449214117Sjamiestatic int
450214117Sjamierunning_jid(const char *name, int flags)
451214117Sjamie{
452214117Sjamie	struct iovec jiov[2];
453214117Sjamie	char *ep;
454214117Sjamie	int jid;
455214117Sjamie
456214117Sjamie	if ((jid = strtol(name, &ep, 10)) && !*ep) {
457214117Sjamie		*(const void **)&jiov[0].iov_base = "jid";
458214117Sjamie		jiov[0].iov_len = sizeof("jid");
459214117Sjamie		jiov[1].iov_base = &jid;
460214117Sjamie		jiov[1].iov_len = sizeof(jid);
461214117Sjamie	} else {
462214117Sjamie		*(const void **)&jiov[0].iov_base = "name";
463214117Sjamie		jiov[0].iov_len = sizeof("name");
464214117Sjamie		jiov[1].iov_len = strlen(name) + 1;
465214117Sjamie		jiov[1].iov_base = alloca(jiov[1].iov_len);
466214117Sjamie		strcpy(jiov[1].iov_base, name);
467214117Sjamie	}
468214117Sjamie	return jail_get(jiov, 2, flags);
469214117Sjamie}
470