xref: /freebsd/sbin/bectl/bectl.c (revision 760987ecd39b54374aef40783b2c232997f3ac04)
1b179da01SKyle Evans /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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>
295952343eSKyle Evans #include <sys/param.h>
305952343eSKyle Evans #include <sys/mount.h>
315952343eSKyle Evans #include <errno.h>
3283244ec1SKyle Evans #include <libutil.h>
335952343eSKyle Evans #include <stdbool.h>
345952343eSKyle Evans #include <stdio.h>
355952343eSKyle Evans #include <stdint.h>
365952343eSKyle Evans #include <stdlib.h>
375952343eSKyle Evans #include <string.h>
385952343eSKyle Evans #include <sysexits.h>
3983244ec1SKyle Evans #include <time.h>
405952343eSKyle Evans #include <unistd.h>
415952343eSKyle Evans 
425952343eSKyle Evans #include <be.h>
435952343eSKyle Evans 
44d694059fSKyle Evans #include "bectl.h"
45d694059fSKyle Evans 
469e004b21SKyle Evans static int bectl_cmd_activate(int argc, char *argv[]);
47490e13c1SKyle Evans static int bectl_cmd_check(int argc, char *argv[]);
489e004b21SKyle Evans static int bectl_cmd_create(int argc, char *argv[]);
499e004b21SKyle Evans static int bectl_cmd_destroy(int argc, char *argv[]);
509e004b21SKyle Evans static int bectl_cmd_export(int argc, char *argv[]);
519e004b21SKyle Evans static int bectl_cmd_import(int argc, char *argv[]);
523d1a1f2cSKyle Evans #if SOON
539e004b21SKyle Evans static int bectl_cmd_add(int argc, char *argv[]);
543d1a1f2cSKyle Evans #endif
559e004b21SKyle Evans static int bectl_cmd_mount(int argc, char *argv[]);
569e004b21SKyle Evans static int bectl_cmd_rename(int argc, char *argv[]);
579e004b21SKyle Evans static int bectl_cmd_unmount(int argc, char *argv[]);
585952343eSKyle Evans 
59d694059fSKyle Evans libbe_handle_t *be;
605952343eSKyle Evans 
61d694059fSKyle Evans int
625952343eSKyle Evans usage(bool explicit)
635952343eSKyle Evans {
6416a10da8SKyle Evans 	FILE *fp;
655952343eSKyle Evans 
6616a10da8SKyle Evans 	fp =  explicit ? stdout : stderr;
6752ee41b7SYuri Pankov 	fprintf(fp, "%s",
6823614c2bSBenedict Reuschling 	    "Usage:\tbectl {-h | -? | subcommand [args...]}\n"
693d1a1f2cSKyle Evans #if SOON
704163bae0SKyle Evans 	    "\tbectl [-r beroot] add (path)*\n"
713d1a1f2cSKyle Evans #endif
724163bae0SKyle Evans 	    "\tbectl [-r beroot] activate [-t] beName\n"
734163bae0SKyle Evans 	    "\tbectl [-r beroot] activate [-T]\n"
744163bae0SKyle Evans 	    "\tbectl [-r beroot] check\n"
754163bae0SKyle Evans 	    "\tbectl [-r beroot] create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n"
764163bae0SKyle Evans 	    "\tbectl [-r beroot] create [-r] beName@snapshot\n"
774163bae0SKyle Evans 	    "\tbectl [-r beroot] destroy [-Fo] {beName | beName@snapshot}\n"
784163bae0SKyle Evans 	    "\tbectl [-r beroot] export sourceBe\n"
794163bae0SKyle Evans 	    "\tbectl [-r beroot] import targetBe\n"
804163bae0SKyle Evans 	    "\tbectl [-r beroot] jail [-bU] [{-o key=value | -u key}]... beName\n"
81b1ea63e2SRobert Wing 	    "\t      [utility [argument ...]]\n"
824163bae0SKyle Evans 	    "\tbectl [-r beroot] list [-aDHs] [{-c property | -C property}]\n"
834163bae0SKyle Evans 	    "\tbectl [-r beroot] mount beName [mountpoint]\n"
844163bae0SKyle Evans 	    "\tbectl [-r beroot] rename origBeName newBeName\n"
854163bae0SKyle Evans 	    "\tbectl [-r beroot] {ujail | unjail} {jailID | jailName | beName}\n"
864163bae0SKyle Evans 	    "\tbectl [-r beroot] {umount | unmount} [-f] beName\n");
875952343eSKyle Evans 
885952343eSKyle Evans 	return (explicit ? 0 : EX_USAGE);
895952343eSKyle Evans }
905952343eSKyle Evans 
915952343eSKyle Evans 
925952343eSKyle Evans /*
935952343eSKyle Evans  * Represents a relationship between the command name and the parser action
945952343eSKyle Evans  * that handles it.
955952343eSKyle Evans  */
965952343eSKyle Evans struct command_map_entry {
975952343eSKyle Evans 	const char *command;
985952343eSKyle Evans 	int (*fn)(int argc, char *argv[]);
99490e13c1SKyle Evans 	/* True if libbe_print_on_error should be disabled */
100490e13c1SKyle Evans 	bool silent;
1015952343eSKyle Evans };
1025952343eSKyle Evans 
1035952343eSKyle Evans static struct command_map_entry command_map[] =
1045952343eSKyle Evans {
105490e13c1SKyle Evans 	{ "activate", bectl_cmd_activate,false   },
106490e13c1SKyle Evans 	{ "create",   bectl_cmd_create,  false   },
107490e13c1SKyle Evans 	{ "destroy",  bectl_cmd_destroy, false   },
108490e13c1SKyle Evans 	{ "export",   bectl_cmd_export,  false   },
109490e13c1SKyle Evans 	{ "import",   bectl_cmd_import,  false   },
1103d1a1f2cSKyle Evans #if SOON
111490e13c1SKyle Evans 	{ "add",      bectl_cmd_add,     false   },
1123d1a1f2cSKyle Evans #endif
113490e13c1SKyle Evans 	{ "jail",     bectl_cmd_jail,    false   },
114490e13c1SKyle Evans 	{ "list",     bectl_cmd_list,    false   },
115490e13c1SKyle Evans 	{ "mount",    bectl_cmd_mount,   false   },
116490e13c1SKyle Evans 	{ "rename",   bectl_cmd_rename,  false   },
117490e13c1SKyle Evans 	{ "unjail",   bectl_cmd_unjail,  false   },
118490e13c1SKyle Evans 	{ "unmount",  bectl_cmd_unmount, false   },
119490e13c1SKyle Evans 	{ "check",    bectl_cmd_check,   true    },
1205952343eSKyle Evans };
1215952343eSKyle Evans 
122490e13c1SKyle Evans static struct command_map_entry *
123490e13c1SKyle Evans get_cmd_info(const char *cmd)
1245952343eSKyle Evans {
125490e13c1SKyle Evans 	size_t i;
1265952343eSKyle Evans 
127490e13c1SKyle Evans 	for (i = 0; i < nitems(command_map); ++i) {
128490e13c1SKyle Evans 		if (strcmp(cmd, command_map[i].command) == 0)
129490e13c1SKyle Evans 			return (&command_map[i]);
1305952343eSKyle Evans 	}
1315952343eSKyle Evans 
132490e13c1SKyle Evans 	return (NULL);
1335952343eSKyle Evans }
1345952343eSKyle Evans 
1355952343eSKyle Evans static int
1369e004b21SKyle Evans bectl_cmd_activate(int argc, char *argv[])
1375952343eSKyle Evans {
1385952343eSKyle Evans 	int err, opt;
139e307eb94SToomas Soome 	bool temp, reset;
1405952343eSKyle Evans 
1415952343eSKyle Evans 	temp = false;
142e307eb94SToomas Soome 	reset = false;
143e307eb94SToomas Soome 	while ((opt = getopt(argc, argv, "tT")) != -1) {
1445952343eSKyle Evans 		switch (opt) {
1455952343eSKyle Evans 		case 't':
146e307eb94SToomas Soome 			if (reset)
147e307eb94SToomas Soome 				return (usage(false));
1485952343eSKyle Evans 			temp = true;
1495952343eSKyle Evans 			break;
150e307eb94SToomas Soome 		case 'T':
151e307eb94SToomas Soome 			if (temp)
152e307eb94SToomas Soome 				return (usage(false));
153e307eb94SToomas Soome 			reset = true;
154e307eb94SToomas Soome 			break;
1555952343eSKyle Evans 		default:
1562c848957SKyle Evans 			fprintf(stderr, "bectl activate: unknown option '-%c'\n",
1575952343eSKyle Evans 			    optopt);
1585952343eSKyle Evans 			return (usage(false));
1595952343eSKyle Evans 		}
1605952343eSKyle Evans 	}
1615952343eSKyle Evans 
1625952343eSKyle Evans 	argc -= optind;
1635952343eSKyle Evans 	argv += optind;
1645952343eSKyle Evans 
165e307eb94SToomas Soome 	if (argc != 1 && (!reset || argc != 0)) {
1662c848957SKyle Evans 		fprintf(stderr, "bectl activate: wrong number of arguments\n");
1675952343eSKyle Evans 		return (usage(false));
1685952343eSKyle Evans 	}
1695952343eSKyle Evans 
170e307eb94SToomas Soome 	if (reset) {
171e307eb94SToomas Soome 		if ((err = be_deactivate(be, NULL, reset)) == 0)
172e307eb94SToomas Soome 			printf("Temporary activation removed\n");
173e307eb94SToomas Soome 		else
174e307eb94SToomas Soome 			printf("Failed to remove temporary activation\n");
175e307eb94SToomas Soome 		return (err);
176e307eb94SToomas Soome 	}
1775952343eSKyle Evans 
1785952343eSKyle Evans 	/* activate logic goes here */
17916a10da8SKyle Evans 	if ((err = be_activate(be, argv[0], temp)) != 0)
18016a10da8SKyle Evans 		/* XXX TODO: more specific error msg based on err */
18123614c2bSBenedict Reuschling 		printf("Did not successfully activate boot environment %s\n",
1825952343eSKyle Evans 		    argv[0]);
18316a10da8SKyle Evans 	else
18423614c2bSBenedict Reuschling 		printf("Successfully activated boot environment %s\n", argv[0]);
1855952343eSKyle Evans 
18616a10da8SKyle Evans 	if (temp)
1875952343eSKyle Evans 		printf("for next boot\n");
1885952343eSKyle Evans 
1895952343eSKyle Evans 	return (err);
1905952343eSKyle Evans }
1915952343eSKyle Evans 
1925952343eSKyle Evans 
19316a10da8SKyle Evans /*
19416a10da8SKyle Evans  * TODO: when only one arg is given, and it contains an "@" the this should
19516a10da8SKyle Evans  * create that snapshot
19616a10da8SKyle Evans  */
1975952343eSKyle Evans static int
1989e004b21SKyle Evans bectl_cmd_create(int argc, char *argv[])
1995952343eSKyle Evans {
200d05fa0d9SKyle Evans 	char snapshot[BE_MAXPATHLEN];
201d05fa0d9SKyle Evans 	char *atpos, *bootenv, *snapname;
2025952343eSKyle Evans 	int err, opt;
203a9c660b0SKyle Evans 	bool recursive;
2045952343eSKyle Evans 
2055952343eSKyle Evans 	snapname = NULL;
206a9c660b0SKyle Evans 	recursive = false;
20725eeb3eaSKyle Evans 	while ((opt = getopt(argc, argv, "e:r")) != -1) {
2085952343eSKyle Evans 		switch (opt) {
2095952343eSKyle Evans 		case 'e':
2105952343eSKyle Evans 			snapname = optarg;
2115952343eSKyle Evans 			break;
212a9c660b0SKyle Evans 		case 'r':
213a9c660b0SKyle Evans 			recursive = true;
21425eeb3eaSKyle Evans 			break;
2155952343eSKyle Evans 		default:
2162c848957SKyle Evans 			fprintf(stderr, "bectl create: unknown option '-%c'\n",
2175952343eSKyle Evans 			    optopt);
2185952343eSKyle Evans 			return (usage(false));
2195952343eSKyle Evans 		}
2205952343eSKyle Evans 	}
2215952343eSKyle Evans 
2225952343eSKyle Evans 	argc -= optind;
2235952343eSKyle Evans 	argv += optind;
2245952343eSKyle Evans 
2255952343eSKyle Evans 	if (argc != 1) {
2262c848957SKyle Evans 		fprintf(stderr, "bectl create: wrong number of arguments\n");
2275952343eSKyle Evans 		return (usage(false));
2285952343eSKyle Evans 	}
2295952343eSKyle Evans 
2305952343eSKyle Evans 	bootenv = *argv;
231d05fa0d9SKyle Evans 
232d05fa0d9SKyle Evans 	err = BE_ERR_SUCCESS;
233dadb9c70SKyle Evans 	if ((atpos = strchr(bootenv, '@')) != NULL) {
234a9c660b0SKyle Evans 		/*
235a9c660b0SKyle Evans 		 * This is the "create a snapshot variant". No new boot
236a9c660b0SKyle Evans 		 * environment is to be created here.
237a9c660b0SKyle Evans 		 */
238a9c660b0SKyle Evans 		*atpos++ = '\0';
239a9c660b0SKyle Evans 		err = be_snapshot(be, bootenv, atpos, recursive, NULL);
2405952343eSKyle Evans 	} else {
241d05fa0d9SKyle Evans 		if (snapname == NULL)
242d05fa0d9SKyle Evans 			/* Create from currently booted BE */
243d05fa0d9SKyle Evans 			err = be_snapshot(be, be_active_path(be), NULL,
244d05fa0d9SKyle Evans 			    recursive, snapshot);
245d05fa0d9SKyle Evans 		else if (strchr(snapname, '@') != NULL)
246d05fa0d9SKyle Evans 			/* Create from given snapshot */
247d05fa0d9SKyle Evans 			strlcpy(snapshot, snapname, sizeof(snapshot));
248d05fa0d9SKyle Evans 		else
249d05fa0d9SKyle Evans 			/* Create from given BE */
250d05fa0d9SKyle Evans 			err = be_snapshot(be, snapname, NULL, recursive,
251d05fa0d9SKyle Evans 			    snapshot);
252d05fa0d9SKyle Evans 
253d05fa0d9SKyle Evans 		if (err == BE_ERR_SUCCESS)
254d05fa0d9SKyle Evans 			err = be_create_depth(be, bootenv, snapshot,
255d05fa0d9SKyle Evans 					      recursive == true ? -1 : 0);
2565952343eSKyle Evans 	}
2575952343eSKyle Evans 
2585952343eSKyle Evans 	switch (err) {
2595952343eSKyle Evans 	case BE_ERR_SUCCESS:
2605952343eSKyle Evans 		break;
2610e6549c8SRobert Wing 	case BE_ERR_INVALIDNAME:
2620e6549c8SRobert Wing 		fprintf(stderr,
2630e6549c8SRobert Wing 		    "bectl create: boot environment name must not contain spaces\n");
2640e6549c8SRobert Wing 		break;
2655952343eSKyle Evans 	default:
266a9c660b0SKyle Evans 		if (atpos != NULL)
267a9c660b0SKyle Evans 			fprintf(stderr,
26823614c2bSBenedict Reuschling 			    "Failed to create a snapshot '%s' of '%s'\n",
269a9c660b0SKyle Evans 			    atpos, bootenv);
270a9c660b0SKyle Evans 		else if (snapname == NULL)
2715952343eSKyle Evans 			fprintf(stderr,
27223614c2bSBenedict Reuschling 			    "Failed to create bootenv %s\n", bootenv);
27316a10da8SKyle Evans 		else
2745952343eSKyle Evans 			fprintf(stderr,
27523614c2bSBenedict Reuschling 			    "Failed to create bootenv %s from snapshot %s\n",
2765952343eSKyle Evans 			    bootenv, snapname);
2775952343eSKyle Evans 	}
2785952343eSKyle Evans 
2795952343eSKyle Evans 	return (err);
2805952343eSKyle Evans }
2815952343eSKyle Evans 
2825952343eSKyle Evans 
2835952343eSKyle Evans static int
2849e004b21SKyle Evans bectl_cmd_export(int argc, char *argv[])
2855952343eSKyle Evans {
2865952343eSKyle Evans 	char *bootenv;
2875952343eSKyle Evans 
2885952343eSKyle Evans 	if (argc == 1) {
2892c848957SKyle Evans 		fprintf(stderr, "bectl export: missing boot environment name\n");
2905952343eSKyle Evans 		return (usage(false));
2915952343eSKyle Evans 	}
2925952343eSKyle Evans 
2935952343eSKyle Evans 	if (argc > 2) {
2942c848957SKyle Evans 		fprintf(stderr, "bectl export: extra arguments provided\n");
2955952343eSKyle Evans 		return (usage(false));
2965952343eSKyle Evans 	}
2975952343eSKyle Evans 
2985952343eSKyle Evans 	bootenv = argv[1];
2995952343eSKyle Evans 
3005952343eSKyle Evans 	if (isatty(STDOUT_FILENO)) {
3012c848957SKyle Evans 		fprintf(stderr, "bectl export: must redirect output\n");
3025952343eSKyle Evans 		return (EX_USAGE);
3035952343eSKyle Evans 	}
3045952343eSKyle Evans 
3055952343eSKyle Evans 	be_export(be, bootenv, STDOUT_FILENO);
3065952343eSKyle Evans 
3075952343eSKyle Evans 	return (0);
3085952343eSKyle Evans }
3095952343eSKyle Evans 
3105952343eSKyle Evans 
3115952343eSKyle Evans static int
3129e004b21SKyle Evans bectl_cmd_import(int argc, char *argv[])
3135952343eSKyle Evans {
3145952343eSKyle Evans 	char *bootenv;
3155952343eSKyle Evans 	int err;
3165952343eSKyle Evans 
3175952343eSKyle Evans 	if (argc == 1) {
3182c848957SKyle Evans 		fprintf(stderr, "bectl import: missing boot environment name\n");
3195952343eSKyle Evans 		return (usage(false));
3205952343eSKyle Evans 	}
3215952343eSKyle Evans 
3225952343eSKyle Evans 	if (argc > 2) {
3232c848957SKyle Evans 		fprintf(stderr, "bectl import: extra arguments provided\n");
3245952343eSKyle Evans 		return (usage(false));
3255952343eSKyle Evans 	}
3265952343eSKyle Evans 
3275952343eSKyle Evans 	bootenv = argv[1];
3285952343eSKyle Evans 
3295952343eSKyle Evans 	if (isatty(STDIN_FILENO)) {
3302c848957SKyle Evans 		fprintf(stderr, "bectl import: input can not be from terminal\n");
3315952343eSKyle Evans 		return (EX_USAGE);
3325952343eSKyle Evans 	}
3335952343eSKyle Evans 
3345952343eSKyle Evans 	err = be_import(be, bootenv, STDIN_FILENO);
3355952343eSKyle Evans 
3365952343eSKyle Evans 	return (err);
3375952343eSKyle Evans }
3385952343eSKyle Evans 
3393d1a1f2cSKyle Evans #if SOON
3405952343eSKyle Evans static int
3419e004b21SKyle Evans bectl_cmd_add(int argc, char *argv[])
3425952343eSKyle Evans {
3435952343eSKyle Evans 
3445952343eSKyle Evans 	if (argc < 2) {
3452c848957SKyle Evans 		fprintf(stderr, "bectl add: must provide at least one path\n");
3465952343eSKyle Evans 		return (usage(false));
3475952343eSKyle Evans 	}
3485952343eSKyle Evans 
3495952343eSKyle Evans 	for (int i = 1; i < argc; ++i) {
3505952343eSKyle Evans 		printf("arg %d: %s\n", i, argv[i]);
35116a10da8SKyle Evans 		/* XXX TODO catch err */
3525952343eSKyle Evans 		be_add_child(be, argv[i], true);
3535952343eSKyle Evans 	}
3545952343eSKyle Evans 
3555952343eSKyle Evans 	return (0);
3565952343eSKyle Evans }
3573d1a1f2cSKyle Evans #endif
3585952343eSKyle Evans 
3595952343eSKyle Evans static int
3609e004b21SKyle Evans bectl_cmd_destroy(int argc, char *argv[])
3615952343eSKyle Evans {
36277b4126cSKyle Evans 	nvlist_t *props;
3632a58b312SMartin Matuska 	char *target, targetds[BE_MAXPATHLEN];
3642a58b312SMartin Matuska 	const char *origin;
36577b4126cSKyle Evans 	int err, flags, opt;
3665952343eSKyle Evans 
36777b4126cSKyle Evans 	flags = 0;
36877b4126cSKyle Evans 	while ((opt = getopt(argc, argv, "Fo")) != -1) {
3695952343eSKyle Evans 		switch (opt) {
3705952343eSKyle Evans 		case 'F':
37177b4126cSKyle Evans 			flags |= BE_DESTROY_FORCE;
37277b4126cSKyle Evans 			break;
37377b4126cSKyle Evans 		case 'o':
37477b4126cSKyle Evans 			flags |= BE_DESTROY_ORIGIN;
3755952343eSKyle Evans 			break;
3765952343eSKyle Evans 		default:
3772c848957SKyle Evans 			fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
3785952343eSKyle Evans 			    optopt);
3795952343eSKyle Evans 			return (usage(false));
3805952343eSKyle Evans 		}
3815952343eSKyle Evans 	}
3825952343eSKyle Evans 
3835952343eSKyle Evans 	argc -= optind;
3845952343eSKyle Evans 	argv += optind;
3855952343eSKyle Evans 
3865952343eSKyle Evans 	if (argc != 1) {
3872c848957SKyle Evans 		fprintf(stderr, "bectl destroy: wrong number of arguments\n");
3885952343eSKyle Evans 		return (usage(false));
3895952343eSKyle Evans 	}
3905952343eSKyle Evans 
3915952343eSKyle Evans 	target = argv[0];
3925952343eSKyle Evans 
39377b4126cSKyle Evans 	/* We'll emit a notice if there's an origin to be cleaned up */
39477b4126cSKyle Evans 	if ((flags & BE_DESTROY_ORIGIN) == 0 && strchr(target, '@') == NULL) {
3958338f584SKyle Evans 		flags |= BE_DESTROY_AUTOORIGIN;
39677b4126cSKyle Evans 		if (be_root_concat(be, target, targetds) != 0)
39777b4126cSKyle Evans 			goto destroy;
39877b4126cSKyle Evans 		if (be_prop_list_alloc(&props) != 0)
39977b4126cSKyle Evans 			goto destroy;
40077b4126cSKyle Evans 		if (be_get_dataset_props(be, targetds, props) != 0) {
40177b4126cSKyle Evans 			be_prop_list_free(props);
40277b4126cSKyle Evans 			goto destroy;
40377b4126cSKyle Evans 		}
4048338f584SKyle Evans 		if (nvlist_lookup_string(props, "origin", &origin) == 0 &&
4058338f584SKyle Evans 		    !be_is_auto_snapshot_name(be, origin))
40677b4126cSKyle Evans 			fprintf(stderr, "bectl destroy: leaving origin '%s' intact\n",
40777b4126cSKyle Evans 			    origin);
40877b4126cSKyle Evans 		be_prop_list_free(props);
40977b4126cSKyle Evans 	}
41077b4126cSKyle Evans 
41177b4126cSKyle Evans destroy:
41277b4126cSKyle Evans 	err = be_destroy(be, target, flags);
4135952343eSKyle Evans 
4145952343eSKyle Evans 	return (err);
4155952343eSKyle Evans }
4165952343eSKyle Evans 
4175952343eSKyle Evans static int
4189e004b21SKyle Evans bectl_cmd_mount(int argc, char *argv[])
4195952343eSKyle Evans {
4205952343eSKyle Evans 	char result_loc[BE_MAXPATHLEN];
42116a10da8SKyle Evans 	char *bootenv, *mountpoint;
4220a603a6eSKyle Evans 	int err, mntflags;
4235952343eSKyle Evans 
4240a603a6eSKyle Evans 	/* XXX TODO: Allow shallow */
4250a603a6eSKyle Evans 	mntflags = BE_MNT_DEEP;
4265952343eSKyle Evans 	if (argc < 2) {
4272c848957SKyle Evans 		fprintf(stderr, "bectl mount: missing argument(s)\n");
4285952343eSKyle Evans 		return (usage(false));
4295952343eSKyle Evans 	}
4305952343eSKyle Evans 
4315952343eSKyle Evans 	if (argc > 3) {
4322c848957SKyle Evans 		fprintf(stderr, "bectl mount: too many arguments\n");
4335952343eSKyle Evans 		return (usage(false));
4345952343eSKyle Evans 	}
4355952343eSKyle Evans 
4365952343eSKyle Evans 	bootenv = argv[1];
4375952343eSKyle Evans 	mountpoint = ((argc == 3) ? argv[2] : NULL);
4385952343eSKyle Evans 
4390a603a6eSKyle Evans 	err = be_mount(be, bootenv, mountpoint, mntflags, result_loc);
4405952343eSKyle Evans 
4415952343eSKyle Evans 	switch (err) {
4425952343eSKyle Evans 	case BE_ERR_SUCCESS:
443*760987ecSRobert Wing 		printf("%s\n", result_loc);
4445952343eSKyle Evans 		break;
4455952343eSKyle Evans 	default:
4465952343eSKyle Evans 		fprintf(stderr,
44723614c2bSBenedict Reuschling 		    (argc == 3) ? "Failed to mount bootenv %s at %s\n" :
44823614c2bSBenedict Reuschling 		    "Failed to mount bootenv %s at temporary path %s\n",
4495952343eSKyle Evans 		    bootenv, mountpoint);
4505952343eSKyle Evans 	}
4515952343eSKyle Evans 
4525952343eSKyle Evans 	return (err);
4535952343eSKyle Evans }
4545952343eSKyle Evans 
4555952343eSKyle Evans 
4565952343eSKyle Evans static int
4579e004b21SKyle Evans bectl_cmd_rename(int argc, char *argv[])
4585952343eSKyle Evans {
45916a10da8SKyle Evans 	char *dest, *src;
4605952343eSKyle Evans 	int err;
4615952343eSKyle Evans 
4625952343eSKyle Evans 	if (argc < 3) {
4632c848957SKyle Evans 		fprintf(stderr, "bectl rename: missing argument\n");
4645952343eSKyle Evans 		return (usage(false));
4655952343eSKyle Evans 	}
4665952343eSKyle Evans 
4675952343eSKyle Evans 	if (argc > 3) {
4682c848957SKyle Evans 		fprintf(stderr, "bectl rename: too many arguments\n");
4695952343eSKyle Evans 		return (usage(false));
4705952343eSKyle Evans 	}
4715952343eSKyle Evans 
4725952343eSKyle Evans 	src = argv[1];
4735952343eSKyle Evans 	dest = argv[2];
4745952343eSKyle Evans 
4755952343eSKyle Evans 	err = be_rename(be, src, dest);
4765952343eSKyle Evans 	switch (err) {
4775952343eSKyle Evans 	case BE_ERR_SUCCESS:
4785952343eSKyle Evans 		break;
4795952343eSKyle Evans 	default:
48023614c2bSBenedict Reuschling 		fprintf(stderr, "Failed to rename bootenv %s to %s\n",
4815952343eSKyle Evans 		    src, dest);
4825952343eSKyle Evans 	}
4835952343eSKyle Evans 
484dadb9c70SKyle Evans 	return (err);
4855952343eSKyle Evans }
4865952343eSKyle Evans 
487ad765da4SKyle Evans static int
4889e004b21SKyle Evans bectl_cmd_unmount(int argc, char *argv[])
4895952343eSKyle Evans {
49016a10da8SKyle Evans 	char *bootenv, *cmd;
4915952343eSKyle Evans 	int err, flags, opt;
4925952343eSKyle Evans 
4935952343eSKyle Evans 	/* Store alias used */
4945952343eSKyle Evans 	cmd = argv[0];
4955952343eSKyle Evans 
4965952343eSKyle Evans 	flags = 0;
4975952343eSKyle Evans 	while ((opt = getopt(argc, argv, "f")) != -1) {
4985952343eSKyle Evans 		switch (opt) {
4995952343eSKyle Evans 		case 'f':
5005952343eSKyle Evans 			flags |= BE_MNT_FORCE;
5015952343eSKyle Evans 			break;
5025952343eSKyle Evans 		default:
5032c848957SKyle Evans 			fprintf(stderr, "bectl %s: unknown option '-%c'\n",
5045952343eSKyle Evans 			    cmd, optopt);
5055952343eSKyle Evans 			return (usage(false));
5065952343eSKyle Evans 		}
5075952343eSKyle Evans 	}
5085952343eSKyle Evans 
5095952343eSKyle Evans 	argc -= optind;
5105952343eSKyle Evans 	argv += optind;
5115952343eSKyle Evans 
5125952343eSKyle Evans 	if (argc != 1) {
5132c848957SKyle Evans 		fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
5145952343eSKyle Evans 		return (usage(false));
5155952343eSKyle Evans 	}
5165952343eSKyle Evans 
5175952343eSKyle Evans 	bootenv = argv[0];
5185952343eSKyle Evans 
5195952343eSKyle Evans 	err = be_unmount(be, bootenv, flags);
5205952343eSKyle Evans 
5215952343eSKyle Evans 	switch (err) {
5225952343eSKyle Evans 	case BE_ERR_SUCCESS:
5235952343eSKyle Evans 		break;
5245952343eSKyle Evans 	default:
52523614c2bSBenedict Reuschling 		fprintf(stderr, "Failed to unmount bootenv %s\n", bootenv);
5265952343eSKyle Evans 	}
5275952343eSKyle Evans 
5285952343eSKyle Evans 	return (err);
5295952343eSKyle Evans }
5305952343eSKyle Evans 
531490e13c1SKyle Evans static int
532490e13c1SKyle Evans bectl_cmd_check(int argc, char *argv[] __unused)
533490e13c1SKyle Evans {
534490e13c1SKyle Evans 
535490e13c1SKyle Evans 	/* The command is left as argv[0] */
536490e13c1SKyle Evans 	if (argc != 1) {
537490e13c1SKyle Evans 		fprintf(stderr, "bectl check: wrong number of arguments\n");
538490e13c1SKyle Evans 		return (usage(false));
539490e13c1SKyle Evans 	}
540490e13c1SKyle Evans 
541490e13c1SKyle Evans 	return (0);
542490e13c1SKyle Evans }
5435952343eSKyle Evans 
5445952343eSKyle Evans int
5455952343eSKyle Evans main(int argc, char *argv[])
5465952343eSKyle Evans {
547490e13c1SKyle Evans 	struct command_map_entry *cmd;
548b29bf2f8SKyle Evans 	const char *command;
549cc624025SKyle Evans 	char *root;
550490e13c1SKyle Evans 	int rc;
5515952343eSKyle Evans 
552490e13c1SKyle Evans 	cmd = NULL;
553cc624025SKyle Evans 	root = NULL;
5548369ba42SKyle Evans 	if (argc < 2)
5555952343eSKyle Evans 		return (usage(false));
5565952343eSKyle Evans 
557cc624025SKyle Evans 	if (strcmp(argv[1], "-r") == 0) {
558cc624025SKyle Evans 		if (argc < 4)
559cc624025SKyle Evans 			return (usage(false));
560cc624025SKyle Evans 		root = strdup(argv[2]);
561cc624025SKyle Evans 		command = argv[3];
562cc624025SKyle Evans 		argc -= 3;
563cc624025SKyle Evans 		argv += 3;
564cc624025SKyle Evans 	} else {
5655952343eSKyle Evans 		command = argv[1];
566cc624025SKyle Evans 		argc -= 1;
567cc624025SKyle Evans 		argv += 1;
568cc624025SKyle Evans 	}
5695952343eSKyle Evans 
5705952343eSKyle Evans 	/* Handle command aliases */
57116a10da8SKyle Evans 	if (strcmp(command, "umount") == 0)
5725952343eSKyle Evans 		command = "unmount";
5735952343eSKyle Evans 
57416a10da8SKyle Evans 	if (strcmp(command, "ujail") == 0)
5755952343eSKyle Evans 		command = "unjail";
5765952343eSKyle Evans 
57716a10da8SKyle Evans 	if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
5785952343eSKyle Evans 		return (usage(true));
5795952343eSKyle Evans 
580490e13c1SKyle Evans 	if ((cmd = get_cmd_info(command)) == NULL) {
58123614c2bSBenedict Reuschling 		fprintf(stderr, "Unknown command: %s\n", command);
5825952343eSKyle Evans 		return (usage(false));
5835952343eSKyle Evans 	}
5845952343eSKyle Evans 
5854c91d6bcSGleb Smirnoff 	if ((be = libbe_init(root)) == NULL) {
5862a58b312SMartin Matuska 		if (!cmd->silent)
5874c91d6bcSGleb Smirnoff 			fprintf(stderr, "libbe_init(\"%s\") failed.\n",
5884c91d6bcSGleb Smirnoff 			    root != NULL ? root : "");
5895952343eSKyle Evans 		return (-1);
5904c91d6bcSGleb Smirnoff 	}
5915952343eSKyle Evans 
592490e13c1SKyle Evans 	libbe_print_on_error(be, !cmd->silent);
5935952343eSKyle Evans 
594490e13c1SKyle Evans 	rc = cmd->fn(argc, argv);
5955952343eSKyle Evans 
5965952343eSKyle Evans 	libbe_close(be);
5975952343eSKyle Evans 	return (rc);
5985952343eSKyle Evans }
599