state.c revision 214117
155714Skris/*-
255714Skris * Copyright (c) 2010 James Gritton
355714Skris * All rights reserved.
455714Skris *
555714Skris * Redistribution and use in source and binary forms, with or without
655714Skris * modification, are permitted provided that the following conditions
755714Skris * are met:
8280304Sjkim * 1. Redistributions of source code must retain the above copyright
955714Skris *    notice, this list of conditions and the following disclaimer.
1055714Skris * 2. Redistributions in binary form must reproduce the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer in the
1255714Skris *    documentation and/or other materials provided with the distribution.
1355714Skris *
1455714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15280304Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1655714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22280304Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2455714Skris * SUCH DAMAGE.
2555714Skris */
2655714Skris
2755714Skris#include <sys/cdefs.h>
2855714Skris__FBSDID("$FreeBSD: projects/jailconf/usr.sbin/jail/state.c 214117 2010-10-20 20:42:33Z jamie $");
2955714Skris
3055714Skris#include <sys/uio.h>
3155714Skris
3255714Skris#include <err.h>
3355714Skris#include <stdlib.h>
3455714Skris#include <string.h>
3555714Skris
3655714Skris#include "jailp.h"
37280304Sjkim
3855714Skrisstruct cfjails ready = TAILQ_HEAD_INITIALIZER(ready);
3955714Skrisstruct cfjails waiting = TAILQ_HEAD_INITIALIZER(waiting);
40280304Sjkim
4155714Skrisstatic void dep_add(struct cfjail *from, struct cfjail *to, unsigned flags);
4255714Skrisstatic int cmp_jailptr(const void *a, const void *b);
4355714Skrisstatic int cmp_jailptr_name(const void *a, const void *b);
4455714Skrisstatic struct cfjail *find_jail(const char *name);
4555714Skrisstatic int running_jid(const char *name, int flags);
4655714Skris
4755714Skrisstatic struct cfjail **jails_byname;
4855714Skrisstatic size_t njails;
4955714Skris
5055714Skris/*
5155714Skris * Set up jail dependency lists.
52280304Sjkim */
5355714Skrisvoid
5455714Skrisdep_setup(int docf)
5555714Skris{
5655714Skris	struct cfjail *j, *dj;
5755714Skris	struct cfparam *p;
5855714Skris	struct cfstring *s;
5955714Skris	struct cfdepend *d;
6055714Skris	const char *cs;
6155714Skris	char *pname;
6255714Skris	size_t plen;
6355714Skris	int error, deps, ldeps;
6455714Skris
6555714Skris	if (!docf) {
6655714Skris		/*
6755714Skris		 * With no config file, let "depend" for a single jail
6855714Skris		 * look at currently running jails.
69109998Smarkm		 */
7055714Skris		if ((j = TAILQ_FIRST(&cfjails)) &&
7155714Skris		    (p = j->intparams[IP_DEPEND])) {
72280304Sjkim			STAILQ_FOREACH(s, &p->val, tq) {
73280304Sjkim				if (running_jid(s->s, 0) < 0) {
74280304Sjkim					warnx("depends on nonexistent jail "
75280304Sjkim					    "\"%s\"", s->s);
76280304Sjkim					j->flags |= JF_FAILED;
77280304Sjkim				}
78280304Sjkim			}
79280304Sjkim		}
80280304Sjkim		return;
81280304Sjkim	}
82280304Sjkim
83280304Sjkim	njails = 0;
84280304Sjkim	TAILQ_FOREACH(j, &cfjails, tq)
8555714Skris		njails++;
8655714Skris	jails_byname = emalloc(njails * sizeof(struct cfjail *));
87280304Sjkim	njails = 0;
88280304Sjkim	TAILQ_FOREACH(j, &cfjails, tq)
89280304Sjkim		jails_byname[njails++] = j;
9055714Skris	qsort(jails_byname, njails, sizeof(struct cfjail *), cmp_jailptr);
91280304Sjkim	error = 0;
92280304Sjkim	deps = 0;
93280304Sjkim	ldeps = 0;
94280304Sjkim	plen = 0;
95280304Sjkim	pname = NULL;
9655714Skris	TAILQ_FOREACH(j, &cfjails, tq) {
97280304Sjkim		if (j->flags & JF_FAILED)
98280304Sjkim			continue;
99280304Sjkim		if ((p = j->intparams[IP_DEPEND])) {
100280304Sjkim			STAILQ_FOREACH(s, &p->val, tq) {
101280304Sjkim				dj = find_jail(s->s);
102280304Sjkim				if (dj != NULL) {
103280304Sjkim					deps++;
104109998Smarkm					dep_add(j, dj, 0);
105280304Sjkim				} else {
106280304Sjkim					jail_warnx(j,
107280304Sjkim					    "depends on undefined jail \"%s\"",
108280304Sjkim					    s->s);
109109998Smarkm					j->flags |= JF_FAILED;
110280304Sjkim				}
111280304Sjkim			}
112280304Sjkim		}
113280304Sjkim		/* A jail has an implied dependency on its parent. */
114280304Sjkim		if ((cs = strrchr(j->name, '.')))
115280304Sjkim		{
116280304Sjkim			if (plen < (size_t)(cs - j->name + 1)) {
117280304Sjkim				plen = (cs - j->name) + 1;
118280304Sjkim				pname = erealloc(pname, plen);
119280304Sjkim			}
120280304Sjkim			strlcpy(pname, j->name, plen);
121280304Sjkim			dj = find_jail(pname);
122280304Sjkim			if (dj != NULL) {
123280304Sjkim				ldeps++;
12455714Skris				dep_add(j, dj, DF_LIGHT);
12555714Skris			}
126280304Sjkim		}
127280304Sjkim	}
128280304Sjkim
129280304Sjkim	/* Look for dependency loops. */
130280304Sjkim	if (deps && (deps > 1 || ldeps)) {
13155714Skris		(void)start_state(NULL, 0, 0);
132280304Sjkim		while ((j = TAILQ_FIRST(&ready))) {
133280304Sjkim			requeue(j, &cfjails);
134280304Sjkim			dep_done(j, DF_NOFAIL);
13555714Skris		}
136280304Sjkim		while ((j = TAILQ_FIRST(&waiting)) != NULL) {
137280304Sjkim			jail_warnx(j, "dependency loop");
138280304Sjkim			j->flags |= JF_FAILED;
139280304Sjkim			do {
14055714Skris				requeue(j, &cfjails);
141280304Sjkim				dep_done(j, DF_NOFAIL);
142280304Sjkim			} while ((j = TAILQ_FIRST(&ready)));
143280304Sjkim		}
144280304Sjkim		TAILQ_FOREACH(j, &cfjails, tq)
145280304Sjkim			STAILQ_FOREACH(d, &j->dep[DEP_FROM], tq[DEP_FROM])
146280304Sjkim				d->flags &= ~DF_SEEN;
147280304Sjkim	}
148280304Sjkim	if (pname != NULL)
149280304Sjkim		free(pname);
150280304Sjkim}
151280304Sjkim
152280304Sjkim/*
153280304Sjkim * Return if a jail has dependencies.
154280304Sjkim */
155280304Sjkimint
156280304Sjkimdep_check(struct cfjail *j)
157280304Sjkim{
158280304Sjkim	int reset, depfrom, depto, ndeps, rev;
159280304Sjkim	struct cfjail *dj;
160280304Sjkim	struct cfdepend *d;
161280304Sjkim
162280304Sjkim	static int bits[] = { 0, 1, 1, 2, 1, 2, 2, 3 };
163280304Sjkim
164280304Sjkim	if (j->ndeps == 0)
165280304Sjkim		return 0;
166280304Sjkim	ndeps = 0;
167280304Sjkim	if ((rev = JF_DO_STOP(j->flags))) {
168280304Sjkim		depfrom = DEP_TO;
169280304Sjkim		depto = DEP_FROM;
170280304Sjkim	} else {
171280304Sjkim		depfrom = DEP_FROM;
172280304Sjkim		depto = DEP_TO;
173280304Sjkim	}
174280304Sjkim	STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom]) {
175280304Sjkim		if (d->flags & DF_SEEN)
176280304Sjkim			continue;
177280304Sjkim		dj = d->j[depto];
178280304Sjkim		if (dj->flags & JF_FAILED) {
179280304Sjkim			if (!(j->flags & (JF_DEPEND | JF_FAILED)) &&
180280304Sjkim			    verbose >= 0)
181280304Sjkim				jail_warnx(j, "skipped");
182280304Sjkim			j->flags |= JF_FAILED;
18355714Skris			continue;
18455714Skris		}
185280304Sjkim		/*
186280304Sjkim		 * The dependee's state may be set (or changed) as a result of
187280304Sjkim		 * being in a dependency it wasn't in earlier.
188280304Sjkim		 */
189280304Sjkim		reset = 0;
19055714Skris		if (bits[dj->flags & JF_OP_MASK] <= 1) {
191280304Sjkim			if (!(dj->flags & JF_OP_MASK)) {
192280304Sjkim				reset = 1;
193280304Sjkim				dj->flags |= JF_DEPEND;
19455714Skris				requeue(dj, &ready);
195280304Sjkim			}
196280304Sjkim			/* Set or change the dependee's state. */
197280304Sjkim			switch (j->flags & JF_OP_MASK) {
198280304Sjkim			case JF_START:
19955714Skris				dj->flags |= JF_START;
200280304Sjkim				break;
201280304Sjkim			case JF_SET:
202280304Sjkim				if (!(dj->flags & JF_OP_MASK))
203280304Sjkim					dj->flags |= JF_SET;
204280304Sjkim				else if (dj->flags & JF_STOP)
205280304Sjkim					dj->flags |= JF_START;
206280304Sjkim				break;
207280304Sjkim			case JF_STOP:
208280304Sjkim			case JF_RESTART:
209280304Sjkim				if (!(dj->flags & JF_STOP))
210280304Sjkim					reset = 1;
211280304Sjkim				dj->flags |= JF_STOP;
212280304Sjkim				if (dj->flags & JF_SET)
213280304Sjkim					dj->flags ^= (JF_START | JF_SET);
214280304Sjkim				break;
215280304Sjkim			}
216280304Sjkim		}
217280304Sjkim		if (reset)
218280304Sjkim			dep_reset(dj);
219280304Sjkim		if (!((d->flags & DF_LIGHT) &&
220280304Sjkim		    (rev ? dj->jid < 0 : dj->jid > 0)))
221280304Sjkim			ndeps++;
222280304Sjkim	}
223280304Sjkim	if (ndeps == 0)
224280304Sjkim		return 0;
225280304Sjkim	requeue(j, &waiting);
226280304Sjkim	return 1;
227280304Sjkim}
228280304Sjkim
229280304Sjkim/*
230280304Sjkim * Resolve any dependencies from a finished jail.
231280304Sjkim */
232280304Sjkimvoid
233280304Sjkimdep_done(struct cfjail *j, unsigned flags)
234280304Sjkim{
235280304Sjkim	struct cfjail *dj;
236280304Sjkim	struct cfdepend *d;
237280304Sjkim	int depfrom, depto;
238280304Sjkim
239280304Sjkim	if (JF_DO_STOP(j->flags)) {
240280304Sjkim		depfrom = DEP_TO;
241280304Sjkim		depto = DEP_FROM;
24255714Skris	} else {
24359191Skris		depfrom = DEP_FROM;
24459191Skris		depto = DEP_TO;
245280304Sjkim	}
246280304Sjkim	STAILQ_FOREACH(d, &j->dep[depto], tq[depto]) {
247280304Sjkim		if ((d->flags & DF_SEEN) | (flags & ~d->flags & DF_LIGHT))
248280304Sjkim			continue;
249280304Sjkim		d->flags |= DF_SEEN;
250280304Sjkim		dj = d->j[depfrom];
251280304Sjkim		if (!(flags & DF_NOFAIL) && (j->flags & JF_FAILED) &&
252280304Sjkim		    (j->flags & (JF_OP_MASK | JF_DEPEND)) !=
253280304Sjkim		    (JF_SET | JF_DEPEND)) {
254280304Sjkim			if (!(dj->flags & (JF_DEPEND | JF_FAILED)) &&
255280304Sjkim			    verbose >= 0)
256280304Sjkim				jail_warnx(dj, "skipped");
257280304Sjkim			dj->flags |= JF_FAILED;
258280304Sjkim		}
259280304Sjkim		if (!--dj->ndeps && dj->queue == &waiting)
260280304Sjkim			requeue(dj, &ready);
261280304Sjkim	}
262280304Sjkim}
263280304Sjkim
264280304Sjkim/*
265280304Sjkim * Count a jail's dependencies and mark them as unseen.
266280304Sjkim */
267280304Sjkimvoid
268280304Sjkimdep_reset(struct cfjail *j)
269280304Sjkim{
270280304Sjkim	int depfrom;
271280304Sjkim	struct cfdepend *d;
272280304Sjkim
273280304Sjkim	depfrom = JF_DO_STOP(j->flags) ? DEP_TO : DEP_FROM;
274280304Sjkim	j->ndeps = 0;
27559191Skris	STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom])
27659191Skris		j->ndeps++;
277280304Sjkim}
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, 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 || (!running && !strcmp(target, "*"))) {
311		/*
312		 * If there's no target specified, set the state on all jails,
313		 * and start with those that have no dependencies.
314		 */
315		TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
316			j->flags = (j->flags & JF_FAILED) | state | JF_WILD;
317			dep_reset(j);
318			requeue(j, j->ndeps ? &waiting : &ready);
319		}
320	} else if (wild_jail_name(target)) {
321		/*
322		 * For targets specified singly, or with a non-global wildcard,
323		 * set their state and call them ready (even if there are
324		 * dependencies).  Leave everything else unqueued for now.
325		 */
326		if (running) {
327			/*
328			 * -R matches its wildcards against currently running
329			 * jails, not against the config file.
330			 */
331			*(const void **)&jiov[0].iov_base = "lastjid";
332			jiov[0].iov_len = sizeof("lastjid");
333			jiov[1].iov_base = &jid;
334			jiov[1].iov_len = sizeof(jid);
335			*(const void **)&jiov[2].iov_base = "jid";
336			jiov[2].iov_len = sizeof("jid");
337			jiov[3].iov_base = &jid;
338			jiov[3].iov_len = sizeof(jid);
339			*(const void **)&jiov[4].iov_base = "name";
340			jiov[4].iov_len = sizeof("name");
341			jiov[5].iov_base = &namebuf;
342			jiov[5].iov_len = sizeof(namebuf);
343			for (jid = 0; jail_get(jiov, 6, 0) > 0; ) {
344				if (wild_jail_match(namebuf, target)) {
345					j = add_jail();
346					j->name = estrdup(namebuf);
347					j->jid = jid;
348					j->flags = (j->flags & JF_FAILED) |
349					    state | JF_WILD;
350					dep_reset(j);
351					requeue(j, &ready);
352				}
353			}
354		} else {
355			TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
356				if (wild_jail_match(j->name, target)) {
357					j->flags = (j->flags & JF_FAILED) |
358					    state | JF_WILD;
359					dep_reset(j);
360					requeue(j, &ready);
361				}
362			}
363		}
364	} else {
365		j = find_jail(target);
366		if (j == NULL && state == JF_STOP) {
367			/* Allow -[rR] to specify a currently running jail. */
368			if ((jid = running_jid(target, JAIL_DYING)) > 0) {
369				j = add_jail();
370				j->name = estrdup(target);
371				j->jid = jid;
372			}
373		}
374		if (j == NULL) {
375			warnx("\"%s\" not found", target);
376			return -1;
377		}
378		j->flags = (j->flags & JF_FAILED) | state;
379		dep_reset(j);
380		requeue(j, &ready);
381	}
382	return 0;
383}
384
385/*
386 * Move a jail to a new list.
387 */
388void
389requeue(struct cfjail *j, struct cfjails *queue)
390{
391	if (j->queue != queue) {
392		TAILQ_REMOVE(j->queue, j, tq);
393		TAILQ_INSERT_TAIL(queue, j, tq);
394		j->queue = queue;
395	}
396}
397
398/*
399 * Add a dependency edge between two jails.
400 */
401static void
402dep_add(struct cfjail *from, struct cfjail *to, unsigned flags)
403{
404	struct cfdepend *d;
405
406	d = emalloc(sizeof(struct cfdepend));
407	d->flags = flags;
408	d->j[DEP_FROM] = from;
409	d->j[DEP_TO] = to;
410	STAILQ_INSERT_TAIL(&from->dep[DEP_FROM], d, tq[DEP_FROM]);
411	STAILQ_INSERT_TAIL(&to->dep[DEP_TO], d, tq[DEP_TO]);
412}
413
414/*
415 * Compare jail pointers for qsort/bsearch.
416 */
417static int
418cmp_jailptr(const void *a, const void *b)
419{
420	return strcmp((*((struct cfjail * const *)a))->name,
421	    ((*(struct cfjail * const *)b))->name);
422}
423
424static int
425cmp_jailptr_name(const void *a, const void *b)
426{
427	return strcmp((const char *)a, ((*(struct cfjail * const *)b))->name);
428}
429
430/*
431 * Find a jail object by name.
432 */
433static struct cfjail *
434find_jail(const char *name)
435{
436	struct cfjail **jp;
437
438	jp = bsearch(name, jails_byname, njails, sizeof(struct cfjail *),
439	    cmp_jailptr_name);
440	return jp ? *jp : NULL;
441}
442
443/*
444 * Return the named jail's jid if it is running, and -1 if it isn't.
445 */
446static int
447running_jid(const char *name, int flags)
448{
449	struct iovec jiov[2];
450	char *ep;
451	int jid;
452
453	if ((jid = strtol(name, &ep, 10)) && !*ep) {
454		*(const void **)&jiov[0].iov_base = "jid";
455		jiov[0].iov_len = sizeof("jid");
456		jiov[1].iov_base = &jid;
457		jiov[1].iov_len = sizeof(jid);
458	} else {
459		*(const void **)&jiov[0].iov_base = "name";
460		jiov[0].iov_len = sizeof("name");
461		jiov[1].iov_len = strlen(name) + 1;
462		jiov[1].iov_base = alloca(jiov[1].iov_len);
463		strcpy(jiov[1].iov_base, name);
464	}
465	return jail_get(jiov, 2, flags);
466}
467