2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/mount.h>
49 static int bectl_cmd_activate(int argc, char *argv[]);
50 static int bectl_cmd_check(int argc, char *argv[]);
51 static int bectl_cmd_create(int argc, char *argv[]);
52 static int bectl_cmd_destroy(int argc, char *argv[]);
53 static int bectl_cmd_export(int argc, char *argv[]);
54 static int bectl_cmd_import(int argc, char *argv[]);
56 static int bectl_cmd_add(int argc, char *argv[]);
58 static int bectl_cmd_mount(int argc, char *argv[]);
59 static int bectl_cmd_rename(int argc, char *argv[]);
60 static int bectl_cmd_unmount(int argc, char *argv[]);
69 fp = explicit ? stdout : stderr;
71 "usage:\tbectl {-h | -? | subcommand [args...]}\n"
73 "\tbectl add (path)*\n"
75 "\tbectl activate [-t] beName\n"
77 "\tbectl create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n"
78 "\tbectl create [-r] beName@snapshot\n"
79 "\tbectl destroy [-F] {beName | beName@snapshot}\n"
80 "\tbectl export sourceBe\n"
81 "\tbectl import targetBe\n"
82 "\tbectl jail {-b | -U} [{-o key=value | -u key}]... "
83 "{jailID | jailName}\n"
84 "\t bootenv [utility [argument ...]]\n"
85 "\tbectl list [-DHas] [{-c property | -C property}]\n"
86 "\tbectl mount beName [mountpoint]\n"
87 "\tbectl rename origBeName newBeName\n"
88 "\tbectl {ujail | unjail} {jailID | jailName} bootenv\n"
89 "\tbectl {umount | unmount} [-f] beName\n");
91 return (explicit ? 0 : EX_USAGE);
96 * Represents a relationship between the command name and the parser action
99 struct command_map_entry {
101 int (*fn)(int argc, char *argv[]);
102 /* True if libbe_print_on_error should be disabled */
106 static struct command_map_entry command_map[] =
108 { "activate", bectl_cmd_activate,false },
109 { "create", bectl_cmd_create, false },
110 { "destroy", bectl_cmd_destroy, false },
111 { "export", bectl_cmd_export, false },
112 { "import", bectl_cmd_import, false },
114 { "add", bectl_cmd_add, false },
116 { "jail", bectl_cmd_jail, false },
117 { "list", bectl_cmd_list, false },
118 { "mount", bectl_cmd_mount, false },
119 { "rename", bectl_cmd_rename, false },
120 { "unjail", bectl_cmd_unjail, false },
121 { "unmount", bectl_cmd_unmount, false },
122 { "check", bectl_cmd_check, true },
125 static struct command_map_entry *
126 get_cmd_info(const char *cmd)
130 for (i = 0; i < nitems(command_map); ++i) {
131 if (strcmp(cmd, command_map[i].command) == 0)
132 return (&command_map[i]);
140 bectl_cmd_activate(int argc, char *argv[])
146 while ((opt = getopt(argc, argv, "t")) != -1) {
152 fprintf(stderr, "bectl activate: unknown option '-%c'\n",
154 return (usage(false));
162 fprintf(stderr, "bectl activate: wrong number of arguments\n");
163 return (usage(false));
167 /* activate logic goes here */
168 if ((err = be_activate(be, argv[0], temp)) != 0)
169 /* XXX TODO: more specific error msg based on err */
170 printf("did not successfully activate boot environment %s\n",
173 printf("successfully activated boot environment %s\n", argv[0]);
176 printf("for next boot\n");
183 * TODO: when only one arg is given, and it contains an "@" the this should
184 * create that snapshot
187 bectl_cmd_create(int argc, char *argv[])
189 char snapshot[BE_MAXPATHLEN];
190 char *atpos, *bootenv, *snapname;
196 while ((opt = getopt(argc, argv, "e:r")) != -1) {
205 fprintf(stderr, "bectl create: unknown option '-%c'\n",
207 return (usage(false));
215 fprintf(stderr, "bectl create: wrong number of arguments\n");
216 return (usage(false));
221 err = BE_ERR_SUCCESS;
222 if ((atpos = strchr(bootenv, '@')) != NULL) {
224 * This is the "create a snapshot variant". No new boot
225 * environment is to be created here.
228 err = be_snapshot(be, bootenv, atpos, recursive, NULL);
230 if (snapname == NULL)
231 /* Create from currently booted BE */
232 err = be_snapshot(be, be_active_path(be), NULL,
233 recursive, snapshot);
234 else if (strchr(snapname, '@') != NULL)
235 /* Create from given snapshot */
236 strlcpy(snapshot, snapname, sizeof(snapshot));
238 /* Create from given BE */
239 err = be_snapshot(be, snapname, NULL, recursive,
242 if (err == BE_ERR_SUCCESS)
243 err = be_create_depth(be, bootenv, snapshot,
244 recursive == true ? -1 : 0);
253 "failed to create a snapshot '%s' of '%s'\n",
255 else if (snapname == NULL)
257 "failed to create bootenv %s\n", bootenv);
260 "failed to create bootenv %s from snapshot %s\n",
269 bectl_cmd_export(int argc, char *argv[])
274 fprintf(stderr, "bectl export: missing boot environment name\n");
275 return (usage(false));
279 fprintf(stderr, "bectl export: extra arguments provided\n");
280 return (usage(false));
285 if (isatty(STDOUT_FILENO)) {
286 fprintf(stderr, "bectl export: must redirect output\n");
290 be_export(be, bootenv, STDOUT_FILENO);
297 bectl_cmd_import(int argc, char *argv[])
303 fprintf(stderr, "bectl import: missing boot environment name\n");
304 return (usage(false));
308 fprintf(stderr, "bectl import: extra arguments provided\n");
309 return (usage(false));
314 if (isatty(STDIN_FILENO)) {
315 fprintf(stderr, "bectl import: input can not be from terminal\n");
319 err = be_import(be, bootenv, STDIN_FILENO);
326 bectl_cmd_add(int argc, char *argv[])
330 fprintf(stderr, "bectl add: must provide at least one path\n");
331 return (usage(false));
334 for (int i = 1; i < argc; ++i) {
335 printf("arg %d: %s\n", i, argv[i]);
336 /* XXX TODO catch err */
337 be_add_child(be, argv[i], true);
345 bectl_cmd_destroy(int argc, char *argv[])
348 char *origin, *target, targetds[BE_MAXPATHLEN];
352 while ((opt = getopt(argc, argv, "Fo")) != -1) {
355 flags |= BE_DESTROY_FORCE;
358 flags |= BE_DESTROY_ORIGIN;
361 fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
363 return (usage(false));
371 fprintf(stderr, "bectl destroy: wrong number of arguments\n");
372 return (usage(false));
377 /* We'll emit a notice if there's an origin to be cleaned up */
378 if ((flags & BE_DESTROY_ORIGIN) == 0 && strchr(target, '@') == NULL) {
379 if (be_root_concat(be, target, targetds) != 0)
381 if (be_prop_list_alloc(&props) != 0)
383 if (be_get_dataset_props(be, targetds, props) != 0) {
384 be_prop_list_free(props);
387 if (nvlist_lookup_string(props, "origin", &origin) == 0)
388 fprintf(stderr, "bectl destroy: leaving origin '%s' intact\n",
390 be_prop_list_free(props);
394 err = be_destroy(be, target, flags);
400 bectl_cmd_mount(int argc, char *argv[])
402 char result_loc[BE_MAXPATHLEN];
403 char *bootenv, *mountpoint;
406 /* XXX TODO: Allow shallow */
407 mntflags = BE_MNT_DEEP;
409 fprintf(stderr, "bectl mount: missing argument(s)\n");
410 return (usage(false));
414 fprintf(stderr, "bectl mount: too many arguments\n");
415 return (usage(false));
419 mountpoint = ((argc == 3) ? argv[2] : NULL);
421 err = be_mount(be, bootenv, mountpoint, mntflags, result_loc);
425 printf("successfully mounted %s at %s\n", bootenv, result_loc);
429 (argc == 3) ? "failed to mount bootenv %s at %s\n" :
430 "failed to mount bootenv %s at temporary path %s\n",
431 bootenv, mountpoint);
439 bectl_cmd_rename(int argc, char *argv[])
445 fprintf(stderr, "bectl rename: missing argument\n");
446 return (usage(false));
450 fprintf(stderr, "bectl rename: too many arguments\n");
451 return (usage(false));
457 err = be_rename(be, src, dest);
463 fprintf(stderr, "failed to rename bootenv %s to %s\n",
471 bectl_cmd_unmount(int argc, char *argv[])
476 /* Store alias used */
480 while ((opt = getopt(argc, argv, "f")) != -1) {
483 flags |= BE_MNT_FORCE;
486 fprintf(stderr, "bectl %s: unknown option '-%c'\n",
488 return (usage(false));
496 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
497 return (usage(false));
502 err = be_unmount(be, bootenv, flags);
508 fprintf(stderr, "failed to unmount bootenv %s\n", bootenv);
515 bectl_cmd_check(int argc, char *argv[] __unused)
518 /* The command is left as argv[0] */
520 fprintf(stderr, "bectl check: wrong number of arguments\n");
521 return (usage(false));
528 main(int argc, char *argv[])
530 struct command_map_entry *cmd;
538 return (usage(false));
540 if (strcmp(argv[1], "-r") == 0) {
542 return (usage(false));
543 root = strdup(argv[2]);
553 /* Handle command aliases */
554 if (strcmp(command, "umount") == 0)
557 if (strcmp(command, "ujail") == 0)
560 if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
561 return (usage(true));
563 if ((cmd = get_cmd_info(command)) == NULL) {
564 fprintf(stderr, "unknown command: %s\n", command);
565 return (usage(false));
568 if ((be = libbe_init(root)) == NULL)
571 libbe_print_on_error(be, !cmd->silent);
573 rc = cmd->fn(argc, argv);