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 } 470