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