xref: /freebsd/sbin/bectl/bectl.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
1b179da01SKyle Evans /*-
2*4d846d26SWarner 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>
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 
63d694059fSKyle Evans int
645952343eSKyle Evans usage(bool explicit)
655952343eSKyle Evans {
6616a10da8SKyle Evans 	FILE *fp;
675952343eSKyle Evans 
6816a10da8SKyle Evans 	fp =  explicit ? stdout : stderr;
6952ee41b7SYuri Pankov 	fprintf(fp, "%s",
7023614c2bSBenedict Reuschling 	    "Usage:\tbectl {-h | -? | subcommand [args...]}\n"
713d1a1f2cSKyle Evans #if SOON
724163bae0SKyle Evans 	    "\tbectl [-r beroot] add (path)*\n"
733d1a1f2cSKyle Evans #endif
744163bae0SKyle Evans 	    "\tbectl [-r beroot] activate [-t] beName\n"
754163bae0SKyle Evans 	    "\tbectl [-r beroot] activate [-T]\n"
764163bae0SKyle Evans 	    "\tbectl [-r beroot] check\n"
774163bae0SKyle Evans 	    "\tbectl [-r beroot] create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n"
784163bae0SKyle Evans 	    "\tbectl [-r beroot] create [-r] beName@snapshot\n"
794163bae0SKyle Evans 	    "\tbectl [-r beroot] destroy [-Fo] {beName | beName@snapshot}\n"
804163bae0SKyle Evans 	    "\tbectl [-r beroot] export sourceBe\n"
814163bae0SKyle Evans 	    "\tbectl [-r beroot] import targetBe\n"
824163bae0SKyle Evans 	    "\tbectl [-r beroot] jail [-bU] [{-o key=value | -u key}]... beName\n"
83b1ea63e2SRobert Wing 	    "\t      [utility [argument ...]]\n"
844163bae0SKyle Evans 	    "\tbectl [-r beroot] list [-aDHs] [{-c property | -C property}]\n"
854163bae0SKyle Evans 	    "\tbectl [-r beroot] mount beName [mountpoint]\n"
864163bae0SKyle Evans 	    "\tbectl [-r beroot] rename origBeName newBeName\n"
874163bae0SKyle Evans 	    "\tbectl [-r beroot] {ujail | unjail} {jailID | jailName | beName}\n"
884163bae0SKyle Evans 	    "\tbectl [-r beroot] {umount | unmount} [-f] beName\n");
895952343eSKyle Evans 
905952343eSKyle Evans 	return (explicit ? 0 : EX_USAGE);
915952343eSKyle Evans }
925952343eSKyle Evans 
935952343eSKyle Evans 
945952343eSKyle Evans /*
955952343eSKyle Evans  * Represents a relationship between the command name and the parser action
965952343eSKyle Evans  * that handles it.
975952343eSKyle Evans  */
985952343eSKyle Evans struct command_map_entry {
995952343eSKyle Evans 	const char *command;
1005952343eSKyle Evans 	int (*fn)(int argc, char *argv[]);
101490e13c1SKyle Evans 	/* True if libbe_print_on_error should be disabled */
102490e13c1SKyle Evans 	bool silent;
1035952343eSKyle Evans };
1045952343eSKyle Evans 
1055952343eSKyle Evans static struct command_map_entry command_map[] =
1065952343eSKyle Evans {
107490e13c1SKyle Evans 	{ "activate", bectl_cmd_activate,false   },
108490e13c1SKyle Evans 	{ "create",   bectl_cmd_create,  false   },
109490e13c1SKyle Evans 	{ "destroy",  bectl_cmd_destroy, false   },
110490e13c1SKyle Evans 	{ "export",   bectl_cmd_export,  false   },
111490e13c1SKyle Evans 	{ "import",   bectl_cmd_import,  false   },
1123d1a1f2cSKyle Evans #if SOON
113490e13c1SKyle Evans 	{ "add",      bectl_cmd_add,     false   },
1143d1a1f2cSKyle Evans #endif
115490e13c1SKyle Evans 	{ "jail",     bectl_cmd_jail,    false   },
116490e13c1SKyle Evans 	{ "list",     bectl_cmd_list,    false   },
117490e13c1SKyle Evans 	{ "mount",    bectl_cmd_mount,   false   },
118490e13c1SKyle Evans 	{ "rename",   bectl_cmd_rename,  false   },
119490e13c1SKyle Evans 	{ "unjail",   bectl_cmd_unjail,  false   },
120490e13c1SKyle Evans 	{ "unmount",  bectl_cmd_unmount, false   },
121490e13c1SKyle Evans 	{ "check",    bectl_cmd_check,   true    },
1225952343eSKyle Evans };
1235952343eSKyle Evans 
124490e13c1SKyle Evans static struct command_map_entry *
125490e13c1SKyle Evans get_cmd_info(const char *cmd)
1265952343eSKyle Evans {
127490e13c1SKyle Evans 	size_t i;
1285952343eSKyle Evans 
129490e13c1SKyle Evans 	for (i = 0; i < nitems(command_map); ++i) {
130490e13c1SKyle Evans 		if (strcmp(cmd, command_map[i].command) == 0)
131490e13c1SKyle Evans 			return (&command_map[i]);
1325952343eSKyle Evans 	}
1335952343eSKyle Evans 
134490e13c1SKyle Evans 	return (NULL);
1355952343eSKyle Evans }
1365952343eSKyle Evans 
1375952343eSKyle Evans static int
1389e004b21SKyle Evans bectl_cmd_activate(int argc, char *argv[])
1395952343eSKyle Evans {
1405952343eSKyle Evans 	int err, opt;
141e307eb94SToomas Soome 	bool temp, reset;
1425952343eSKyle Evans 
1435952343eSKyle Evans 	temp = false;
144e307eb94SToomas Soome 	reset = false;
145e307eb94SToomas Soome 	while ((opt = getopt(argc, argv, "tT")) != -1) {
1465952343eSKyle Evans 		switch (opt) {
1475952343eSKyle Evans 		case 't':
148e307eb94SToomas Soome 			if (reset)
149e307eb94SToomas Soome 				return (usage(false));
1505952343eSKyle Evans 			temp = true;
1515952343eSKyle Evans 			break;
152e307eb94SToomas Soome 		case 'T':
153e307eb94SToomas Soome 			if (temp)
154e307eb94SToomas Soome 				return (usage(false));
155e307eb94SToomas Soome 			reset = true;
156e307eb94SToomas Soome 			break;
1575952343eSKyle Evans 		default:
1582c848957SKyle Evans 			fprintf(stderr, "bectl activate: unknown option '-%c'\n",
1595952343eSKyle Evans 			    optopt);
1605952343eSKyle Evans 			return (usage(false));
1615952343eSKyle Evans 		}
1625952343eSKyle Evans 	}
1635952343eSKyle Evans 
1645952343eSKyle Evans 	argc -= optind;
1655952343eSKyle Evans 	argv += optind;
1665952343eSKyle Evans 
167e307eb94SToomas Soome 	if (argc != 1 && (!reset || argc != 0)) {
1682c848957SKyle Evans 		fprintf(stderr, "bectl activate: wrong number of arguments\n");
1695952343eSKyle Evans 		return (usage(false));
1705952343eSKyle Evans 	}
1715952343eSKyle Evans 
172e307eb94SToomas Soome 	if (reset) {
173e307eb94SToomas Soome 		if ((err = be_deactivate(be, NULL, reset)) == 0)
174e307eb94SToomas Soome 			printf("Temporary activation removed\n");
175e307eb94SToomas Soome 		else
176e307eb94SToomas Soome 			printf("Failed to remove temporary activation\n");
177e307eb94SToomas Soome 		return (err);
178e307eb94SToomas Soome 	}
1795952343eSKyle Evans 
1805952343eSKyle Evans 	/* activate logic goes here */
18116a10da8SKyle Evans 	if ((err = be_activate(be, argv[0], temp)) != 0)
18216a10da8SKyle Evans 		/* XXX TODO: more specific error msg based on err */
18323614c2bSBenedict Reuschling 		printf("Did not successfully activate boot environment %s\n",
1845952343eSKyle Evans 		    argv[0]);
18516a10da8SKyle Evans 	else
18623614c2bSBenedict Reuschling 		printf("Successfully activated boot environment %s\n", argv[0]);
1875952343eSKyle Evans 
18816a10da8SKyle Evans 	if (temp)
1895952343eSKyle Evans 		printf("for next boot\n");
1905952343eSKyle Evans 
1915952343eSKyle Evans 	return (err);
1925952343eSKyle Evans }
1935952343eSKyle Evans 
1945952343eSKyle Evans 
19516a10da8SKyle Evans /*
19616a10da8SKyle Evans  * TODO: when only one arg is given, and it contains an "@" the this should
19716a10da8SKyle Evans  * create that snapshot
19816a10da8SKyle Evans  */
1995952343eSKyle Evans static int
2009e004b21SKyle Evans bectl_cmd_create(int argc, char *argv[])
2015952343eSKyle Evans {
202d05fa0d9SKyle Evans 	char snapshot[BE_MAXPATHLEN];
203d05fa0d9SKyle Evans 	char *atpos, *bootenv, *snapname;
2045952343eSKyle Evans 	int err, opt;
205a9c660b0SKyle Evans 	bool recursive;
2065952343eSKyle Evans 
2075952343eSKyle Evans 	snapname = NULL;
208a9c660b0SKyle Evans 	recursive = false;
20925eeb3eaSKyle Evans 	while ((opt = getopt(argc, argv, "e:r")) != -1) {
2105952343eSKyle Evans 		switch (opt) {
2115952343eSKyle Evans 		case 'e':
2125952343eSKyle Evans 			snapname = optarg;
2135952343eSKyle Evans 			break;
214a9c660b0SKyle Evans 		case 'r':
215a9c660b0SKyle Evans 			recursive = true;
21625eeb3eaSKyle Evans 			break;
2175952343eSKyle Evans 		default:
2182c848957SKyle Evans 			fprintf(stderr, "bectl create: unknown option '-%c'\n",
2195952343eSKyle Evans 			    optopt);
2205952343eSKyle Evans 			return (usage(false));
2215952343eSKyle Evans 		}
2225952343eSKyle Evans 	}
2235952343eSKyle Evans 
2245952343eSKyle Evans 	argc -= optind;
2255952343eSKyle Evans 	argv += optind;
2265952343eSKyle Evans 
2275952343eSKyle Evans 	if (argc != 1) {
2282c848957SKyle Evans 		fprintf(stderr, "bectl create: wrong number of arguments\n");
2295952343eSKyle Evans 		return (usage(false));
2305952343eSKyle Evans 	}
2315952343eSKyle Evans 
2325952343eSKyle Evans 	bootenv = *argv;
233d05fa0d9SKyle Evans 
234d05fa0d9SKyle Evans 	err = BE_ERR_SUCCESS;
235dadb9c70SKyle Evans 	if ((atpos = strchr(bootenv, '@')) != NULL) {
236a9c660b0SKyle Evans 		/*
237a9c660b0SKyle Evans 		 * This is the "create a snapshot variant". No new boot
238a9c660b0SKyle Evans 		 * environment is to be created here.
239a9c660b0SKyle Evans 		 */
240a9c660b0SKyle Evans 		*atpos++ = '\0';
241a9c660b0SKyle Evans 		err = be_snapshot(be, bootenv, atpos, recursive, NULL);
2425952343eSKyle Evans 	} else {
243d05fa0d9SKyle Evans 		if (snapname == NULL)
244d05fa0d9SKyle Evans 			/* Create from currently booted BE */
245d05fa0d9SKyle Evans 			err = be_snapshot(be, be_active_path(be), NULL,
246d05fa0d9SKyle Evans 			    recursive, snapshot);
247d05fa0d9SKyle Evans 		else if (strchr(snapname, '@') != NULL)
248d05fa0d9SKyle Evans 			/* Create from given snapshot */
249d05fa0d9SKyle Evans 			strlcpy(snapshot, snapname, sizeof(snapshot));
250d05fa0d9SKyle Evans 		else
251d05fa0d9SKyle Evans 			/* Create from given BE */
252d05fa0d9SKyle Evans 			err = be_snapshot(be, snapname, NULL, recursive,
253d05fa0d9SKyle Evans 			    snapshot);
254d05fa0d9SKyle Evans 
255d05fa0d9SKyle Evans 		if (err == BE_ERR_SUCCESS)
256d05fa0d9SKyle Evans 			err = be_create_depth(be, bootenv, snapshot,
257d05fa0d9SKyle Evans 					      recursive == true ? -1 : 0);
2585952343eSKyle Evans 	}
2595952343eSKyle Evans 
2605952343eSKyle Evans 	switch (err) {
2615952343eSKyle Evans 	case BE_ERR_SUCCESS:
2625952343eSKyle Evans 		break;
2630e6549c8SRobert Wing 	case BE_ERR_INVALIDNAME:
2640e6549c8SRobert Wing 		fprintf(stderr,
2650e6549c8SRobert Wing 		    "bectl create: boot environment name must not contain spaces\n");
2660e6549c8SRobert Wing 		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;
3652a58b312SMartin Matuska 	char *target, targetds[BE_MAXPATHLEN];
3662a58b312SMartin Matuska 	const char *origin;
36777b4126cSKyle Evans 	int err, flags, opt;
3685952343eSKyle Evans 
36977b4126cSKyle Evans 	flags = 0;
37077b4126cSKyle Evans 	while ((opt = getopt(argc, argv, "Fo")) != -1) {
3715952343eSKyle Evans 		switch (opt) {
3725952343eSKyle Evans 		case 'F':
37377b4126cSKyle Evans 			flags |= BE_DESTROY_FORCE;
37477b4126cSKyle Evans 			break;
37577b4126cSKyle Evans 		case 'o':
37677b4126cSKyle Evans 			flags |= BE_DESTROY_ORIGIN;
3775952343eSKyle Evans 			break;
3785952343eSKyle Evans 		default:
3792c848957SKyle Evans 			fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
3805952343eSKyle Evans 			    optopt);
3815952343eSKyle Evans 			return (usage(false));
3825952343eSKyle Evans 		}
3835952343eSKyle Evans 	}
3845952343eSKyle Evans 
3855952343eSKyle Evans 	argc -= optind;
3865952343eSKyle Evans 	argv += optind;
3875952343eSKyle Evans 
3885952343eSKyle Evans 	if (argc != 1) {
3892c848957SKyle Evans 		fprintf(stderr, "bectl destroy: wrong number of arguments\n");
3905952343eSKyle Evans 		return (usage(false));
3915952343eSKyle Evans 	}
3925952343eSKyle Evans 
3935952343eSKyle Evans 	target = argv[0];
3945952343eSKyle Evans 
39577b4126cSKyle Evans 	/* We'll emit a notice if there's an origin to be cleaned up */
39677b4126cSKyle Evans 	if ((flags & BE_DESTROY_ORIGIN) == 0 && strchr(target, '@') == NULL) {
3978338f584SKyle Evans 		flags |= BE_DESTROY_AUTOORIGIN;
39877b4126cSKyle Evans 		if (be_root_concat(be, target, targetds) != 0)
39977b4126cSKyle Evans 			goto destroy;
40077b4126cSKyle Evans 		if (be_prop_list_alloc(&props) != 0)
40177b4126cSKyle Evans 			goto destroy;
40277b4126cSKyle Evans 		if (be_get_dataset_props(be, targetds, props) != 0) {
40377b4126cSKyle Evans 			be_prop_list_free(props);
40477b4126cSKyle Evans 			goto destroy;
40577b4126cSKyle Evans 		}
4068338f584SKyle Evans 		if (nvlist_lookup_string(props, "origin", &origin) == 0 &&
4078338f584SKyle Evans 		    !be_is_auto_snapshot_name(be, origin))
40877b4126cSKyle Evans 			fprintf(stderr, "bectl destroy: leaving origin '%s' intact\n",
40977b4126cSKyle Evans 			    origin);
41077b4126cSKyle Evans 		be_prop_list_free(props);
41177b4126cSKyle Evans 	}
41277b4126cSKyle Evans 
41377b4126cSKyle Evans destroy:
41477b4126cSKyle Evans 	err = be_destroy(be, target, flags);
4155952343eSKyle Evans 
4165952343eSKyle Evans 	return (err);
4175952343eSKyle Evans }
4185952343eSKyle Evans 
4195952343eSKyle Evans static int
4209e004b21SKyle Evans bectl_cmd_mount(int argc, char *argv[])
4215952343eSKyle Evans {
4225952343eSKyle Evans 	char result_loc[BE_MAXPATHLEN];
42316a10da8SKyle Evans 	char *bootenv, *mountpoint;
4240a603a6eSKyle Evans 	int err, mntflags;
4255952343eSKyle Evans 
4260a603a6eSKyle Evans 	/* XXX TODO: Allow shallow */
4270a603a6eSKyle Evans 	mntflags = BE_MNT_DEEP;
4285952343eSKyle Evans 	if (argc < 2) {
4292c848957SKyle Evans 		fprintf(stderr, "bectl mount: missing argument(s)\n");
4305952343eSKyle Evans 		return (usage(false));
4315952343eSKyle Evans 	}
4325952343eSKyle Evans 
4335952343eSKyle Evans 	if (argc > 3) {
4342c848957SKyle Evans 		fprintf(stderr, "bectl mount: too many arguments\n");
4355952343eSKyle Evans 		return (usage(false));
4365952343eSKyle Evans 	}
4375952343eSKyle Evans 
4385952343eSKyle Evans 	bootenv = argv[1];
4395952343eSKyle Evans 	mountpoint = ((argc == 3) ? argv[2] : NULL);
4405952343eSKyle Evans 
4410a603a6eSKyle Evans 	err = be_mount(be, bootenv, mountpoint, mntflags, result_loc);
4425952343eSKyle Evans 
4435952343eSKyle Evans 	switch (err) {
4445952343eSKyle Evans 	case BE_ERR_SUCCESS:
44523614c2bSBenedict Reuschling 		printf("Successfully mounted %s at %s\n", bootenv, result_loc);
4465952343eSKyle Evans 		break;
4475952343eSKyle Evans 	default:
4485952343eSKyle Evans 		fprintf(stderr,
44923614c2bSBenedict Reuschling 		    (argc == 3) ? "Failed to mount bootenv %s at %s\n" :
45023614c2bSBenedict Reuschling 		    "Failed to mount bootenv %s at temporary path %s\n",
4515952343eSKyle Evans 		    bootenv, mountpoint);
4525952343eSKyle Evans 	}
4535952343eSKyle Evans 
4545952343eSKyle Evans 	return (err);
4555952343eSKyle Evans }
4565952343eSKyle Evans 
4575952343eSKyle Evans 
4585952343eSKyle Evans static int
4599e004b21SKyle Evans bectl_cmd_rename(int argc, char *argv[])
4605952343eSKyle Evans {
46116a10da8SKyle Evans 	char *dest, *src;
4625952343eSKyle Evans 	int err;
4635952343eSKyle Evans 
4645952343eSKyle Evans 	if (argc < 3) {
4652c848957SKyle Evans 		fprintf(stderr, "bectl rename: missing argument\n");
4665952343eSKyle Evans 		return (usage(false));
4675952343eSKyle Evans 	}
4685952343eSKyle Evans 
4695952343eSKyle Evans 	if (argc > 3) {
4702c848957SKyle Evans 		fprintf(stderr, "bectl rename: too many arguments\n");
4715952343eSKyle Evans 		return (usage(false));
4725952343eSKyle Evans 	}
4735952343eSKyle Evans 
4745952343eSKyle Evans 	src = argv[1];
4755952343eSKyle Evans 	dest = argv[2];
4765952343eSKyle Evans 
4775952343eSKyle Evans 	err = be_rename(be, src, dest);
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 
486dadb9c70SKyle Evans 	return (err);
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 
5874c91d6bcSGleb Smirnoff 	if ((be = libbe_init(root)) == NULL) {
5882a58b312SMartin Matuska 		if (!cmd->silent)
5894c91d6bcSGleb Smirnoff 			fprintf(stderr, "libbe_init(\"%s\") failed.\n",
5904c91d6bcSGleb Smirnoff 			    root != NULL ? root : "");
5915952343eSKyle Evans 		return (-1);
5924c91d6bcSGleb Smirnoff 	}
5935952343eSKyle Evans 
594490e13c1SKyle Evans 	libbe_print_on_error(be, !cmd->silent);
5955952343eSKyle Evans 
596490e13c1SKyle Evans 	rc = cmd->fn(argc, argv);
5975952343eSKyle Evans 
5985952343eSKyle Evans 	libbe_close(be);
5995952343eSKyle Evans 	return (rc);
6005952343eSKyle Evans }
601