1/*-
2 * Copyright (c) 2011 James Gritton
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/uio.h>
31
32#include <err.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include "jailp.h"
37
38struct cfjails ready = TAILQ_HEAD_INITIALIZER(ready);
39struct cfjails depend = TAILQ_HEAD_INITIALIZER(depend);
40
41static void dep_add(struct cfjail *from, struct cfjail *to, unsigned flags);
42static int cmp_jailptr(const void *a, const void *b);
43static int cmp_jailptr_name(const void *a, const void *b);
44static struct cfjail *find_jail(const char *name);
45static int running_jid(const char *name, int flags);
46
47static struct cfjail **jails_byname;
48static size_t njails;
49
50/*
51 * Set up jail dependency lists.
52 */
53void
54dep_setup(int docf)
55{
56	struct cfjail *j, *dj;
57	struct cfparam *p;
58	struct cfstring *s;
59	struct cfdepend *d;
60	const char *cs;
61	char *pname;
62	size_t plen;
63	int error, deps, ldeps;
64
65	if (!docf) {
66		/*
67		 * With no config file, let "depend" for a single jail
68		 * look at currently running jails.
69		 */
70		if ((j = TAILQ_FIRST(&cfjails)) &&
71		    (p = j->intparams[IP_DEPEND])) {
72			TAILQ_FOREACH(s, &p->val, tq) {
73				if (running_jid(s->s, 0) < 0) {
74					warnx("depends on nonexistent jail "
75					    "\"%s\"", s->s);
76					j->flags |= JF_FAILED;
77				}
78			}
79		}
80		return;
81	}
82
83	njails = 0;
84	TAILQ_FOREACH(j, &cfjails, tq)
85		njails++;
86	jails_byname = emalloc(njails * sizeof(struct cfjail *));
87	njails = 0;
88	TAILQ_FOREACH(j, &cfjails, tq)
89		jails_byname[njails++] = j;
90	qsort(jails_byname, njails, sizeof(struct cfjail *), cmp_jailptr);
91	error = 0;
92	deps = 0;
93	ldeps = 0;
94	plen = 0;
95	pname = NULL;
96	TAILQ_FOREACH(j, &cfjails, tq) {
97		if (j->flags & JF_FAILED)
98			continue;
99		if ((p = j->intparams[IP_DEPEND])) {
100			TAILQ_FOREACH(s, &p->val, tq) {
101				dj = find_jail(s->s);
102				if (dj != NULL) {
103					deps++;
104					dep_add(j, dj, 0);
105				} else {
106					jail_warnx(j,
107					    "depends on undefined jail \"%s\"",
108					    s->s);
109					j->flags |= JF_FAILED;
110				}
111			}
112		}
113		/* A jail has an implied dependency on its parent. */
114		if ((cs = strrchr(j->name, '.')))
115		{
116			if (plen < (size_t)(cs - j->name + 1)) {
117				plen = (cs - j->name) + 1;
118				pname = erealloc(pname, plen);
119			}
120			strlcpy(pname, j->name, plen);
121			dj = find_jail(pname);
122			if (dj != NULL) {
123				ldeps++;
124				dep_add(j, dj, DF_LIGHT);
125			}
126		}
127	}
128
129	/* Look for dependency loops. */
130	if (deps && (deps > 1 || ldeps)) {
131		(void)start_state(NULL, 0, 0, 0);
132		while ((j = TAILQ_FIRST(&ready))) {
133			requeue(j, &cfjails);
134			dep_done(j, DF_NOFAIL);
135		}
136		while ((j = TAILQ_FIRST(&depend)) != NULL) {
137			jail_warnx(j, "dependency loop");
138			j->flags |= JF_FAILED;
139			do {
140				requeue(j, &cfjails);
141				dep_done(j, DF_NOFAIL);
142			} while ((j = TAILQ_FIRST(&ready)));
143		}
144		TAILQ_FOREACH(j, &cfjails, tq)
145			STAILQ_FOREACH(d, &j->dep[DEP_FROM], tq[DEP_FROM])
146				d->flags &= ~DF_SEEN;
147	}
148	if (pname != NULL)
149		free(pname);
150}
151
152/*
153 * Return if a jail has dependencies.
154 */
155int
156dep_check(struct cfjail *j)
157{
158	int reset, depfrom, depto, ndeps, rev;
159	struct cfjail *dj;
160	struct cfdepend *d;
161
162	static int bits[] = { 0, 1, 1, 2, 1, 2, 2, 3 };
163
164	if (j->ndeps == 0)
165		return 0;
166	ndeps = 0;
167	if ((rev = JF_DO_STOP(j->flags))) {
168		depfrom = DEP_TO;
169		depto = DEP_FROM;
170	} else {
171		depfrom = DEP_FROM;
172		depto = DEP_TO;
173	}
174	STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom]) {
175		if (d->flags & DF_SEEN)
176			continue;
177		dj = d->j[depto];
178		if (dj->flags & JF_FAILED) {
179			if (!(j->flags & (JF_DEPEND | JF_FAILED)) &&
180			    verbose >= 0)
181				jail_warnx(j, "skipped");
182			j->flags |= JF_FAILED;
183			continue;
184		}
185		/*
186		 * The dependee's state may be set (or changed) as a result of
187		 * being in a dependency it wasn't in earlier.
188		 */
189		reset = 0;
190		if (bits[dj->flags & JF_OP_MASK] <= 1) {
191			if (!(dj->flags & JF_OP_MASK)) {
192				reset = 1;
193				dj->flags |= JF_DEPEND;
194				requeue(dj, &ready);
195			}
196			/* Set or change the dependee's state. */
197			switch (j->flags & JF_OP_MASK) {
198			case JF_START:
199				dj->flags |= JF_START;
200				break;
201			case JF_SET:
202				if (!(dj->flags & JF_OP_MASK))
203					dj->flags |= JF_SET;
204				else if (dj->flags & JF_STOP)
205					dj->flags |= JF_START;
206				break;
207			case JF_STOP:
208			case JF_RESTART:
209				if (!(dj->flags & JF_STOP))
210					reset = 1;
211				dj->flags |= JF_STOP;
212				if (dj->flags & JF_SET)
213					dj->flags ^= (JF_START | JF_SET);
214				break;
215			}
216		}
217		if (reset)
218			dep_reset(dj);
219		if (!((d->flags & DF_LIGHT) &&
220		    (rev ? dj->jid < 0 : dj->jid > 0)))
221			ndeps++;
222	}
223	if (ndeps == 0)
224		return 0;
225	requeue(j, &depend);
226	return 1;
227}
228
229/*
230 * Resolve any dependencies from a finished jail.
231 */
232void
233dep_done(struct cfjail *j, unsigned flags)
234{
235	struct cfjail *dj;
236	struct cfdepend *d;
237	int depfrom, depto;
238
239	if (JF_DO_STOP(j->flags)) {
240		depfrom = DEP_TO;
241		depto = DEP_FROM;
242	} else {
243		depfrom = DEP_FROM;
244		depto = DEP_TO;
245	}
246	STAILQ_FOREACH(d, &j->dep[depto], tq[depto]) {
247		if ((d->flags & DF_SEEN) | (flags & ~d->flags & DF_LIGHT))
248			continue;
249		d->flags |= DF_SEEN;
250		dj = d->j[depfrom];
251		if (!(flags & DF_NOFAIL) && (j->flags & JF_FAILED) &&
252		    (j->flags & (JF_OP_MASK | JF_DEPEND)) !=
253		    (JF_SET | JF_DEPEND)) {
254			if (!(dj->flags & (JF_DEPEND | JF_FAILED)) &&
255			    verbose >= 0)
256				jail_warnx(dj, "skipped");
257			dj->flags |= JF_FAILED;
258		}
259		if (!--dj->ndeps && dj->queue == &depend)
260			requeue(dj, &ready);
261	}
262}
263
264/*
265 * Count a jail's dependencies and mark them as unseen.
266 */
267void
268dep_reset(struct cfjail *j)
269{
270	int depfrom;
271	struct cfdepend *d;
272
273	depfrom = JF_DO_STOP(j->flags) ? DEP_TO : DEP_FROM;
274	j->ndeps = 0;
275	STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom])
276		j->ndeps++;
277}
278
279/*
280 * Find the next jail ready to do something.
281 */
282struct cfjail *
283next_jail(void)
284{
285	struct cfjail *j;
286
287	if (!(j = next_proc(!TAILQ_EMPTY(&ready))) &&
288	    (j = TAILQ_FIRST(&ready)) && JF_DO_STOP(j->flags) &&
289	    (j = TAILQ_LAST(&ready, cfjails)) && !JF_DO_STOP(j->flags)) {
290		TAILQ_FOREACH_REVERSE(j, &ready, cfjails, tq)
291			if (JF_DO_STOP(j->flags))
292				break;
293	}
294	if (j != NULL)
295		requeue(j, &cfjails);
296	return j;
297}
298
299/*
300 * Set jails to the proper start state.
301 */
302int
303start_state(const char *target, int docf, unsigned state, int running)
304{
305	struct iovec jiov[6];
306	struct cfjail *j, *tj;
307	int jid;
308	char namebuf[MAXHOSTNAMELEN];
309
310	if (!target || (!docf && state != JF_STOP) ||
311	    (!running && !strcmp(target, "*"))) {
312		/*
313		 * For a global wildcard (including no target specified),
314		 * set the state on all jails and start with those that
315		 * have no dependencies.
316		 */
317		TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
318			j->flags = (j->flags & JF_FAILED) | state |
319			    (docf ? JF_WILD : 0);
320			dep_reset(j);
321			requeue(j, j->ndeps ? &depend : &ready);
322		}
323	} else if (wild_jail_name(target)) {
324		/*
325		 * For targets specified singly, or with a non-global wildcard,
326		 * set their state and call them ready (even if there are
327		 * dependencies).  Leave everything else unqueued for now.
328		 */
329		if (running) {
330			/*
331			 * -R matches its wildcards against currently running
332			 * jails, not against the config file.
333			 */
334			*(const void **)&jiov[0].iov_base = "lastjid";
335			jiov[0].iov_len = sizeof("lastjid");
336			jiov[1].iov_base = &jid;
337			jiov[1].iov_len = sizeof(jid);
338			*(const void **)&jiov[2].iov_base = "jid";
339			jiov[2].iov_len = sizeof("jid");
340			jiov[3].iov_base = &jid;
341			jiov[3].iov_len = sizeof(jid);
342			*(const void **)&jiov[4].iov_base = "name";
343			jiov[4].iov_len = sizeof("name");
344			jiov[5].iov_base = &namebuf;
345			jiov[5].iov_len = sizeof(namebuf);
346			for (jid = 0; jail_get(jiov, 6, 0) > 0; ) {
347				if (wild_jail_match(namebuf, target)) {
348					j = add_jail();
349					j->name = estrdup(namebuf);
350					j->jid = jid;
351					j->flags = (j->flags & JF_FAILED) |
352					    state | JF_WILD;
353					dep_reset(j);
354					requeue(j, &ready);
355				}
356			}
357		} else {
358			TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
359				if (wild_jail_match(j->name, target)) {
360					j->flags = (j->flags & JF_FAILED) |
361					    state | JF_WILD;
362					dep_reset(j);
363					requeue(j, &ready);
364				}
365			}
366		}
367	} else {
368		j = find_jail(target);
369		if (j == NULL && state == JF_STOP) {
370			/* Allow -[rR] to specify a currently running jail. */
371			if ((jid = running_jid(target, JAIL_DYING)) > 0) {
372				j = add_jail();
373				j->name = estrdup(target);
374				j->jid = jid;
375			}
376		}
377		if (j == NULL) {
378			warnx("\"%s\" not found", target);
379			return -1;
380		}
381		j->flags = (j->flags & JF_FAILED) | state;
382		dep_reset(j);
383		requeue(j, &ready);
384	}
385	return 0;
386}
387
388/*
389 * Move a jail to a new list.
390 */
391void
392requeue(struct cfjail *j, struct cfjails *queue)
393{
394	if (j->queue != queue) {
395		TAILQ_REMOVE(j->queue, j, tq);
396		TAILQ_INSERT_TAIL(queue, j, tq);
397		j->queue = queue;
398	}
399}
400
401/*
402 * Add a dependency edge between two jails.
403 */
404static void
405dep_add(struct cfjail *from, struct cfjail *to, unsigned flags)
406{
407	struct cfdepend *d;
408
409	d = emalloc(sizeof(struct cfdepend));
410	d->flags = flags;
411	d->j[DEP_FROM] = from;
412	d->j[DEP_TO] = to;
413	STAILQ_INSERT_TAIL(&from->dep[DEP_FROM], d, tq[DEP_FROM]);
414	STAILQ_INSERT_TAIL(&to->dep[DEP_TO], d, tq[DEP_TO]);
415}
416
417/*
418 * Compare jail pointers for qsort/bsearch.
419 */
420static int
421cmp_jailptr(const void *a, const void *b)
422{
423	return strcmp((*((struct cfjail * const *)a))->name,
424	    ((*(struct cfjail * const *)b))->name);
425}
426
427static int
428cmp_jailptr_name(const void *a, const void *b)
429{
430	return strcmp((const char *)a, ((*(struct cfjail * const *)b))->name);
431}
432
433/*
434 * Find a jail object by name.
435 */
436static struct cfjail *
437find_jail(const char *name)
438{
439	struct cfjail **jp;
440
441	jp = bsearch(name, jails_byname, njails, sizeof(struct cfjail *),
442	    cmp_jailptr_name);
443	return jp ? *jp : NULL;
444}
445
446/*
447 * Return the named jail's jid if it is running, and -1 if it isn't.
448 */
449static int
450running_jid(const char *name, int flags)
451{
452	struct iovec jiov[2];
453	char *ep;
454	int jid;
455
456	if ((jid = strtol(name, &ep, 10)) && !*ep) {
457		*(const void **)&jiov[0].iov_base = "jid";
458		jiov[0].iov_len = sizeof("jid");
459		jiov[1].iov_base = &jid;
460		jiov[1].iov_len = sizeof(jid);
461	} else {
462		*(const void **)&jiov[0].iov_base = "name";
463		jiov[0].iov_len = sizeof("name");
464		jiov[1].iov_len = strlen(name) + 1;
465		jiov[1].iov_base = alloca(jiov[1].iov_len);
466		strcpy(jiov[1].iov_base, name);
467	}
468	return jail_get(jiov, 2, flags);
469}
470