]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/jail/state.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / jail / state.c
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
38 struct cfjails ready = TAILQ_HEAD_INITIALIZER(ready);
39 struct cfjails depend = TAILQ_HEAD_INITIALIZER(depend);
40
41 static void dep_add(struct cfjail *from, struct cfjail *to, unsigned flags);
42 static int cmp_jailptr(const void *a, const void *b);
43 static int cmp_jailptr_name(const void *a, const void *b);
44 static struct cfjail *find_jail(const char *name);
45 static int running_jid(const char *name, int flags);
46
47 static struct cfjail **jails_byname;
48 static size_t njails;
49
50 /*
51  * Set up jail dependency lists.
52  */
53 void
54 dep_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  */
155 int
156 dep_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  */
232 void
233 dep_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  */
267 void
268 dep_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  */
282 struct cfjail *
283 next_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  */
302 int
303 start_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  */
391 void
392 requeue(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  */
404 static void
405 dep_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  */
420 static int
421 cmp_jailptr(const void *a, const void *b)
422 {
423         return strcmp((*((struct cfjail * const *)a))->name,
424             ((*(struct cfjail * const *)b))->name);
425 }
426
427 static int
428 cmp_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  */
436 static struct cfjail *
437 find_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  */
449 static int
450 running_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 }