2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
33 #include <sys/mount.h>
45 static void jailparam_grow(void);
46 static void jailparam_add(const char *name, const char *val);
47 static int jailparam_del(const char *name);
48 static bool jailparam_addarg(char *arg);
49 static int jailparam_delarg(char *arg);
51 static int bectl_search_jail_paths(const char *mnt);
52 static int bectl_locate_jail(const char *ident);
54 /* We'll start with 8 parameters initially and grow as needed. */
55 #define INIT_PARAMCOUNT 8
57 static struct jailparam *jp;
60 static char mnt_loc[BE_MAXPATHLEN];
67 jp = realloc(jp, jpcnt * sizeof(*jp));
73 jailparam_add(const char *name, const char *val)
77 for (i = 0; i < jpused; ++i) {
78 if (strcmp(name, jp[i].jp_name) == 0)
83 jailparam_free(&jp[i], 1);
84 else if (jpused == jpcnt)
85 /* The next slot isn't allocated yet */
88 if (jailparam_init(&jp[i], name) != 0)
90 if (jailparam_import(&jp[i], val) != 0)
96 jailparam_del(const char *name)
101 for (i = 0; i < jpused; ++i) {
102 if (strcmp(name, jp[i].jp_name) == 0)
109 for (; i < jpused - 1; ++i) {
110 val = jailparam_export(&jp[i + 1]);
112 jailparam_free(&jp[i], 1);
114 * Given the context, the following will really only fail if
115 * they can't allocate the copy of the name or value.
117 if (jailparam_init(&jp[i], jp[i + 1].jp_name) != 0) {
121 if (jailparam_import(&jp[i], val) != 0) {
122 jailparam_free(&jp[i], 1);
129 jailparam_free(&jp[i], 1);
135 jailparam_addarg(char *arg)
142 if ((val = strchr(arg, '=')) == NULL) {
143 fprintf(stderr, "bectl jail: malformed jail option '%s'\n",
149 if (strcmp(name, "path") == 0) {
150 if (strlen(val) >= BE_MAXPATHLEN) {
152 "bectl jail: skipping too long path assignment '%s' (max length = %d)\n",
156 strlcpy(mnt_loc, val, sizeof(mnt_loc));
158 jailparam_add(name, val);
163 jailparam_delarg(char *arg)
170 if ((val = strchr(name, '=')) != NULL)
173 if (strcmp(name, "path") == 0)
175 return (jailparam_del(name));
179 bectl_cmd_jail(int argc, char *argv[])
181 char *bootenv, *mountpoint;
183 bool default_hostname, default_name;
185 default_hostname = default_name = true;
186 jpcnt = INIT_PARAMCOUNT;
187 jp = malloc(jpcnt * sizeof(*jp));
191 jailparam_add("persist", "true");
192 jailparam_add("allow.mount", "true");
193 jailparam_add("allow.mount.devfs", "true");
194 jailparam_add("enforce_statfs", "1");
196 while ((opt = getopt(argc, argv, "o:u:")) != -1) {
199 if (jailparam_addarg(optarg)) {
201 * optarg has been modified to null terminate
202 * at the assignment operator.
204 if (strcmp(optarg, "name") == 0)
205 default_name = false;
206 if (strcmp(optarg, "host.hostname") == 0)
207 default_hostname = false;
211 if ((ret = jailparam_delarg(optarg)) == 0) {
212 if (strcmp(optarg, "name") == 0)
214 if (strcmp(optarg, "host.hostname") == 0)
215 default_hostname = true;
216 } else if (ret != ENOENT) {
218 "bectl jail: error unsetting \"%s\"\n",
224 fprintf(stderr, "bectl jail: unknown option '-%c'\n",
226 return (usage(false));
233 /* struct jail be_jail = { 0 }; */
235 fprintf(stderr, "bectl jail: missing boot environment name\n");
236 return (usage(false));
239 fprintf(stderr, "bectl jail: too many arguments\n");
240 return (usage(false));
246 * XXX TODO: if its already mounted, perhaps there should be a flag to
247 * indicate its okay to proceed??
249 if (*mnt_loc == '\0')
252 mountpoint = mnt_loc;
253 if (be_mount(be, bootenv, mountpoint, 0, mnt_loc) != BE_ERR_SUCCESS) {
254 fprintf(stderr, "could not mount bootenv\n");
259 jailparam_add("name", bootenv);
260 if (default_hostname)
261 jailparam_add("host.hostname", bootenv);
263 * This is our indicator that path was not set by the user, so we'll use
264 * the path that libbe generated for us.
266 if (mountpoint == NULL)
267 jailparam_add("path", mnt_loc);
268 jid = jailparam_set(jp, jpused, JAIL_CREATE | JAIL_ATTACH);
270 fprintf(stderr, "unable to create jail. error: %d\n", errno);
274 jailparam_free(jp, jpused);
277 /* We're attached within the jail... good bye! */
279 execl("/bin/sh", "/bin/sh", NULL);
284 bectl_search_jail_paths(const char *mnt)
286 char jailpath[MAXPATHLEN];
291 while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath,
293 if (strcmp(jailpath, mnt) == 0)
301 * Locate a jail based on an arbitrary identifier. This may be either a name,
302 * a jid, or a BE name. Returns the jid or -1 on failure.
305 bectl_locate_jail(const char *ident)
307 nvlist_t *belist, *props;
311 /* Try the easy-match first */
312 jid = jail_getid(ident);
316 /* Attempt to try it as a BE name, first */
317 if (be_prop_list_alloc(&belist) != 0)
320 if (be_get_bootenv_props(be, belist) != 0)
323 if (nvlist_lookup_nvlist(belist, ident, &props) == 0) {
324 /* We'll attempt to resolve the jid by way of mountpoint */
325 if (nvlist_lookup_string(props, "mountpoint", &mnt) == 0) {
326 jid = bectl_search_jail_paths(mnt);
327 be_prop_list_free(belist);
331 be_prop_list_free(belist);
338 bectl_cmd_unjail(int argc, char *argv[])
340 char path[MAXPATHLEN];
341 char *cmd, *name, *target;
344 /* Store alias used */
348 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
349 return (usage(false));
354 /* Locate the jail */
355 if ((jid = bectl_locate_jail(target)) == -1) {
356 fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd,
361 bzero(&path, MAXPATHLEN);
362 name = jail_getname(jid);
363 if (jail_getv(0, "name", name, "path", path, NULL) != jid) {
366 "bectl %s: failed to get path for jail requested by '%s'\n",
373 if (be_mounted_at(be, path, NULL) != 0) {
374 fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n",