]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/bectl/bectl_jail.c
bectl(8): Implement the 'create a snapshot' variant of create
[FreeBSD/FreeBSD.git] / sbin / bectl / bectl_jail.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
14  *
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
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/jail.h>
33 #include <sys/mount.h>
34 #include <err.h>
35 #include <jail.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include <be.h>
42
43 #include "bectl.h"
44
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);
50
51 static int bectl_search_jail_paths(const char *mnt);
52 static int bectl_locate_jail(const char *ident);
53
54 /* We'll start with 8 parameters initially and grow as needed. */
55 #define INIT_PARAMCOUNT 8
56
57 static struct jailparam *jp;
58 static int jpcnt;
59 static int jpused;
60 static char mnt_loc[BE_MAXPATHLEN];
61
62 static void
63 jailparam_grow(void)
64 {
65
66         jpcnt *= 2;
67         jp = realloc(jp, jpcnt * sizeof(*jp));
68         if (jp == NULL)
69                 err(2, "realloc");
70 }
71
72 static void
73 jailparam_add(const char *name, const char *val)
74 {
75         int i;
76
77         for (i = 0; i < jpused; ++i) {
78                 if (strcmp(name, jp[i].jp_name) == 0)
79                         break;
80         }
81
82         if (i < jpused)
83                 jailparam_free(&jp[i], 1);
84         else if (jpused == jpcnt)
85                 /* The next slot isn't allocated yet */
86                 jailparam_grow();
87
88         if (jailparam_init(&jp[i], name) != 0)
89                 return;
90         if (jailparam_import(&jp[i], val) != 0)
91                 return;
92         ++jpused;
93 }
94
95 static int
96 jailparam_del(const char *name)
97 {
98         int i;
99         char *val;
100
101         for (i = 0; i < jpused; ++i) {
102                 if (strcmp(name, jp[i].jp_name) == 0)
103                         break;
104         }
105
106         if (i == jpused)
107                 return (ENOENT);
108
109         for (; i < jpused - 1; ++i) {
110                 val = jailparam_export(&jp[i + 1]);
111
112                 jailparam_free(&jp[i], 1);
113                 /*
114                  * Given the context, the following will really only fail if
115                  * they can't allocate the copy of the name or value.
116                  */
117                 if (jailparam_init(&jp[i], jp[i + 1].jp_name) != 0) {
118                         free(val);
119                         return (ENOMEM);
120                 }
121                 if (jailparam_import(&jp[i], val) != 0) {
122                         jailparam_free(&jp[i], 1);
123                         free(val);
124                         return (ENOMEM);
125                 }
126                 free(val);
127         }
128
129         jailparam_free(&jp[i], 1);
130         --jpused;
131         return (0);
132 }
133
134 static bool
135 jailparam_addarg(char *arg)
136 {
137         char *name, *val;
138
139         if (arg == NULL)
140                 return (false);
141         name = arg;
142         if ((val = strchr(arg, '=')) == NULL) {
143                 fprintf(stderr, "bectl jail: malformed jail option '%s'\n",
144                     arg);
145                 return (false);
146         }
147
148         *val++ = '\0';
149         if (strcmp(name, "path") == 0) {
150                 if (strlen(val) >= BE_MAXPATHLEN) {
151                         fprintf(stderr,
152                             "bectl jail: skipping too long path assignment '%s' (max length = %d)\n",
153                             val, BE_MAXPATHLEN);
154                         return (false);
155                 }
156                 strlcpy(mnt_loc, val, sizeof(mnt_loc));
157         }
158         jailparam_add(name, val);
159         return (true);
160 }
161
162 static int
163 jailparam_delarg(char *arg)
164 {
165         char *name, *val;
166
167         if (arg == NULL)
168                 return (EINVAL);
169         name = arg;
170         if ((val = strchr(name, '=')) != NULL)
171                 *val++ = '\0';
172
173         if (strcmp(name, "path") == 0)
174                 *mnt_loc = '\0';
175         return (jailparam_del(name));
176 }
177
178 int
179 bectl_cmd_jail(int argc, char *argv[])
180 {
181         char *bootenv, *mountpoint;
182         int jid, opt, ret;
183         bool default_hostname, default_name;
184
185         default_hostname = default_name = true;
186         jpcnt = INIT_PARAMCOUNT;
187         jp = malloc(jpcnt * sizeof(*jp));
188         if (jp == NULL)
189                 err(2, "malloc");
190
191         jailparam_add("persist", "true");
192         jailparam_add("allow.mount", "true");
193         jailparam_add("allow.mount.devfs", "true");
194         jailparam_add("enforce_statfs", "1");
195
196         while ((opt = getopt(argc, argv, "o:u:")) != -1) {
197                 switch (opt) {
198                 case 'o':
199                         if (jailparam_addarg(optarg)) {
200                                 /*
201                                  * optarg has been modified to null terminate
202                                  * at the assignment operator.
203                                  */
204                                 if (strcmp(optarg, "name") == 0)
205                                         default_name = false;
206                                 if (strcmp(optarg, "host.hostname") == 0)
207                                         default_hostname = false;
208                         }
209                         break;
210                 case 'u':
211                         if ((ret = jailparam_delarg(optarg)) == 0) {
212                                 if (strcmp(optarg, "name") == 0)
213                                         default_name = true;
214                                 if (strcmp(optarg, "host.hostname") == 0)
215                                         default_hostname = true;
216                         } else if (ret != ENOENT) {
217                                 fprintf(stderr,
218                                     "bectl jail: error unsetting \"%s\"\n",
219                                     optarg);
220                                 return (ret);
221                         }
222                         break;
223                 default:
224                         fprintf(stderr, "bectl jail: unknown option '-%c'\n",
225                             optopt);
226                         return (usage(false));
227                 }
228         }
229
230         argc -= optind;
231         argv += optind;
232
233         /* struct jail be_jail = { 0 }; */
234         if (argc < 1) {
235                 fprintf(stderr, "bectl jail: missing boot environment name\n");
236                 return (usage(false));
237         }
238         if (argc > 2) {
239                 fprintf(stderr, "bectl jail: too many arguments\n");
240                 return (usage(false));
241         }
242
243         bootenv = argv[0];
244
245         /*
246          * XXX TODO: if its already mounted, perhaps there should be a flag to
247          * indicate its okay to proceed??
248          */
249         if (*mnt_loc == '\0')
250                 mountpoint = NULL;
251         else
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");
255                 return (1);
256         }
257
258         if (default_name)
259                 jailparam_add("name", bootenv);
260         if (default_hostname)
261                 jailparam_add("host.hostname", bootenv);
262         /*
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.
265          */
266         if (mountpoint == NULL)
267                 jailparam_add("path", mnt_loc);
268         jid = jailparam_set(jp, jpused, JAIL_CREATE | JAIL_ATTACH);
269         if (jid == -1) {
270                 fprintf(stderr, "unable to create jail.  error: %d\n", errno);
271                 return (1);
272         }
273
274         jailparam_free(jp, jpused);
275         free(jp);
276
277         /* We're attached within the jail... good bye! */
278         chdir("/");
279         execl("/bin/sh", "/bin/sh", NULL);
280         return (0);
281 }
282
283 static int
284 bectl_search_jail_paths(const char *mnt)
285 {
286         char jailpath[MAXPATHLEN];
287         int jid;
288
289         jid = 0;
290         (void)mnt;
291         while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath,
292             NULL)) != -1) {
293                 if (strcmp(jailpath, mnt) == 0)
294                         return (jid);
295         }
296
297         return (-1);
298 }
299
300 /*
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.
303  */
304 static int
305 bectl_locate_jail(const char *ident)
306 {
307         nvlist_t *belist, *props;
308         char *mnt;
309         int jid;
310
311         /* Try the easy-match first */
312         jid = jail_getid(ident);
313         if (jid != -1)
314                 return (jid);
315
316         /* Attempt to try it as a BE name, first */
317         if (be_prop_list_alloc(&belist) != 0)
318                 return (-1);
319
320         if (be_get_bootenv_props(be, belist) != 0)
321                 return (-1);
322
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);
328                         return (jid);
329                 }
330
331                 be_prop_list_free(belist);
332         }
333
334         return (-1);
335 }
336
337 int
338 bectl_cmd_unjail(int argc, char *argv[])
339 {
340         char path[MAXPATHLEN];
341         char *cmd, *name, *target;
342         int jid;
343
344         /* Store alias used */
345         cmd = argv[0];
346
347         if (argc != 2) {
348                 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
349                 return (usage(false));
350         }
351
352         target = argv[1];
353
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,
357                     target);
358                 return (1);
359         }
360
361         bzero(&path, MAXPATHLEN);
362         name = jail_getname(jid);
363         if (jail_getv(0, "name", name, "path", path, NULL) != jid) {
364                 free(name);
365                 fprintf(stderr,
366                     "bectl %s: failed to get path for jail requested by '%s'\n",
367                     cmd, target);
368                 return (1);
369         }
370
371         free(name);
372
373         if (be_mounted_at(be, path, NULL) != 0) {
374                 fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n",
375                     cmd, target);
376                 return (1);
377         }
378
379         jail_remove(jid);
380         unmount(path, 0);
381
382         return (0);
383 }