Deleted Added
full compact
state.c (216367) state.c (223188)
1/*-
2 * Copyright (c) 2010 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>
1/*-
2 * Copyright (c) 2010 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: projects/jailconf/usr.sbin/jail/state.c 216367 2010-12-10 23:57:55Z jamie $");
28__FBSDID("$FreeBSD: projects/jailconf/usr.sbin/jail/state.c 223188 2011-06-17 16:06:13Z jamie $");
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])) {
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 STAILQ_FOREACH(s, &p->val, tq) {
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])) {
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 STAILQ_FOREACH(s, &p->val, tq) {
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);
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, 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 ? &depend : &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}
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);
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, 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 ? &depend : &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}