state.c revision 214117
155714Skris/*- 255714Skris * Copyright (c) 2010 James Gritton 355714Skris * All rights reserved. 455714Skris * 555714Skris * Redistribution and use in source and binary forms, with or without 655714Skris * modification, are permitted provided that the following conditions 755714Skris * are met: 8280304Sjkim * 1. Redistributions of source code must retain the above copyright 955714Skris * notice, this list of conditions and the following disclaimer. 1055714Skris * 2. Redistributions in binary form must reproduce the above copyright 1155714Skris * notice, this list of conditions and the following disclaimer in the 1255714Skris * documentation and/or other materials provided with the distribution. 1355714Skris * 1455714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15280304Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1655714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1755714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22280304Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2455714Skris * SUCH DAMAGE. 2555714Skris */ 2655714Skris 2755714Skris#include <sys/cdefs.h> 2855714Skris__FBSDID("$FreeBSD: projects/jailconf/usr.sbin/jail/state.c 214117 2010-10-20 20:42:33Z jamie $"); 2955714Skris 3055714Skris#include <sys/uio.h> 3155714Skris 3255714Skris#include <err.h> 3355714Skris#include <stdlib.h> 3455714Skris#include <string.h> 3555714Skris 3655714Skris#include "jailp.h" 37280304Sjkim 3855714Skrisstruct cfjails ready = TAILQ_HEAD_INITIALIZER(ready); 3955714Skrisstruct cfjails waiting = TAILQ_HEAD_INITIALIZER(waiting); 40280304Sjkim 4155714Skrisstatic void dep_add(struct cfjail *from, struct cfjail *to, unsigned flags); 4255714Skrisstatic int cmp_jailptr(const void *a, const void *b); 4355714Skrisstatic int cmp_jailptr_name(const void *a, const void *b); 4455714Skrisstatic struct cfjail *find_jail(const char *name); 4555714Skrisstatic int running_jid(const char *name, int flags); 4655714Skris 4755714Skrisstatic struct cfjail **jails_byname; 4855714Skrisstatic size_t njails; 4955714Skris 5055714Skris/* 5155714Skris * Set up jail dependency lists. 52280304Sjkim */ 5355714Skrisvoid 5455714Skrisdep_setup(int docf) 5555714Skris{ 5655714Skris struct cfjail *j, *dj; 5755714Skris struct cfparam *p; 5855714Skris struct cfstring *s; 5955714Skris struct cfdepend *d; 6055714Skris const char *cs; 6155714Skris char *pname; 6255714Skris size_t plen; 6355714Skris int error, deps, ldeps; 6455714Skris 6555714Skris if (!docf) { 6655714Skris /* 6755714Skris * With no config file, let "depend" for a single jail 6855714Skris * look at currently running jails. 69109998Smarkm */ 7055714Skris if ((j = TAILQ_FIRST(&cfjails)) && 7155714Skris (p = j->intparams[IP_DEPEND])) { 72280304Sjkim STAILQ_FOREACH(s, &p->val, tq) { 73280304Sjkim if (running_jid(s->s, 0) < 0) { 74280304Sjkim warnx("depends on nonexistent jail " 75280304Sjkim "\"%s\"", s->s); 76280304Sjkim j->flags |= JF_FAILED; 77280304Sjkim } 78280304Sjkim } 79280304Sjkim } 80280304Sjkim return; 81280304Sjkim } 82280304Sjkim 83280304Sjkim njails = 0; 84280304Sjkim TAILQ_FOREACH(j, &cfjails, tq) 8555714Skris njails++; 8655714Skris jails_byname = emalloc(njails * sizeof(struct cfjail *)); 87280304Sjkim njails = 0; 88280304Sjkim TAILQ_FOREACH(j, &cfjails, tq) 89280304Sjkim jails_byname[njails++] = j; 9055714Skris qsort(jails_byname, njails, sizeof(struct cfjail *), cmp_jailptr); 91280304Sjkim error = 0; 92280304Sjkim deps = 0; 93280304Sjkim ldeps = 0; 94280304Sjkim plen = 0; 95280304Sjkim pname = NULL; 9655714Skris TAILQ_FOREACH(j, &cfjails, tq) { 97280304Sjkim if (j->flags & JF_FAILED) 98280304Sjkim continue; 99280304Sjkim if ((p = j->intparams[IP_DEPEND])) { 100280304Sjkim STAILQ_FOREACH(s, &p->val, tq) { 101280304Sjkim dj = find_jail(s->s); 102280304Sjkim if (dj != NULL) { 103280304Sjkim deps++; 104109998Smarkm dep_add(j, dj, 0); 105280304Sjkim } else { 106280304Sjkim jail_warnx(j, 107280304Sjkim "depends on undefined jail \"%s\"", 108280304Sjkim s->s); 109109998Smarkm j->flags |= JF_FAILED; 110280304Sjkim } 111280304Sjkim } 112280304Sjkim } 113280304Sjkim /* A jail has an implied dependency on its parent. */ 114280304Sjkim if ((cs = strrchr(j->name, '.'))) 115280304Sjkim { 116280304Sjkim if (plen < (size_t)(cs - j->name + 1)) { 117280304Sjkim plen = (cs - j->name) + 1; 118280304Sjkim pname = erealloc(pname, plen); 119280304Sjkim } 120280304Sjkim strlcpy(pname, j->name, plen); 121280304Sjkim dj = find_jail(pname); 122280304Sjkim if (dj != NULL) { 123280304Sjkim ldeps++; 12455714Skris dep_add(j, dj, DF_LIGHT); 12555714Skris } 126280304Sjkim } 127280304Sjkim } 128280304Sjkim 129280304Sjkim /* Look for dependency loops. */ 130280304Sjkim if (deps && (deps > 1 || ldeps)) { 13155714Skris (void)start_state(NULL, 0, 0); 132280304Sjkim while ((j = TAILQ_FIRST(&ready))) { 133280304Sjkim requeue(j, &cfjails); 134280304Sjkim dep_done(j, DF_NOFAIL); 13555714Skris } 136280304Sjkim while ((j = TAILQ_FIRST(&waiting)) != NULL) { 137280304Sjkim jail_warnx(j, "dependency loop"); 138280304Sjkim j->flags |= JF_FAILED; 139280304Sjkim do { 14055714Skris requeue(j, &cfjails); 141280304Sjkim dep_done(j, DF_NOFAIL); 142280304Sjkim } while ((j = TAILQ_FIRST(&ready))); 143280304Sjkim } 144280304Sjkim TAILQ_FOREACH(j, &cfjails, tq) 145280304Sjkim STAILQ_FOREACH(d, &j->dep[DEP_FROM], tq[DEP_FROM]) 146280304Sjkim d->flags &= ~DF_SEEN; 147280304Sjkim } 148280304Sjkim if (pname != NULL) 149280304Sjkim free(pname); 150280304Sjkim} 151280304Sjkim 152280304Sjkim/* 153280304Sjkim * Return if a jail has dependencies. 154280304Sjkim */ 155280304Sjkimint 156280304Sjkimdep_check(struct cfjail *j) 157280304Sjkim{ 158280304Sjkim int reset, depfrom, depto, ndeps, rev; 159280304Sjkim struct cfjail *dj; 160280304Sjkim struct cfdepend *d; 161280304Sjkim 162280304Sjkim static int bits[] = { 0, 1, 1, 2, 1, 2, 2, 3 }; 163280304Sjkim 164280304Sjkim if (j->ndeps == 0) 165280304Sjkim return 0; 166280304Sjkim ndeps = 0; 167280304Sjkim if ((rev = JF_DO_STOP(j->flags))) { 168280304Sjkim depfrom = DEP_TO; 169280304Sjkim depto = DEP_FROM; 170280304Sjkim } else { 171280304Sjkim depfrom = DEP_FROM; 172280304Sjkim depto = DEP_TO; 173280304Sjkim } 174280304Sjkim STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom]) { 175280304Sjkim if (d->flags & DF_SEEN) 176280304Sjkim continue; 177280304Sjkim dj = d->j[depto]; 178280304Sjkim if (dj->flags & JF_FAILED) { 179280304Sjkim if (!(j->flags & (JF_DEPEND | JF_FAILED)) && 180280304Sjkim verbose >= 0) 181280304Sjkim jail_warnx(j, "skipped"); 182280304Sjkim j->flags |= JF_FAILED; 18355714Skris continue; 18455714Skris } 185280304Sjkim /* 186280304Sjkim * The dependee's state may be set (or changed) as a result of 187280304Sjkim * being in a dependency it wasn't in earlier. 188280304Sjkim */ 189280304Sjkim reset = 0; 19055714Skris if (bits[dj->flags & JF_OP_MASK] <= 1) { 191280304Sjkim if (!(dj->flags & JF_OP_MASK)) { 192280304Sjkim reset = 1; 193280304Sjkim dj->flags |= JF_DEPEND; 19455714Skris requeue(dj, &ready); 195280304Sjkim } 196280304Sjkim /* Set or change the dependee's state. */ 197280304Sjkim switch (j->flags & JF_OP_MASK) { 198280304Sjkim case JF_START: 19955714Skris dj->flags |= JF_START; 200280304Sjkim break; 201280304Sjkim case JF_SET: 202280304Sjkim if (!(dj->flags & JF_OP_MASK)) 203280304Sjkim dj->flags |= JF_SET; 204280304Sjkim else if (dj->flags & JF_STOP) 205280304Sjkim dj->flags |= JF_START; 206280304Sjkim break; 207280304Sjkim case JF_STOP: 208280304Sjkim case JF_RESTART: 209280304Sjkim if (!(dj->flags & JF_STOP)) 210280304Sjkim reset = 1; 211280304Sjkim dj->flags |= JF_STOP; 212280304Sjkim if (dj->flags & JF_SET) 213280304Sjkim dj->flags ^= (JF_START | JF_SET); 214280304Sjkim break; 215280304Sjkim } 216280304Sjkim } 217280304Sjkim if (reset) 218280304Sjkim dep_reset(dj); 219280304Sjkim if (!((d->flags & DF_LIGHT) && 220280304Sjkim (rev ? dj->jid < 0 : dj->jid > 0))) 221280304Sjkim ndeps++; 222280304Sjkim } 223280304Sjkim if (ndeps == 0) 224280304Sjkim return 0; 225280304Sjkim requeue(j, &waiting); 226280304Sjkim return 1; 227280304Sjkim} 228280304Sjkim 229280304Sjkim/* 230280304Sjkim * Resolve any dependencies from a finished jail. 231280304Sjkim */ 232280304Sjkimvoid 233280304Sjkimdep_done(struct cfjail *j, unsigned flags) 234280304Sjkim{ 235280304Sjkim struct cfjail *dj; 236280304Sjkim struct cfdepend *d; 237280304Sjkim int depfrom, depto; 238280304Sjkim 239280304Sjkim if (JF_DO_STOP(j->flags)) { 240280304Sjkim depfrom = DEP_TO; 241280304Sjkim depto = DEP_FROM; 24255714Skris } else { 24359191Skris depfrom = DEP_FROM; 24459191Skris depto = DEP_TO; 245280304Sjkim } 246280304Sjkim STAILQ_FOREACH(d, &j->dep[depto], tq[depto]) { 247280304Sjkim if ((d->flags & DF_SEEN) | (flags & ~d->flags & DF_LIGHT)) 248280304Sjkim continue; 249280304Sjkim d->flags |= DF_SEEN; 250280304Sjkim dj = d->j[depfrom]; 251280304Sjkim if (!(flags & DF_NOFAIL) && (j->flags & JF_FAILED) && 252280304Sjkim (j->flags & (JF_OP_MASK | JF_DEPEND)) != 253280304Sjkim (JF_SET | JF_DEPEND)) { 254280304Sjkim if (!(dj->flags & (JF_DEPEND | JF_FAILED)) && 255280304Sjkim verbose >= 0) 256280304Sjkim jail_warnx(dj, "skipped"); 257280304Sjkim dj->flags |= JF_FAILED; 258280304Sjkim } 259280304Sjkim if (!--dj->ndeps && dj->queue == &waiting) 260280304Sjkim requeue(dj, &ready); 261280304Sjkim } 262280304Sjkim} 263280304Sjkim 264280304Sjkim/* 265280304Sjkim * Count a jail's dependencies and mark them as unseen. 266280304Sjkim */ 267280304Sjkimvoid 268280304Sjkimdep_reset(struct cfjail *j) 269280304Sjkim{ 270280304Sjkim int depfrom; 271280304Sjkim struct cfdepend *d; 272280304Sjkim 273280304Sjkim depfrom = JF_DO_STOP(j->flags) ? DEP_TO : DEP_FROM; 274280304Sjkim j->ndeps = 0; 27559191Skris STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom]) 27659191Skris j->ndeps++; 277280304Sjkim} 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 ? &waiting : &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} 467