xref: /freebsd/sbin/bectl/bectl.c (revision 4c91d6bc44394645f41306277e83be11d856539d)
1b179da01SKyle Evans /*-
2b179da01SKyle Evans  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
35952343eSKyle Evans  *
45952343eSKyle Evans  * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
55952343eSKyle Evans  *
65952343eSKyle Evans  * Redistribution and use in source and binary forms, with or without
75952343eSKyle Evans  * modification, are permitted provided that the following conditions
85952343eSKyle Evans  * are met:
95952343eSKyle Evans  * 1. Redistributions of source code must retain the above copyright
105952343eSKyle Evans  *    notice, this list of conditions and the following disclaimer.
115952343eSKyle Evans  * 2. Redistributions in binary form must reproduce the above copyright
125952343eSKyle Evans  *    notice, this list of conditions and the following disclaimer in the
135952343eSKyle Evans  *    documentation and/or other materials provided with the distribution.
145952343eSKyle Evans  *
155952343eSKyle Evans  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
165952343eSKyle Evans  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
175952343eSKyle Evans  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
185952343eSKyle Evans  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
195952343eSKyle Evans  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
205952343eSKyle Evans  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
215952343eSKyle Evans  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
225952343eSKyle Evans  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
235952343eSKyle Evans  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
245952343eSKyle Evans  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
255952343eSKyle Evans  * SUCH DAMAGE.
265952343eSKyle Evans  */
275952343eSKyle Evans 
28b6e7c421SKyle Evans #include <sys/cdefs.h>
29b6e7c421SKyle Evans __FBSDID("$FreeBSD$");
30b6e7c421SKyle Evans 
315952343eSKyle Evans #include <sys/param.h>
325952343eSKyle Evans #include <sys/mount.h>
335952343eSKyle Evans #include <errno.h>
3483244ec1SKyle Evans #include <libutil.h>
355952343eSKyle Evans #include <stdbool.h>
365952343eSKyle Evans #include <stdio.h>
375952343eSKyle Evans #include <stdint.h>
385952343eSKyle Evans #include <stdlib.h>
395952343eSKyle Evans #include <string.h>
405952343eSKyle Evans #include <sysexits.h>
4183244ec1SKyle Evans #include <time.h>
425952343eSKyle Evans #include <unistd.h>
435952343eSKyle Evans 
445952343eSKyle Evans #include <be.h>
455952343eSKyle Evans 
46d694059fSKyle Evans #include "bectl.h"
47d694059fSKyle Evans 
489e004b21SKyle Evans static int bectl_cmd_activate(int argc, char *argv[]);
49490e13c1SKyle Evans static int bectl_cmd_check(int argc, char *argv[]);
509e004b21SKyle Evans static int bectl_cmd_create(int argc, char *argv[]);
519e004b21SKyle Evans static int bectl_cmd_destroy(int argc, char *argv[]);
529e004b21SKyle Evans static int bectl_cmd_export(int argc, char *argv[]);
539e004b21SKyle Evans static int bectl_cmd_import(int argc, char *argv[]);
543d1a1f2cSKyle Evans #if SOON
559e004b21SKyle Evans static int bectl_cmd_add(int argc, char *argv[]);
563d1a1f2cSKyle Evans #endif
579e004b21SKyle Evans static int bectl_cmd_mount(int argc, char *argv[]);
589e004b21SKyle Evans static int bectl_cmd_rename(int argc, char *argv[]);
599e004b21SKyle Evans static int bectl_cmd_unmount(int argc, char *argv[]);
605952343eSKyle Evans 
61d694059fSKyle Evans libbe_handle_t *be;
625952343eSKyle Evans 
639e5787d2SMatt Macy int aok;
649e5787d2SMatt Macy 
65d694059fSKyle Evans int
665952343eSKyle Evans usage(bool explicit)
675952343eSKyle Evans {
6816a10da8SKyle Evans 	FILE *fp;
695952343eSKyle Evans 
7016a10da8SKyle Evans 	fp =  explicit ? stdout : stderr;
7152ee41b7SYuri Pankov 	fprintf(fp, "%s",
7223614c2bSBenedict Reuschling 	    "Usage:\tbectl {-h | -? | subcommand [args...]}\n"
733d1a1f2cSKyle Evans #if SOON
749e004b21SKyle Evans 	    "\tbectl add (path)*\n"
753d1a1f2cSKyle Evans #endif
7652ee41b7SYuri Pankov 	    "\tbectl activate [-t] beName\n"
77e307eb94SToomas Soome 	    "\tbectl activate [-T]\n"
78490e13c1SKyle Evans 	    "\tbectl check\n"
7952ee41b7SYuri Pankov 	    "\tbectl create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n"
8052ee41b7SYuri Pankov 	    "\tbectl create [-r] beName@snapshot\n"
816c0ecdd0SLuca Pizzamiglio 	    "\tbectl destroy [-Fo] {beName | beName@snapshot}\n"
8252ee41b7SYuri Pankov 	    "\tbectl export sourceBe\n"
8352ee41b7SYuri Pankov 	    "\tbectl import targetBe\n"
8452ee41b7SYuri Pankov 	    "\tbectl jail {-b | -U} [{-o key=value | -u key}]... "
8552ee41b7SYuri Pankov 	    "{jailID | jailName}\n"
8652ee41b7SYuri Pankov 	    "\t      bootenv [utility [argument ...]]\n"
87f0298be0SKyle Evans 	    "\tbectl list [-DHas] [{-c property | -C property}]\n"
889e004b21SKyle Evans 	    "\tbectl mount beName [mountpoint]\n"
899e004b21SKyle Evans 	    "\tbectl rename origBeName newBeName\n"
9052ee41b7SYuri Pankov 	    "\tbectl {ujail | unjail} {jailID | jailName} bootenv\n"
919e004b21SKyle Evans 	    "\tbectl {umount | unmount} [-f] beName\n");
925952343eSKyle Evans 
935952343eSKyle Evans 	return (explicit ? 0 : EX_USAGE);
945952343eSKyle Evans }
955952343eSKyle Evans 
965952343eSKyle Evans 
975952343eSKyle Evans /*
985952343eSKyle Evans  * Represents a relationship between the command name and the parser action
995952343eSKyle Evans  * that handles it.
1005952343eSKyle Evans  */
1015952343eSKyle Evans struct command_map_entry {
1025952343eSKyle Evans 	const char *command;
1035952343eSKyle Evans 	int (*fn)(int argc, char *argv[]);
104490e13c1SKyle Evans 	/* True if libbe_print_on_error should be disabled */
105490e13c1SKyle Evans 	bool silent;
1065952343eSKyle Evans };
1075952343eSKyle Evans 
1085952343eSKyle Evans static struct command_map_entry command_map[] =
1095952343eSKyle Evans {
110490e13c1SKyle Evans 	{ "activate", bectl_cmd_activate,false   },
111490e13c1SKyle Evans 	{ "create",   bectl_cmd_create,  false   },
112490e13c1SKyle Evans 	{ "destroy",  bectl_cmd_destroy, false   },
113490e13c1SKyle Evans 	{ "export",   bectl_cmd_export,  false   },
114490e13c1SKyle Evans 	{ "import",   bectl_cmd_import,  false   },
1153d1a1f2cSKyle Evans #if SOON
116490e13c1SKyle Evans 	{ "add",      bectl_cmd_add,     false   },
1173d1a1f2cSKyle Evans #endif
118490e13c1SKyle Evans 	{ "jail",     bectl_cmd_jail,    false   },
119490e13c1SKyle Evans 	{ "list",     bectl_cmd_list,    false   },
120490e13c1SKyle Evans 	{ "mount",    bectl_cmd_mount,   false   },
121490e13c1SKyle Evans 	{ "rename",   bectl_cmd_rename,  false   },
122490e13c1SKyle Evans 	{ "unjail",   bectl_cmd_unjail,  false   },
123490e13c1SKyle Evans 	{ "unmount",  bectl_cmd_unmount, false   },
124490e13c1SKyle Evans 	{ "check",    bectl_cmd_check,   true    },
1255952343eSKyle Evans };
1265952343eSKyle Evans 
127490e13c1SKyle Evans static struct command_map_entry *
128490e13c1SKyle Evans get_cmd_info(const char *cmd)
1295952343eSKyle Evans {
130490e13c1SKyle Evans 	size_t i;
1315952343eSKyle Evans 
132490e13c1SKyle Evans 	for (i = 0; i < nitems(command_map); ++i) {
133490e13c1SKyle Evans 		if (strcmp(cmd, command_map[i].command) == 0)
134490e13c1SKyle Evans 			return (&command_map[i]);
1355952343eSKyle Evans 	}
1365952343eSKyle Evans 
137490e13c1SKyle Evans 	return (NULL);
1385952343eSKyle Evans }
1395952343eSKyle Evans 
1405952343eSKyle Evans 
1415952343eSKyle Evans static int
1429e004b21SKyle Evans bectl_cmd_activate(int argc, char *argv[])
1435952343eSKyle Evans {
1445952343eSKyle Evans 	int err, opt;
145e307eb94SToomas Soome 	bool temp, reset;
1465952343eSKyle Evans 
1475952343eSKyle Evans 	temp = false;
148e307eb94SToomas Soome 	reset = false;
149e307eb94SToomas Soome 	while ((opt = getopt(argc, argv, "tT")) != -1) {
1505952343eSKyle Evans 		switch (opt) {
1515952343eSKyle Evans 		case 't':
152e307eb94SToomas Soome 			if (reset)
153e307eb94SToomas Soome 				return (usage(false));
1545952343eSKyle Evans 			temp = true;
1555952343eSKyle Evans 			break;
156e307eb94SToomas Soome 		case 'T':
157e307eb94SToomas Soome 			if (temp)
158e307eb94SToomas Soome 				return (usage(false));
159e307eb94SToomas Soome 			reset = true;
160e307eb94SToomas Soome 			break;
1615952343eSKyle Evans 		default:
1622c848957SKyle Evans 			fprintf(stderr, "bectl activate: unknown option '-%c'\n",
1635952343eSKyle Evans 			    optopt);
1645952343eSKyle Evans 			return (usage(false));
1655952343eSKyle Evans 		}
1665952343eSKyle Evans 	}
1675952343eSKyle Evans 
1685952343eSKyle Evans 	argc -= optind;
1695952343eSKyle Evans 	argv += optind;
1705952343eSKyle Evans 
171e307eb94SToomas Soome 	if (argc != 1 && (!reset || argc != 0)) {
1722c848957SKyle Evans 		fprintf(stderr, "bectl activate: wrong number of arguments\n");
1735952343eSKyle Evans 		return (usage(false));
1745952343eSKyle Evans 	}
1755952343eSKyle Evans 
176e307eb94SToomas Soome 	if (reset) {
177e307eb94SToomas Soome 		if ((err = be_deactivate(be, NULL, reset)) == 0)
178e307eb94SToomas Soome 			printf("Temporary activation removed\n");
179e307eb94SToomas Soome 		else
180e307eb94SToomas Soome 			printf("Failed to remove temporary activation\n");
181e307eb94SToomas Soome 		return (err);
182e307eb94SToomas Soome 	}
1835952343eSKyle Evans 
1845952343eSKyle Evans 	/* activate logic goes here */
18516a10da8SKyle Evans 	if ((err = be_activate(be, argv[0], temp)) != 0)
18616a10da8SKyle Evans 		/* XXX TODO: more specific error msg based on err */
18723614c2bSBenedict Reuschling 		printf("Did not successfully activate boot environment %s\n",
1885952343eSKyle Evans 		    argv[0]);
18916a10da8SKyle Evans 	else
19023614c2bSBenedict Reuschling 		printf("Successfully activated boot environment %s\n", argv[0]);
1915952343eSKyle Evans 
19216a10da8SKyle Evans 	if (temp)
1935952343eSKyle Evans 		printf("for next boot\n");
1945952343eSKyle Evans 
1955952343eSKyle Evans 	return (err);
1965952343eSKyle Evans }
1975952343eSKyle Evans 
1985952343eSKyle Evans 
19916a10da8SKyle Evans /*
20016a10da8SKyle Evans  * TODO: when only one arg is given, and it contains an "@" the this should
20116a10da8SKyle Evans  * create that snapshot
20216a10da8SKyle Evans  */
2035952343eSKyle Evans static int
2049e004b21SKyle Evans bectl_cmd_create(int argc, char *argv[])
2055952343eSKyle Evans {
206d05fa0d9SKyle Evans 	char snapshot[BE_MAXPATHLEN];
207d05fa0d9SKyle Evans 	char *atpos, *bootenv, *snapname;
2085952343eSKyle Evans 	int err, opt;
209a9c660b0SKyle Evans 	bool recursive;
2105952343eSKyle Evans 
2115952343eSKyle Evans 	snapname = NULL;
212a9c660b0SKyle Evans 	recursive = false;
21325eeb3eaSKyle Evans 	while ((opt = getopt(argc, argv, "e:r")) != -1) {
2145952343eSKyle Evans 		switch (opt) {
2155952343eSKyle Evans 		case 'e':
2165952343eSKyle Evans 			snapname = optarg;
2175952343eSKyle Evans 			break;
218a9c660b0SKyle Evans 		case 'r':
219a9c660b0SKyle Evans 			recursive = true;
22025eeb3eaSKyle Evans 			break;
2215952343eSKyle Evans 		default:
2222c848957SKyle Evans 			fprintf(stderr, "bectl create: unknown option '-%c'\n",
2235952343eSKyle Evans 			    optopt);
2245952343eSKyle Evans 			return (usage(false));
2255952343eSKyle Evans 		}
2265952343eSKyle Evans 	}
2275952343eSKyle Evans 
2285952343eSKyle Evans 	argc -= optind;
2295952343eSKyle Evans 	argv += optind;
2305952343eSKyle Evans 
2315952343eSKyle Evans 	if (argc != 1) {
2322c848957SKyle Evans 		fprintf(stderr, "bectl create: wrong number of arguments\n");
2335952343eSKyle Evans 		return (usage(false));
2345952343eSKyle Evans 	}
2355952343eSKyle Evans 
2365952343eSKyle Evans 	bootenv = *argv;
237d05fa0d9SKyle Evans 
238d05fa0d9SKyle Evans 	err = BE_ERR_SUCCESS;
239a9c660b0SKyle Evans 	if ((atpos = strchr(bootenv, '@')) != NULL) {
240a9c660b0SKyle Evans 		/*
241a9c660b0SKyle Evans 		 * This is the "create a snapshot variant". No new boot
242a9c660b0SKyle Evans 		 * environment is to be created here.
243a9c660b0SKyle Evans 		 */
244a9c660b0SKyle Evans 		*atpos++ = '\0';
245a9c660b0SKyle Evans 		err = be_snapshot(be, bootenv, atpos, recursive, NULL);
2465952343eSKyle Evans 	} else {
247d05fa0d9SKyle Evans 		if (snapname == NULL)
248d05fa0d9SKyle Evans 			/* Create from currently booted BE */
249d05fa0d9SKyle Evans 			err = be_snapshot(be, be_active_path(be), NULL,
250d05fa0d9SKyle Evans 			    recursive, snapshot);
251d05fa0d9SKyle Evans 		else if (strchr(snapname, '@') != NULL)
252d05fa0d9SKyle Evans 			/* Create from given snapshot */
253d05fa0d9SKyle Evans 			strlcpy(snapshot, snapname, sizeof(snapshot));
254d05fa0d9SKyle Evans 		else
255d05fa0d9SKyle Evans 			/* Create from given BE */
256d05fa0d9SKyle Evans 			err = be_snapshot(be, snapname, NULL, recursive,
257d05fa0d9SKyle Evans 			    snapshot);
258d05fa0d9SKyle Evans 
259d05fa0d9SKyle Evans 		if (err == BE_ERR_SUCCESS)
260d05fa0d9SKyle Evans 			err = be_create_depth(be, bootenv, snapshot,
261d05fa0d9SKyle Evans 					      recursive == true ? -1 : 0);
2625952343eSKyle Evans 	}
2635952343eSKyle Evans 
2645952343eSKyle Evans 	switch (err) {
2655952343eSKyle Evans 	case BE_ERR_SUCCESS:
2665952343eSKyle Evans 		break;
2675952343eSKyle Evans 	default:
268a9c660b0SKyle Evans 		if (atpos != NULL)
269a9c660b0SKyle Evans 			fprintf(stderr,
27023614c2bSBenedict Reuschling 			    "Failed to create a snapshot '%s' of '%s'\n",
271a9c660b0SKyle Evans 			    atpos, bootenv);
272a9c660b0SKyle Evans 		else if (snapname == NULL)
2735952343eSKyle Evans 			fprintf(stderr,
27423614c2bSBenedict Reuschling 			    "Failed to create bootenv %s\n", bootenv);
27516a10da8SKyle Evans 		else
2765952343eSKyle Evans 			fprintf(stderr,
27723614c2bSBenedict Reuschling 			    "Failed to create bootenv %s from snapshot %s\n",
2785952343eSKyle Evans 			    bootenv, snapname);
2795952343eSKyle Evans 	}
2805952343eSKyle Evans 
2815952343eSKyle Evans 	return (err);
2825952343eSKyle Evans }
2835952343eSKyle Evans 
2845952343eSKyle Evans 
2855952343eSKyle Evans static int
2869e004b21SKyle Evans bectl_cmd_export(int argc, char *argv[])
2875952343eSKyle Evans {
2885952343eSKyle Evans 	char *bootenv;
2895952343eSKyle Evans 
2905952343eSKyle Evans 	if (argc == 1) {
2912c848957SKyle Evans 		fprintf(stderr, "bectl export: missing boot environment name\n");
2925952343eSKyle Evans 		return (usage(false));
2935952343eSKyle Evans 	}
2945952343eSKyle Evans 
2955952343eSKyle Evans 	if (argc > 2) {
2962c848957SKyle Evans 		fprintf(stderr, "bectl export: extra arguments provided\n");
2975952343eSKyle Evans 		return (usage(false));
2985952343eSKyle Evans 	}
2995952343eSKyle Evans 
3005952343eSKyle Evans 	bootenv = argv[1];
3015952343eSKyle Evans 
3025952343eSKyle Evans 	if (isatty(STDOUT_FILENO)) {
3032c848957SKyle Evans 		fprintf(stderr, "bectl export: must redirect output\n");
3045952343eSKyle Evans 		return (EX_USAGE);
3055952343eSKyle Evans 	}
3065952343eSKyle Evans 
3075952343eSKyle Evans 	be_export(be, bootenv, STDOUT_FILENO);
3085952343eSKyle Evans 
3095952343eSKyle Evans 	return (0);
3105952343eSKyle Evans }
3115952343eSKyle Evans 
3125952343eSKyle Evans 
3135952343eSKyle Evans static int
3149e004b21SKyle Evans bectl_cmd_import(int argc, char *argv[])
3155952343eSKyle Evans {
3165952343eSKyle Evans 	char *bootenv;
3175952343eSKyle Evans 	int err;
3185952343eSKyle Evans 
3195952343eSKyle Evans 	if (argc == 1) {
3202c848957SKyle Evans 		fprintf(stderr, "bectl import: missing boot environment name\n");
3215952343eSKyle Evans 		return (usage(false));
3225952343eSKyle Evans 	}
3235952343eSKyle Evans 
3245952343eSKyle Evans 	if (argc > 2) {
3252c848957SKyle Evans 		fprintf(stderr, "bectl import: extra arguments provided\n");
3265952343eSKyle Evans 		return (usage(false));
3275952343eSKyle Evans 	}
3285952343eSKyle Evans 
3295952343eSKyle Evans 	bootenv = argv[1];
3305952343eSKyle Evans 
3315952343eSKyle Evans 	if (isatty(STDIN_FILENO)) {
3322c848957SKyle Evans 		fprintf(stderr, "bectl import: input can not be from terminal\n");
3335952343eSKyle Evans 		return (EX_USAGE);
3345952343eSKyle Evans 	}
3355952343eSKyle Evans 
3365952343eSKyle Evans 	err = be_import(be, bootenv, STDIN_FILENO);
3375952343eSKyle Evans 
3385952343eSKyle Evans 	return (err);
3395952343eSKyle Evans }
3405952343eSKyle Evans 
3413d1a1f2cSKyle Evans #if SOON
3425952343eSKyle Evans static int
3439e004b21SKyle Evans bectl_cmd_add(int argc, char *argv[])
3445952343eSKyle Evans {
3455952343eSKyle Evans 
3465952343eSKyle Evans 	if (argc < 2) {
3472c848957SKyle Evans 		fprintf(stderr, "bectl add: must provide at least one path\n");
3485952343eSKyle Evans 		return (usage(false));
3495952343eSKyle Evans 	}
3505952343eSKyle Evans 
3515952343eSKyle Evans 	for (int i = 1; i < argc; ++i) {
3525952343eSKyle Evans 		printf("arg %d: %s\n", i, argv[i]);
35316a10da8SKyle Evans 		/* XXX TODO catch err */
3545952343eSKyle Evans 		be_add_child(be, argv[i], true);
3555952343eSKyle Evans 	}
3565952343eSKyle Evans 
3575952343eSKyle Evans 	return (0);
3585952343eSKyle Evans }
3593d1a1f2cSKyle Evans #endif
3605952343eSKyle Evans 
3615952343eSKyle Evans static int
3629e004b21SKyle Evans bectl_cmd_destroy(int argc, char *argv[])
3635952343eSKyle Evans {
36477b4126cSKyle Evans 	nvlist_t *props;
36577b4126cSKyle Evans 	char *origin, *target, targetds[BE_MAXPATHLEN];
36677b4126cSKyle Evans 	int err, flags, opt;
3675952343eSKyle Evans 
36877b4126cSKyle Evans 	flags = 0;
36977b4126cSKyle Evans 	while ((opt = getopt(argc, argv, "Fo")) != -1) {
3705952343eSKyle Evans 		switch (opt) {
3715952343eSKyle Evans 		case 'F':
37277b4126cSKyle Evans 			flags |= BE_DESTROY_FORCE;
37377b4126cSKyle Evans 			break;
37477b4126cSKyle Evans 		case 'o':
37577b4126cSKyle Evans 			flags |= BE_DESTROY_ORIGIN;
3765952343eSKyle Evans 			break;
3775952343eSKyle Evans 		default:
3782c848957SKyle Evans 			fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
3795952343eSKyle Evans 			    optopt);
3805952343eSKyle Evans 			return (usage(false));
3815952343eSKyle Evans 		}
3825952343eSKyle Evans 	}
3835952343eSKyle Evans 
3845952343eSKyle Evans 	argc -= optind;
3855952343eSKyle Evans 	argv += optind;
3865952343eSKyle Evans 
3875952343eSKyle Evans 	if (argc != 1) {
3882c848957SKyle Evans 		fprintf(stderr, "bectl destroy: wrong number of arguments\n");
3895952343eSKyle Evans 		return (usage(false));
3905952343eSKyle Evans 	}
3915952343eSKyle Evans 
3925952343eSKyle Evans 	target = argv[0];
3935952343eSKyle Evans 
39477b4126cSKyle Evans 	/* We'll emit a notice if there's an origin to be cleaned up */
39577b4126cSKyle Evans 	if ((flags & BE_DESTROY_ORIGIN) == 0 && strchr(target, '@') == NULL) {
3968338f584SKyle Evans 		flags |= BE_DESTROY_AUTOORIGIN;
39777b4126cSKyle Evans 		if (be_root_concat(be, target, targetds) != 0)
39877b4126cSKyle Evans 			goto destroy;
39977b4126cSKyle Evans 		if (be_prop_list_alloc(&props) != 0)
40077b4126cSKyle Evans 			goto destroy;
40177b4126cSKyle Evans 		if (be_get_dataset_props(be, targetds, props) != 0) {
40277b4126cSKyle Evans 			be_prop_list_free(props);
40377b4126cSKyle Evans 			goto destroy;
40477b4126cSKyle Evans 		}
4058338f584SKyle Evans 		if (nvlist_lookup_string(props, "origin", &origin) == 0 &&
4068338f584SKyle Evans 		    !be_is_auto_snapshot_name(be, origin))
40777b4126cSKyle Evans 			fprintf(stderr, "bectl destroy: leaving origin '%s' intact\n",
40877b4126cSKyle Evans 			    origin);
40977b4126cSKyle Evans 		be_prop_list_free(props);
41077b4126cSKyle Evans 	}
41177b4126cSKyle Evans 
41277b4126cSKyle Evans destroy:
41377b4126cSKyle Evans 	err = be_destroy(be, target, flags);
4145952343eSKyle Evans 
4155952343eSKyle Evans 	return (err);
4165952343eSKyle Evans }
4175952343eSKyle Evans 
4185952343eSKyle Evans static int
4199e004b21SKyle Evans bectl_cmd_mount(int argc, char *argv[])
4205952343eSKyle Evans {
4215952343eSKyle Evans 	char result_loc[BE_MAXPATHLEN];
42216a10da8SKyle Evans 	char *bootenv, *mountpoint;
4230a603a6eSKyle Evans 	int err, mntflags;
4245952343eSKyle Evans 
4250a603a6eSKyle Evans 	/* XXX TODO: Allow shallow */
4260a603a6eSKyle Evans 	mntflags = BE_MNT_DEEP;
4275952343eSKyle Evans 	if (argc < 2) {
4282c848957SKyle Evans 		fprintf(stderr, "bectl mount: missing argument(s)\n");
4295952343eSKyle Evans 		return (usage(false));
4305952343eSKyle Evans 	}
4315952343eSKyle Evans 
4325952343eSKyle Evans 	if (argc > 3) {
4332c848957SKyle Evans 		fprintf(stderr, "bectl mount: too many arguments\n");
4345952343eSKyle Evans 		return (usage(false));
4355952343eSKyle Evans 	}
4365952343eSKyle Evans 
4375952343eSKyle Evans 	bootenv = argv[1];
4385952343eSKyle Evans 	mountpoint = ((argc == 3) ? argv[2] : NULL);
4395952343eSKyle Evans 
4400a603a6eSKyle Evans 	err = be_mount(be, bootenv, mountpoint, mntflags, result_loc);
4415952343eSKyle Evans 
4425952343eSKyle Evans 	switch (err) {
4435952343eSKyle Evans 	case BE_ERR_SUCCESS:
44423614c2bSBenedict Reuschling 		printf("Successfully mounted %s at %s\n", bootenv, result_loc);
4455952343eSKyle Evans 		break;
4465952343eSKyle Evans 	default:
4475952343eSKyle Evans 		fprintf(stderr,
44823614c2bSBenedict Reuschling 		    (argc == 3) ? "Failed to mount bootenv %s at %s\n" :
44923614c2bSBenedict Reuschling 		    "Failed to mount bootenv %s at temporary path %s\n",
4505952343eSKyle Evans 		    bootenv, mountpoint);
4515952343eSKyle Evans 	}
4525952343eSKyle Evans 
4535952343eSKyle Evans 	return (err);
4545952343eSKyle Evans }
4555952343eSKyle Evans 
4565952343eSKyle Evans 
4575952343eSKyle Evans static int
4589e004b21SKyle Evans bectl_cmd_rename(int argc, char *argv[])
4595952343eSKyle Evans {
46016a10da8SKyle Evans 	char *dest, *src;
4615952343eSKyle Evans 	int err;
4625952343eSKyle Evans 
4635952343eSKyle Evans 	if (argc < 3) {
4642c848957SKyle Evans 		fprintf(stderr, "bectl rename: missing argument\n");
4655952343eSKyle Evans 		return (usage(false));
4665952343eSKyle Evans 	}
4675952343eSKyle Evans 
4685952343eSKyle Evans 	if (argc > 3) {
4692c848957SKyle Evans 		fprintf(stderr, "bectl rename: too many arguments\n");
4705952343eSKyle Evans 		return (usage(false));
4715952343eSKyle Evans 	}
4725952343eSKyle Evans 
4735952343eSKyle Evans 	src = argv[1];
4745952343eSKyle Evans 	dest = argv[2];
4755952343eSKyle Evans 
4765952343eSKyle Evans 	err = be_rename(be, src, dest);
4775952343eSKyle Evans 
4785952343eSKyle Evans 	switch (err) {
4795952343eSKyle Evans 	case BE_ERR_SUCCESS:
4805952343eSKyle Evans 		break;
4815952343eSKyle Evans 	default:
48223614c2bSBenedict Reuschling 		fprintf(stderr, "Failed to rename bootenv %s to %s\n",
4835952343eSKyle Evans 		    src, dest);
4845952343eSKyle Evans 	}
4855952343eSKyle Evans 
4865952343eSKyle Evans 	return (0);
4875952343eSKyle Evans }
4885952343eSKyle Evans 
489ad765da4SKyle Evans static int
4909e004b21SKyle Evans bectl_cmd_unmount(int argc, char *argv[])
4915952343eSKyle Evans {
49216a10da8SKyle Evans 	char *bootenv, *cmd;
4935952343eSKyle Evans 	int err, flags, opt;
4945952343eSKyle Evans 
4955952343eSKyle Evans 	/* Store alias used */
4965952343eSKyle Evans 	cmd = argv[0];
4975952343eSKyle Evans 
4985952343eSKyle Evans 	flags = 0;
4995952343eSKyle Evans 	while ((opt = getopt(argc, argv, "f")) != -1) {
5005952343eSKyle Evans 		switch (opt) {
5015952343eSKyle Evans 		case 'f':
5025952343eSKyle Evans 			flags |= BE_MNT_FORCE;
5035952343eSKyle Evans 			break;
5045952343eSKyle Evans 		default:
5052c848957SKyle Evans 			fprintf(stderr, "bectl %s: unknown option '-%c'\n",
5065952343eSKyle Evans 			    cmd, optopt);
5075952343eSKyle Evans 			return (usage(false));
5085952343eSKyle Evans 		}
5095952343eSKyle Evans 	}
5105952343eSKyle Evans 
5115952343eSKyle Evans 	argc -= optind;
5125952343eSKyle Evans 	argv += optind;
5135952343eSKyle Evans 
5145952343eSKyle Evans 	if (argc != 1) {
5152c848957SKyle Evans 		fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
5165952343eSKyle Evans 		return (usage(false));
5175952343eSKyle Evans 	}
5185952343eSKyle Evans 
5195952343eSKyle Evans 	bootenv = argv[0];
5205952343eSKyle Evans 
5215952343eSKyle Evans 	err = be_unmount(be, bootenv, flags);
5225952343eSKyle Evans 
5235952343eSKyle Evans 	switch (err) {
5245952343eSKyle Evans 	case BE_ERR_SUCCESS:
5255952343eSKyle Evans 		break;
5265952343eSKyle Evans 	default:
52723614c2bSBenedict Reuschling 		fprintf(stderr, "Failed to unmount bootenv %s\n", bootenv);
5285952343eSKyle Evans 	}
5295952343eSKyle Evans 
5305952343eSKyle Evans 	return (err);
5315952343eSKyle Evans }
5325952343eSKyle Evans 
533490e13c1SKyle Evans static int
534490e13c1SKyle Evans bectl_cmd_check(int argc, char *argv[] __unused)
535490e13c1SKyle Evans {
536490e13c1SKyle Evans 
537490e13c1SKyle Evans 	/* The command is left as argv[0] */
538490e13c1SKyle Evans 	if (argc != 1) {
539490e13c1SKyle Evans 		fprintf(stderr, "bectl check: wrong number of arguments\n");
540490e13c1SKyle Evans 		return (usage(false));
541490e13c1SKyle Evans 	}
542490e13c1SKyle Evans 
543490e13c1SKyle Evans 	return (0);
544490e13c1SKyle Evans }
5455952343eSKyle Evans 
5465952343eSKyle Evans int
5475952343eSKyle Evans main(int argc, char *argv[])
5485952343eSKyle Evans {
549490e13c1SKyle Evans 	struct command_map_entry *cmd;
550b29bf2f8SKyle Evans 	const char *command;
551cc624025SKyle Evans 	char *root;
552490e13c1SKyle Evans 	int rc;
5535952343eSKyle Evans 
554490e13c1SKyle Evans 	cmd = NULL;
555cc624025SKyle Evans 	root = NULL;
5568369ba42SKyle Evans 	if (argc < 2)
5575952343eSKyle Evans 		return (usage(false));
5585952343eSKyle Evans 
559cc624025SKyle Evans 	if (strcmp(argv[1], "-r") == 0) {
560cc624025SKyle Evans 		if (argc < 4)
561cc624025SKyle Evans 			return (usage(false));
562cc624025SKyle Evans 		root = strdup(argv[2]);
563cc624025SKyle Evans 		command = argv[3];
564cc624025SKyle Evans 		argc -= 3;
565cc624025SKyle Evans 		argv += 3;
566cc624025SKyle Evans 	} else {
5675952343eSKyle Evans 		command = argv[1];
568cc624025SKyle Evans 		argc -= 1;
569cc624025SKyle Evans 		argv += 1;
570cc624025SKyle Evans 	}
5715952343eSKyle Evans 
5725952343eSKyle Evans 	/* Handle command aliases */
57316a10da8SKyle Evans 	if (strcmp(command, "umount") == 0)
5745952343eSKyle Evans 		command = "unmount";
5755952343eSKyle Evans 
57616a10da8SKyle Evans 	if (strcmp(command, "ujail") == 0)
5775952343eSKyle Evans 		command = "unjail";
5785952343eSKyle Evans 
57916a10da8SKyle Evans 	if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
5805952343eSKyle Evans 		return (usage(true));
5815952343eSKyle Evans 
582490e13c1SKyle Evans 	if ((cmd = get_cmd_info(command)) == NULL) {
58323614c2bSBenedict Reuschling 		fprintf(stderr, "Unknown command: %s\n", command);
5845952343eSKyle Evans 		return (usage(false));
5855952343eSKyle Evans 	}
5865952343eSKyle Evans 
587*4c91d6bcSGleb Smirnoff 	if ((be = libbe_init(root)) == NULL) {
588*4c91d6bcSGleb Smirnoff 		fprintf(stderr, "libbe_init(\"%s\") failed.\n",
589*4c91d6bcSGleb Smirnoff 		    root != NULL ? root : "");
5905952343eSKyle Evans 		return (-1);
591*4c91d6bcSGleb Smirnoff 	}
5925952343eSKyle Evans 
593490e13c1SKyle Evans 	libbe_print_on_error(be, !cmd->silent);
5945952343eSKyle Evans 
595490e13c1SKyle Evans 	rc = cmd->fn(argc, argv);
5965952343eSKyle Evans 
5975952343eSKyle Evans 	libbe_close(be);
5985952343eSKyle Evans 	return (rc);
5995952343eSKyle Evans }
600