1214117Sjamie/*- 2223190Sjamie * Copyright (c) 2011 James Gritton 3214117Sjamie * All rights reserved. 4214117Sjamie * 5214117Sjamie * Redistribution and use in source and binary forms, with or without 6214117Sjamie * modification, are permitted provided that the following conditions 7214117Sjamie * are met: 8214117Sjamie * 1. Redistributions of source code must retain the above copyright 9214117Sjamie * notice, this list of conditions and the following disclaimer. 10214117Sjamie * 2. Redistributions in binary form must reproduce the above copyright 11214117Sjamie * notice, this list of conditions and the following disclaimer in the 12214117Sjamie * documentation and/or other materials provided with the distribution. 13214117Sjamie * 14214117Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15214117Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16214117Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17214117Sjamie * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18214117Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19214117Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20214117Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21214117Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22214117Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23214117Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24214117Sjamie * SUCH DAMAGE. 25214117Sjamie */ 26214117Sjamie 27214117Sjamie#include <sys/cdefs.h> 28214117Sjamie__FBSDID("$FreeBSD$"); 29214117Sjamie 30214117Sjamie#include <sys/uio.h> 31214117Sjamie 32214117Sjamie#include <err.h> 33214117Sjamie#include <stdlib.h> 34214117Sjamie#include <string.h> 35214117Sjamie 36214117Sjamie#include "jailp.h" 37214117Sjamie 38214117Sjamiestruct cfjails ready = TAILQ_HEAD_INITIALIZER(ready); 39216367Sjamiestruct cfjails depend = TAILQ_HEAD_INITIALIZER(depend); 40214117Sjamie 41214117Sjamiestatic void dep_add(struct cfjail *from, struct cfjail *to, unsigned flags); 42214117Sjamiestatic int cmp_jailptr(const void *a, const void *b); 43214117Sjamiestatic int cmp_jailptr_name(const void *a, const void *b); 44214117Sjamiestatic struct cfjail *find_jail(const char *name); 45214117Sjamiestatic int running_jid(const char *name, int flags); 46214117Sjamie 47214117Sjamiestatic struct cfjail **jails_byname; 48214117Sjamiestatic size_t njails; 49214117Sjamie 50214117Sjamie/* 51214117Sjamie * Set up jail dependency lists. 52214117Sjamie */ 53214117Sjamievoid 54214117Sjamiedep_setup(int docf) 55214117Sjamie{ 56214117Sjamie struct cfjail *j, *dj; 57214117Sjamie struct cfparam *p; 58214117Sjamie struct cfstring *s; 59214117Sjamie struct cfdepend *d; 60214117Sjamie const char *cs; 61214117Sjamie char *pname; 62214117Sjamie size_t plen; 63214117Sjamie int error, deps, ldeps; 64214117Sjamie 65214117Sjamie if (!docf) { 66214117Sjamie /* 67214117Sjamie * With no config file, let "depend" for a single jail 68214117Sjamie * look at currently running jails. 69214117Sjamie */ 70214117Sjamie if ((j = TAILQ_FIRST(&cfjails)) && 71214117Sjamie (p = j->intparams[IP_DEPEND])) { 72223188Sjamie TAILQ_FOREACH(s, &p->val, tq) { 73214117Sjamie if (running_jid(s->s, 0) < 0) { 74214117Sjamie warnx("depends on nonexistent jail " 75214117Sjamie "\"%s\"", s->s); 76214117Sjamie j->flags |= JF_FAILED; 77214117Sjamie } 78214117Sjamie } 79214117Sjamie } 80214117Sjamie return; 81214117Sjamie } 82214117Sjamie 83214117Sjamie njails = 0; 84214117Sjamie TAILQ_FOREACH(j, &cfjails, tq) 85214117Sjamie njails++; 86214117Sjamie jails_byname = emalloc(njails * sizeof(struct cfjail *)); 87214117Sjamie njails = 0; 88214117Sjamie TAILQ_FOREACH(j, &cfjails, tq) 89214117Sjamie jails_byname[njails++] = j; 90214117Sjamie qsort(jails_byname, njails, sizeof(struct cfjail *), cmp_jailptr); 91214117Sjamie error = 0; 92214117Sjamie deps = 0; 93214117Sjamie ldeps = 0; 94214117Sjamie plen = 0; 95214117Sjamie pname = NULL; 96214117Sjamie TAILQ_FOREACH(j, &cfjails, tq) { 97214117Sjamie if (j->flags & JF_FAILED) 98214117Sjamie continue; 99214117Sjamie if ((p = j->intparams[IP_DEPEND])) { 100223188Sjamie TAILQ_FOREACH(s, &p->val, tq) { 101214117Sjamie dj = find_jail(s->s); 102214117Sjamie if (dj != NULL) { 103214117Sjamie deps++; 104214117Sjamie dep_add(j, dj, 0); 105214117Sjamie } else { 106214117Sjamie jail_warnx(j, 107214117Sjamie "depends on undefined jail \"%s\"", 108214117Sjamie s->s); 109214117Sjamie j->flags |= JF_FAILED; 110214117Sjamie } 111214117Sjamie } 112214117Sjamie } 113214117Sjamie /* A jail has an implied dependency on its parent. */ 114214117Sjamie if ((cs = strrchr(j->name, '.'))) 115214117Sjamie { 116214117Sjamie if (plen < (size_t)(cs - j->name + 1)) { 117214117Sjamie plen = (cs - j->name) + 1; 118214117Sjamie pname = erealloc(pname, plen); 119214117Sjamie } 120214117Sjamie strlcpy(pname, j->name, plen); 121214117Sjamie dj = find_jail(pname); 122214117Sjamie if (dj != NULL) { 123214117Sjamie ldeps++; 124214117Sjamie dep_add(j, dj, DF_LIGHT); 125214117Sjamie } 126214117Sjamie } 127214117Sjamie } 128214117Sjamie 129214117Sjamie /* Look for dependency loops. */ 130214117Sjamie if (deps && (deps > 1 || ldeps)) { 131231238Sjamie (void)start_state(NULL, 0, 0, 0); 132214117Sjamie while ((j = TAILQ_FIRST(&ready))) { 133214117Sjamie requeue(j, &cfjails); 134214117Sjamie dep_done(j, DF_NOFAIL); 135214117Sjamie } 136216367Sjamie while ((j = TAILQ_FIRST(&depend)) != NULL) { 137214117Sjamie jail_warnx(j, "dependency loop"); 138214117Sjamie j->flags |= JF_FAILED; 139214117Sjamie do { 140214117Sjamie requeue(j, &cfjails); 141214117Sjamie dep_done(j, DF_NOFAIL); 142214117Sjamie } while ((j = TAILQ_FIRST(&ready))); 143214117Sjamie } 144214117Sjamie TAILQ_FOREACH(j, &cfjails, tq) 145214117Sjamie STAILQ_FOREACH(d, &j->dep[DEP_FROM], tq[DEP_FROM]) 146214117Sjamie d->flags &= ~DF_SEEN; 147214117Sjamie } 148214117Sjamie if (pname != NULL) 149214117Sjamie free(pname); 150214117Sjamie} 151214117Sjamie 152214117Sjamie/* 153214117Sjamie * Return if a jail has dependencies. 154214117Sjamie */ 155214117Sjamieint 156214117Sjamiedep_check(struct cfjail *j) 157214117Sjamie{ 158214117Sjamie int reset, depfrom, depto, ndeps, rev; 159214117Sjamie struct cfjail *dj; 160214117Sjamie struct cfdepend *d; 161214117Sjamie 162214117Sjamie static int bits[] = { 0, 1, 1, 2, 1, 2, 2, 3 }; 163214117Sjamie 164214117Sjamie if (j->ndeps == 0) 165214117Sjamie return 0; 166214117Sjamie ndeps = 0; 167214117Sjamie if ((rev = JF_DO_STOP(j->flags))) { 168214117Sjamie depfrom = DEP_TO; 169214117Sjamie depto = DEP_FROM; 170214117Sjamie } else { 171214117Sjamie depfrom = DEP_FROM; 172214117Sjamie depto = DEP_TO; 173214117Sjamie } 174214117Sjamie STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom]) { 175214117Sjamie if (d->flags & DF_SEEN) 176214117Sjamie continue; 177214117Sjamie dj = d->j[depto]; 178214117Sjamie if (dj->flags & JF_FAILED) { 179214117Sjamie if (!(j->flags & (JF_DEPEND | JF_FAILED)) && 180214117Sjamie verbose >= 0) 181214117Sjamie jail_warnx(j, "skipped"); 182214117Sjamie j->flags |= JF_FAILED; 183214117Sjamie continue; 184214117Sjamie } 185214117Sjamie /* 186214117Sjamie * The dependee's state may be set (or changed) as a result of 187214117Sjamie * being in a dependency it wasn't in earlier. 188214117Sjamie */ 189214117Sjamie reset = 0; 190214117Sjamie if (bits[dj->flags & JF_OP_MASK] <= 1) { 191214117Sjamie if (!(dj->flags & JF_OP_MASK)) { 192214117Sjamie reset = 1; 193214117Sjamie dj->flags |= JF_DEPEND; 194214117Sjamie requeue(dj, &ready); 195214117Sjamie } 196214117Sjamie /* Set or change the dependee's state. */ 197214117Sjamie switch (j->flags & JF_OP_MASK) { 198214117Sjamie case JF_START: 199214117Sjamie dj->flags |= JF_START; 200214117Sjamie break; 201214117Sjamie case JF_SET: 202214117Sjamie if (!(dj->flags & JF_OP_MASK)) 203214117Sjamie dj->flags |= JF_SET; 204214117Sjamie else if (dj->flags & JF_STOP) 205214117Sjamie dj->flags |= JF_START; 206214117Sjamie break; 207214117Sjamie case JF_STOP: 208214117Sjamie case JF_RESTART: 209214117Sjamie if (!(dj->flags & JF_STOP)) 210214117Sjamie reset = 1; 211214117Sjamie dj->flags |= JF_STOP; 212214117Sjamie if (dj->flags & JF_SET) 213214117Sjamie dj->flags ^= (JF_START | JF_SET); 214214117Sjamie break; 215214117Sjamie } 216214117Sjamie } 217214117Sjamie if (reset) 218214117Sjamie dep_reset(dj); 219214117Sjamie if (!((d->flags & DF_LIGHT) && 220214117Sjamie (rev ? dj->jid < 0 : dj->jid > 0))) 221214117Sjamie ndeps++; 222214117Sjamie } 223214117Sjamie if (ndeps == 0) 224214117Sjamie return 0; 225216367Sjamie requeue(j, &depend); 226214117Sjamie return 1; 227214117Sjamie} 228214117Sjamie 229214117Sjamie/* 230214117Sjamie * Resolve any dependencies from a finished jail. 231214117Sjamie */ 232214117Sjamievoid 233214117Sjamiedep_done(struct cfjail *j, unsigned flags) 234214117Sjamie{ 235214117Sjamie struct cfjail *dj; 236214117Sjamie struct cfdepend *d; 237214117Sjamie int depfrom, depto; 238214117Sjamie 239214117Sjamie if (JF_DO_STOP(j->flags)) { 240214117Sjamie depfrom = DEP_TO; 241214117Sjamie depto = DEP_FROM; 242214117Sjamie } else { 243214117Sjamie depfrom = DEP_FROM; 244214117Sjamie depto = DEP_TO; 245214117Sjamie } 246214117Sjamie STAILQ_FOREACH(d, &j->dep[depto], tq[depto]) { 247214117Sjamie if ((d->flags & DF_SEEN) | (flags & ~d->flags & DF_LIGHT)) 248214117Sjamie continue; 249214117Sjamie d->flags |= DF_SEEN; 250214117Sjamie dj = d->j[depfrom]; 251214117Sjamie if (!(flags & DF_NOFAIL) && (j->flags & JF_FAILED) && 252214117Sjamie (j->flags & (JF_OP_MASK | JF_DEPEND)) != 253214117Sjamie (JF_SET | JF_DEPEND)) { 254214117Sjamie if (!(dj->flags & (JF_DEPEND | JF_FAILED)) && 255214117Sjamie verbose >= 0) 256214117Sjamie jail_warnx(dj, "skipped"); 257214117Sjamie dj->flags |= JF_FAILED; 258214117Sjamie } 259216367Sjamie if (!--dj->ndeps && dj->queue == &depend) 260214117Sjamie requeue(dj, &ready); 261214117Sjamie } 262214117Sjamie} 263214117Sjamie 264214117Sjamie/* 265214117Sjamie * Count a jail's dependencies and mark them as unseen. 266214117Sjamie */ 267214117Sjamievoid 268214117Sjamiedep_reset(struct cfjail *j) 269214117Sjamie{ 270214117Sjamie int depfrom; 271214117Sjamie struct cfdepend *d; 272214117Sjamie 273214117Sjamie depfrom = JF_DO_STOP(j->flags) ? DEP_TO : DEP_FROM; 274214117Sjamie j->ndeps = 0; 275214117Sjamie STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom]) 276214117Sjamie j->ndeps++; 277214117Sjamie} 278214117Sjamie 279214117Sjamie/* 280214117Sjamie * Find the next jail ready to do something. 281214117Sjamie */ 282214117Sjamiestruct cfjail * 283214117Sjamienext_jail(void) 284214117Sjamie{ 285214117Sjamie struct cfjail *j; 286214117Sjamie 287214117Sjamie if (!(j = next_proc(!TAILQ_EMPTY(&ready))) && 288214117Sjamie (j = TAILQ_FIRST(&ready)) && JF_DO_STOP(j->flags) && 289214117Sjamie (j = TAILQ_LAST(&ready, cfjails)) && !JF_DO_STOP(j->flags)) { 290214117Sjamie TAILQ_FOREACH_REVERSE(j, &ready, cfjails, tq) 291214117Sjamie if (JF_DO_STOP(j->flags)) 292214117Sjamie break; 293214117Sjamie } 294214117Sjamie if (j != NULL) 295214117Sjamie requeue(j, &cfjails); 296214117Sjamie return j; 297214117Sjamie} 298214117Sjamie 299214117Sjamie/* 300214117Sjamie * Set jails to the proper start state. 301214117Sjamie */ 302214117Sjamieint 303231238Sjamiestart_state(const char *target, int docf, unsigned state, int running) 304214117Sjamie{ 305214117Sjamie struct iovec jiov[6]; 306214117Sjamie struct cfjail *j, *tj; 307214117Sjamie int jid; 308214117Sjamie char namebuf[MAXHOSTNAMELEN]; 309214117Sjamie 310231238Sjamie if (!target || (!docf && state != JF_STOP) || 311231238Sjamie (!running && !strcmp(target, "*"))) { 312214117Sjamie /* 313231238Sjamie * For a global wildcard (including no target specified), 314231238Sjamie * set the state on all jails and start with those that 315231238Sjamie * have no dependencies. 316214117Sjamie */ 317214117Sjamie TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) { 318231238Sjamie j->flags = (j->flags & JF_FAILED) | state | 319231238Sjamie (docf ? JF_WILD : 0); 320214117Sjamie dep_reset(j); 321216367Sjamie requeue(j, j->ndeps ? &depend : &ready); 322214117Sjamie } 323214117Sjamie } else if (wild_jail_name(target)) { 324214117Sjamie /* 325214117Sjamie * For targets specified singly, or with a non-global wildcard, 326214117Sjamie * set their state and call them ready (even if there are 327214117Sjamie * dependencies). Leave everything else unqueued for now. 328214117Sjamie */ 329214117Sjamie if (running) { 330214117Sjamie /* 331214117Sjamie * -R matches its wildcards against currently running 332214117Sjamie * jails, not against the config file. 333214117Sjamie */ 334214117Sjamie *(const void **)&jiov[0].iov_base = "lastjid"; 335214117Sjamie jiov[0].iov_len = sizeof("lastjid"); 336214117Sjamie jiov[1].iov_base = &jid; 337214117Sjamie jiov[1].iov_len = sizeof(jid); 338214117Sjamie *(const void **)&jiov[2].iov_base = "jid"; 339214117Sjamie jiov[2].iov_len = sizeof("jid"); 340214117Sjamie jiov[3].iov_base = &jid; 341214117Sjamie jiov[3].iov_len = sizeof(jid); 342214117Sjamie *(const void **)&jiov[4].iov_base = "name"; 343214117Sjamie jiov[4].iov_len = sizeof("name"); 344214117Sjamie jiov[5].iov_base = &namebuf; 345214117Sjamie jiov[5].iov_len = sizeof(namebuf); 346214117Sjamie for (jid = 0; jail_get(jiov, 6, 0) > 0; ) { 347214117Sjamie if (wild_jail_match(namebuf, target)) { 348214117Sjamie j = add_jail(); 349214117Sjamie j->name = estrdup(namebuf); 350214117Sjamie j->jid = jid; 351214117Sjamie j->flags = (j->flags & JF_FAILED) | 352214117Sjamie state | JF_WILD; 353214117Sjamie dep_reset(j); 354214117Sjamie requeue(j, &ready); 355214117Sjamie } 356214117Sjamie } 357214117Sjamie } else { 358214117Sjamie TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) { 359214117Sjamie if (wild_jail_match(j->name, target)) { 360214117Sjamie j->flags = (j->flags & JF_FAILED) | 361214117Sjamie state | JF_WILD; 362214117Sjamie dep_reset(j); 363214117Sjamie requeue(j, &ready); 364214117Sjamie } 365214117Sjamie } 366214117Sjamie } 367214117Sjamie } else { 368214117Sjamie j = find_jail(target); 369214117Sjamie if (j == NULL && state == JF_STOP) { 370214117Sjamie /* Allow -[rR] to specify a currently running jail. */ 371214117Sjamie if ((jid = running_jid(target, JAIL_DYING)) > 0) { 372214117Sjamie j = add_jail(); 373214117Sjamie j->name = estrdup(target); 374214117Sjamie j->jid = jid; 375214117Sjamie } 376214117Sjamie } 377214117Sjamie if (j == NULL) { 378214117Sjamie warnx("\"%s\" not found", target); 379214117Sjamie return -1; 380214117Sjamie } 381214117Sjamie j->flags = (j->flags & JF_FAILED) | state; 382214117Sjamie dep_reset(j); 383214117Sjamie requeue(j, &ready); 384214117Sjamie } 385214117Sjamie return 0; 386214117Sjamie} 387214117Sjamie 388214117Sjamie/* 389214117Sjamie * Move a jail to a new list. 390214117Sjamie */ 391214117Sjamievoid 392214117Sjamierequeue(struct cfjail *j, struct cfjails *queue) 393214117Sjamie{ 394214117Sjamie if (j->queue != queue) { 395214117Sjamie TAILQ_REMOVE(j->queue, j, tq); 396214117Sjamie TAILQ_INSERT_TAIL(queue, j, tq); 397214117Sjamie j->queue = queue; 398214117Sjamie } 399214117Sjamie} 400214117Sjamie 401214117Sjamie/* 402214117Sjamie * Add a dependency edge between two jails. 403214117Sjamie */ 404214117Sjamiestatic void 405214117Sjamiedep_add(struct cfjail *from, struct cfjail *to, unsigned flags) 406214117Sjamie{ 407214117Sjamie struct cfdepend *d; 408214117Sjamie 409214117Sjamie d = emalloc(sizeof(struct cfdepend)); 410214117Sjamie d->flags = flags; 411214117Sjamie d->j[DEP_FROM] = from; 412214117Sjamie d->j[DEP_TO] = to; 413214117Sjamie STAILQ_INSERT_TAIL(&from->dep[DEP_FROM], d, tq[DEP_FROM]); 414214117Sjamie STAILQ_INSERT_TAIL(&to->dep[DEP_TO], d, tq[DEP_TO]); 415214117Sjamie} 416214117Sjamie 417214117Sjamie/* 418214117Sjamie * Compare jail pointers for qsort/bsearch. 419214117Sjamie */ 420214117Sjamiestatic int 421214117Sjamiecmp_jailptr(const void *a, const void *b) 422214117Sjamie{ 423214117Sjamie return strcmp((*((struct cfjail * const *)a))->name, 424214117Sjamie ((*(struct cfjail * const *)b))->name); 425214117Sjamie} 426214117Sjamie 427214117Sjamiestatic int 428214117Sjamiecmp_jailptr_name(const void *a, const void *b) 429214117Sjamie{ 430214117Sjamie return strcmp((const char *)a, ((*(struct cfjail * const *)b))->name); 431214117Sjamie} 432214117Sjamie 433214117Sjamie/* 434214117Sjamie * Find a jail object by name. 435214117Sjamie */ 436214117Sjamiestatic struct cfjail * 437214117Sjamiefind_jail(const char *name) 438214117Sjamie{ 439214117Sjamie struct cfjail **jp; 440214117Sjamie 441214117Sjamie jp = bsearch(name, jails_byname, njails, sizeof(struct cfjail *), 442214117Sjamie cmp_jailptr_name); 443214117Sjamie return jp ? *jp : NULL; 444214117Sjamie} 445214117Sjamie 446214117Sjamie/* 447214117Sjamie * Return the named jail's jid if it is running, and -1 if it isn't. 448214117Sjamie */ 449214117Sjamiestatic int 450214117Sjamierunning_jid(const char *name, int flags) 451214117Sjamie{ 452214117Sjamie struct iovec jiov[2]; 453214117Sjamie char *ep; 454214117Sjamie int jid; 455214117Sjamie 456214117Sjamie if ((jid = strtol(name, &ep, 10)) && !*ep) { 457214117Sjamie *(const void **)&jiov[0].iov_base = "jid"; 458214117Sjamie jiov[0].iov_len = sizeof("jid"); 459214117Sjamie jiov[1].iov_base = &jid; 460214117Sjamie jiov[1].iov_len = sizeof(jid); 461214117Sjamie } else { 462214117Sjamie *(const void **)&jiov[0].iov_base = "name"; 463214117Sjamie jiov[0].iov_len = sizeof("name"); 464214117Sjamie jiov[1].iov_len = strlen(name) + 1; 465214117Sjamie jiov[1].iov_base = alloca(jiov[1].iov_len); 466214117Sjamie strcpy(jiov[1].iov_base, name); 467214117Sjamie } 468214117Sjamie return jail_get(jiov, 2, flags); 469214117Sjamie} 470