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 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 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 */ 154 int 155 dep_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 */ 231 void 232 dep_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 */ 266 void 267 dep_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 */ 281 struct cfjail * 282 next_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 */ 301 int 302 start_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_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_STOP) { 369 /* Allow -[rR] to specify a currently running jail. */ 370 if ((jid = running_jid(target, JAIL_DYING)) > 0) { 371 j = add_jail(); 372 j->name = estrdup(target); 373 j->jid = jid; 374 } 375 } 376 if (j == NULL) { 377 warnx("\"%s\" not found", target); 378 return -1; 379 } 380 j->flags = (j->flags & JF_FAILED) | state; 381 dep_reset(j); 382 requeue(j, &ready); 383 } 384 return 0; 385 } 386 387 /* 388 * Move a jail to a new list. 389 */ 390 void 391 requeue(struct cfjail *j, struct cfjails *queue) 392 { 393 if (j->queue != queue) { 394 TAILQ_REMOVE(j->queue, j, tq); 395 TAILQ_INSERT_TAIL(queue, j, tq); 396 j->queue = queue; 397 } 398 } 399 400 /* 401 * Add a dependency edge between two jails. 402 */ 403 static void 404 dep_add(struct cfjail *from, struct cfjail *to, unsigned flags) 405 { 406 struct cfdepend *d; 407 408 d = emalloc(sizeof(struct cfdepend)); 409 d->flags = flags; 410 d->j[DEP_FROM] = from; 411 d->j[DEP_TO] = to; 412 STAILQ_INSERT_TAIL(&from->dep[DEP_FROM], d, tq[DEP_FROM]); 413 STAILQ_INSERT_TAIL(&to->dep[DEP_TO], d, tq[DEP_TO]); 414 } 415 416 /* 417 * Compare jail pointers for qsort/bsearch. 418 */ 419 static int 420 cmp_jailptr(const void *a, const void *b) 421 { 422 return strcmp((*((struct cfjail * const *)a))->name, 423 ((*(struct cfjail * const *)b))->name); 424 } 425 426 static int 427 cmp_jailptr_name(const void *a, const void *b) 428 { 429 return strcmp((const char *)a, ((*(struct cfjail * const *)b))->name); 430 } 431 432 /* 433 * Find a jail object by name. 434 */ 435 static struct cfjail * 436 find_jail(const char *name) 437 { 438 struct cfjail **jp; 439 440 jp = bsearch(name, jails_byname, njails, sizeof(struct cfjail *), 441 cmp_jailptr_name); 442 return jp ? *jp : NULL; 443 } 444 445 /* 446 * Return the named jail's jid if it is running, and -1 if it isn't. 447 */ 448 static int 449 running_jid(const char *name, int flags) 450 { 451 struct iovec jiov[2]; 452 char *ep; 453 int jid; 454 455 if ((jid = strtol(name, &ep, 10)) && !*ep) { 456 jiov[0].iov_base = __DECONST(char *, "jid"); 457 jiov[0].iov_len = sizeof("jid"); 458 jiov[1].iov_base = &jid; 459 jiov[1].iov_len = sizeof(jid); 460 } else { 461 jiov[0].iov_base = __DECONST(char *, "name"); 462 jiov[0].iov_len = sizeof("name"); 463 jiov[1].iov_len = strlen(name) + 1; 464 jiov[1].iov_base = alloca(jiov[1].iov_len); 465 strcpy(jiov[1].iov_base, name); 466 } 467 return jail_get(jiov, 2, flags); 468 } 469