xref: /freebsd/sbin/bectl/bectl.c (revision 490e13c1403f863296eed0915c270c8781deaf38)
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  * All rights reserved.
65952343eSKyle Evans  *
75952343eSKyle Evans  * Redistribution and use in source and binary forms, with or without
85952343eSKyle Evans  * modification, are permitted provided that the following conditions
95952343eSKyle Evans  * are met:
105952343eSKyle Evans  * 1. Redistributions of source code must retain the above copyright
115952343eSKyle Evans  *    notice, this list of conditions and the following disclaimer.
125952343eSKyle Evans  * 2. Redistributions in binary form must reproduce the above copyright
135952343eSKyle Evans  *    notice, this list of conditions and the following disclaimer in the
145952343eSKyle Evans  *    documentation and/or other materials provided with the distribution.
155952343eSKyle Evans  *
165952343eSKyle Evans  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
175952343eSKyle Evans  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
185952343eSKyle Evans  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
195952343eSKyle Evans  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
205952343eSKyle Evans  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
215952343eSKyle Evans  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
225952343eSKyle Evans  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
235952343eSKyle Evans  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
245952343eSKyle Evans  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
255952343eSKyle Evans  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
265952343eSKyle Evans  * SUCH DAMAGE.
275952343eSKyle Evans  */
285952343eSKyle Evans 
29b6e7c421SKyle Evans #include <sys/cdefs.h>
30b6e7c421SKyle Evans __FBSDID("$FreeBSD$");
31b6e7c421SKyle Evans 
325952343eSKyle Evans #include <sys/param.h>
335952343eSKyle Evans #include <sys/mount.h>
345952343eSKyle Evans #include <errno.h>
3583244ec1SKyle Evans #include <libutil.h>
365952343eSKyle Evans #include <stdbool.h>
375952343eSKyle Evans #include <stdio.h>
385952343eSKyle Evans #include <stdint.h>
395952343eSKyle Evans #include <stdlib.h>
405952343eSKyle Evans #include <string.h>
415952343eSKyle Evans #include <sysexits.h>
4283244ec1SKyle Evans #include <time.h>
435952343eSKyle Evans #include <unistd.h>
445952343eSKyle Evans 
455952343eSKyle Evans #include <be.h>
465952343eSKyle Evans 
47d694059fSKyle Evans #include "bectl.h"
48d694059fSKyle Evans 
499e004b21SKyle Evans static int bectl_cmd_activate(int argc, char *argv[]);
50*490e13c1SKyle Evans static int bectl_cmd_check(int argc, char *argv[]);
519e004b21SKyle Evans static int bectl_cmd_create(int argc, char *argv[]);
529e004b21SKyle Evans static int bectl_cmd_destroy(int argc, char *argv[]);
539e004b21SKyle Evans static int bectl_cmd_export(int argc, char *argv[]);
549e004b21SKyle Evans static int bectl_cmd_import(int argc, char *argv[]);
553d1a1f2cSKyle Evans #if SOON
569e004b21SKyle Evans static int bectl_cmd_add(int argc, char *argv[]);
573d1a1f2cSKyle Evans #endif
589e004b21SKyle Evans static int bectl_cmd_mount(int argc, char *argv[]);
599e004b21SKyle Evans static int bectl_cmd_rename(int argc, char *argv[]);
609e004b21SKyle Evans static int bectl_cmd_unmount(int argc, char *argv[]);
615952343eSKyle Evans 
62d694059fSKyle Evans libbe_handle_t *be;
635952343eSKyle Evans 
64d694059fSKyle Evans int
655952343eSKyle Evans usage(bool explicit)
665952343eSKyle Evans {
6716a10da8SKyle Evans 	FILE *fp;
685952343eSKyle Evans 
6916a10da8SKyle Evans 	fp =  explicit ? stdout : stderr;
7052ee41b7SYuri Pankov 	fprintf(fp, "%s",
71d81df689SKyle Evans 	    "usage:\tbectl {-h | -? | subcommand [args...]}\n"
723d1a1f2cSKyle Evans #if SOON
739e004b21SKyle Evans 	    "\tbectl add (path)*\n"
743d1a1f2cSKyle Evans #endif
7552ee41b7SYuri Pankov 	    "\tbectl activate [-t] beName\n"
76*490e13c1SKyle Evans 	    "\tbectl check\n"
7752ee41b7SYuri Pankov 	    "\tbectl create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n"
7852ee41b7SYuri Pankov 	    "\tbectl create [-r] beName@snapshot\n"
7952ee41b7SYuri Pankov 	    "\tbectl destroy [-F] {beName | beName@snapshot}\n"
8052ee41b7SYuri Pankov 	    "\tbectl export sourceBe\n"
8152ee41b7SYuri Pankov 	    "\tbectl import targetBe\n"
8252ee41b7SYuri Pankov 	    "\tbectl jail {-b | -U} [{-o key=value | -u key}]... "
8352ee41b7SYuri Pankov 	    "{jailID | jailName}\n"
8452ee41b7SYuri Pankov 	    "\t      bootenv [utility [argument ...]]\n"
85f0298be0SKyle Evans 	    "\tbectl list [-DHas] [{-c property | -C property}]\n"
869e004b21SKyle Evans 	    "\tbectl mount beName [mountpoint]\n"
879e004b21SKyle Evans 	    "\tbectl rename origBeName newBeName\n"
8852ee41b7SYuri Pankov 	    "\tbectl {ujail | unjail} {jailID | jailName} bootenv\n"
899e004b21SKyle Evans 	    "\tbectl {umount | unmount} [-f] beName\n");
905952343eSKyle Evans 
915952343eSKyle Evans 	return (explicit ? 0 : EX_USAGE);
925952343eSKyle Evans }
935952343eSKyle Evans 
945952343eSKyle Evans 
955952343eSKyle Evans /*
965952343eSKyle Evans  * Represents a relationship between the command name and the parser action
975952343eSKyle Evans  * that handles it.
985952343eSKyle Evans  */
995952343eSKyle Evans struct command_map_entry {
1005952343eSKyle Evans 	const char *command;
1015952343eSKyle Evans 	int (*fn)(int argc, char *argv[]);
102*490e13c1SKyle Evans 	/* True if libbe_print_on_error should be disabled */
103*490e13c1SKyle Evans 	bool silent;
1045952343eSKyle Evans };
1055952343eSKyle Evans 
1065952343eSKyle Evans static struct command_map_entry command_map[] =
1075952343eSKyle Evans {
108*490e13c1SKyle Evans 	{ "activate", bectl_cmd_activate,false   },
109*490e13c1SKyle Evans 	{ "create",   bectl_cmd_create,  false   },
110*490e13c1SKyle Evans 	{ "destroy",  bectl_cmd_destroy, false   },
111*490e13c1SKyle Evans 	{ "export",   bectl_cmd_export,  false   },
112*490e13c1SKyle Evans 	{ "import",   bectl_cmd_import,  false   },
1133d1a1f2cSKyle Evans #if SOON
114*490e13c1SKyle Evans 	{ "add",      bectl_cmd_add,     false   },
1153d1a1f2cSKyle Evans #endif
116*490e13c1SKyle Evans 	{ "jail",     bectl_cmd_jail,    false   },
117*490e13c1SKyle Evans 	{ "list",     bectl_cmd_list,    false   },
118*490e13c1SKyle Evans 	{ "mount",    bectl_cmd_mount,   false   },
119*490e13c1SKyle Evans 	{ "rename",   bectl_cmd_rename,  false   },
120*490e13c1SKyle Evans 	{ "unjail",   bectl_cmd_unjail,  false   },
121*490e13c1SKyle Evans 	{ "unmount",  bectl_cmd_unmount, false   },
122*490e13c1SKyle Evans 	{ "check",    bectl_cmd_check,   true    },
1235952343eSKyle Evans };
1245952343eSKyle Evans 
125*490e13c1SKyle Evans static struct command_map_entry *
126*490e13c1SKyle Evans get_cmd_info(const char *cmd)
1275952343eSKyle Evans {
128*490e13c1SKyle Evans 	size_t i;
1295952343eSKyle Evans 
130*490e13c1SKyle Evans 	for (i = 0; i < nitems(command_map); ++i) {
131*490e13c1SKyle Evans 		if (strcmp(cmd, command_map[i].command) == 0)
132*490e13c1SKyle Evans 			return (&command_map[i]);
1335952343eSKyle Evans 	}
1345952343eSKyle Evans 
135*490e13c1SKyle Evans 	return (NULL);
1365952343eSKyle Evans }
1375952343eSKyle Evans 
1385952343eSKyle Evans 
1395952343eSKyle Evans static int
1409e004b21SKyle Evans bectl_cmd_activate(int argc, char *argv[])
1415952343eSKyle Evans {
1425952343eSKyle Evans 	int err, opt;
1435952343eSKyle Evans 	bool temp;
1445952343eSKyle Evans 
1455952343eSKyle Evans 	temp = false;
1465952343eSKyle Evans 	while ((opt = getopt(argc, argv, "t")) != -1) {
1475952343eSKyle Evans 		switch (opt) {
1485952343eSKyle Evans 		case 't':
1495952343eSKyle Evans 			temp = true;
1505952343eSKyle Evans 			break;
1515952343eSKyle Evans 		default:
1522c848957SKyle Evans 			fprintf(stderr, "bectl activate: unknown option '-%c'\n",
1535952343eSKyle Evans 			    optopt);
1545952343eSKyle Evans 			return (usage(false));
1555952343eSKyle Evans 		}
1565952343eSKyle Evans 	}
1575952343eSKyle Evans 
1585952343eSKyle Evans 	argc -= optind;
1595952343eSKyle Evans 	argv += optind;
1605952343eSKyle Evans 
1615952343eSKyle Evans 	if (argc != 1) {
1622c848957SKyle Evans 		fprintf(stderr, "bectl activate: wrong number of arguments\n");
1635952343eSKyle Evans 		return (usage(false));
1645952343eSKyle Evans 	}
1655952343eSKyle Evans 
1665952343eSKyle Evans 
1675952343eSKyle Evans 	/* activate logic goes here */
16816a10da8SKyle Evans 	if ((err = be_activate(be, argv[0], temp)) != 0)
16916a10da8SKyle Evans 		/* XXX TODO: more specific error msg based on err */
1705952343eSKyle Evans 		printf("did not successfully activate boot environment %s\n",
1715952343eSKyle Evans 		    argv[0]);
17216a10da8SKyle Evans 	else
1735952343eSKyle Evans 		printf("successfully activated boot environment %s\n", argv[0]);
1745952343eSKyle Evans 
17516a10da8SKyle Evans 	if (temp)
1765952343eSKyle Evans 		printf("for next boot\n");
1775952343eSKyle Evans 
1785952343eSKyle Evans 	return (err);
1795952343eSKyle Evans }
1805952343eSKyle Evans 
1815952343eSKyle Evans 
18216a10da8SKyle Evans /*
18316a10da8SKyle Evans  * TODO: when only one arg is given, and it contains an "@" the this should
18416a10da8SKyle Evans  * create that snapshot
18516a10da8SKyle Evans  */
1865952343eSKyle Evans static int
1879e004b21SKyle Evans bectl_cmd_create(int argc, char *argv[])
1885952343eSKyle Evans {
189d05fa0d9SKyle Evans 	char snapshot[BE_MAXPATHLEN];
190d05fa0d9SKyle Evans 	char *atpos, *bootenv, *snapname;
1915952343eSKyle Evans 	int err, opt;
192a9c660b0SKyle Evans 	bool recursive;
1935952343eSKyle Evans 
1945952343eSKyle Evans 	snapname = NULL;
195a9c660b0SKyle Evans 	recursive = false;
19625eeb3eaSKyle Evans 	while ((opt = getopt(argc, argv, "e:r")) != -1) {
1975952343eSKyle Evans 		switch (opt) {
1985952343eSKyle Evans 		case 'e':
1995952343eSKyle Evans 			snapname = optarg;
2005952343eSKyle Evans 			break;
201a9c660b0SKyle Evans 		case 'r':
202a9c660b0SKyle Evans 			recursive = true;
20325eeb3eaSKyle Evans 			break;
2045952343eSKyle Evans 		default:
2052c848957SKyle Evans 			fprintf(stderr, "bectl create: unknown option '-%c'\n",
2065952343eSKyle Evans 			    optopt);
2075952343eSKyle Evans 			return (usage(false));
2085952343eSKyle Evans 		}
2095952343eSKyle Evans 	}
2105952343eSKyle Evans 
2115952343eSKyle Evans 	argc -= optind;
2125952343eSKyle Evans 	argv += optind;
2135952343eSKyle Evans 
2145952343eSKyle Evans 	if (argc != 1) {
2152c848957SKyle Evans 		fprintf(stderr, "bectl create: wrong number of arguments\n");
2165952343eSKyle Evans 		return (usage(false));
2175952343eSKyle Evans 	}
2185952343eSKyle Evans 
2195952343eSKyle Evans 	bootenv = *argv;
220d05fa0d9SKyle Evans 
221d05fa0d9SKyle Evans 	err = BE_ERR_SUCCESS;
222a9c660b0SKyle Evans 	if ((atpos = strchr(bootenv, '@')) != NULL) {
223a9c660b0SKyle Evans 		/*
224a9c660b0SKyle Evans 		 * This is the "create a snapshot variant". No new boot
225a9c660b0SKyle Evans 		 * environment is to be created here.
226a9c660b0SKyle Evans 		 */
227a9c660b0SKyle Evans 		*atpos++ = '\0';
228a9c660b0SKyle Evans 		err = be_snapshot(be, bootenv, atpos, recursive, NULL);
2295952343eSKyle Evans 	} else {
230d05fa0d9SKyle Evans 		if (snapname == NULL)
231d05fa0d9SKyle Evans 			/* Create from currently booted BE */
232d05fa0d9SKyle Evans 			err = be_snapshot(be, be_active_path(be), NULL,
233d05fa0d9SKyle Evans 			    recursive, snapshot);
234d05fa0d9SKyle Evans 		else if (strchr(snapname, '@') != NULL)
235d05fa0d9SKyle Evans 			/* Create from given snapshot */
236d05fa0d9SKyle Evans 			strlcpy(snapshot, snapname, sizeof(snapshot));
237d05fa0d9SKyle Evans 		else
238d05fa0d9SKyle Evans 			/* Create from given BE */
239d05fa0d9SKyle Evans 			err = be_snapshot(be, snapname, NULL, recursive,
240d05fa0d9SKyle Evans 			    snapshot);
241d05fa0d9SKyle Evans 
242d05fa0d9SKyle Evans 		if (err == BE_ERR_SUCCESS)
243d05fa0d9SKyle Evans 			err = be_create_depth(be, bootenv, snapshot,
244d05fa0d9SKyle Evans 					      recursive == true ? -1 : 0);
2455952343eSKyle Evans 	}
2465952343eSKyle Evans 
2475952343eSKyle Evans 	switch (err) {
2485952343eSKyle Evans 	case BE_ERR_SUCCESS:
2495952343eSKyle Evans 		break;
2505952343eSKyle Evans 	default:
251a9c660b0SKyle Evans 		if (atpos != NULL)
252a9c660b0SKyle Evans 			fprintf(stderr,
253a9c660b0SKyle Evans 			    "failed to create a snapshot '%s' of '%s'\n",
254a9c660b0SKyle Evans 			    atpos, bootenv);
255a9c660b0SKyle Evans 		else if (snapname == NULL)
2565952343eSKyle Evans 			fprintf(stderr,
2575952343eSKyle Evans 			    "failed to create bootenv %s\n", bootenv);
25816a10da8SKyle Evans 		else
2595952343eSKyle Evans 			fprintf(stderr,
2605952343eSKyle Evans 			    "failed to create bootenv %s from snapshot %s\n",
2615952343eSKyle Evans 			    bootenv, snapname);
2625952343eSKyle Evans 	}
2635952343eSKyle Evans 
2645952343eSKyle Evans 	return (err);
2655952343eSKyle Evans }
2665952343eSKyle Evans 
2675952343eSKyle Evans 
2685952343eSKyle Evans static int
2699e004b21SKyle Evans bectl_cmd_export(int argc, char *argv[])
2705952343eSKyle Evans {
2715952343eSKyle Evans 	char *bootenv;
2725952343eSKyle Evans 
2735952343eSKyle Evans 	if (argc == 1) {
2742c848957SKyle Evans 		fprintf(stderr, "bectl export: missing boot environment name\n");
2755952343eSKyle Evans 		return (usage(false));
2765952343eSKyle Evans 	}
2775952343eSKyle Evans 
2785952343eSKyle Evans 	if (argc > 2) {
2792c848957SKyle Evans 		fprintf(stderr, "bectl export: extra arguments provided\n");
2805952343eSKyle Evans 		return (usage(false));
2815952343eSKyle Evans 	}
2825952343eSKyle Evans 
2835952343eSKyle Evans 	bootenv = argv[1];
2845952343eSKyle Evans 
2855952343eSKyle Evans 	if (isatty(STDOUT_FILENO)) {
2862c848957SKyle Evans 		fprintf(stderr, "bectl export: must redirect output\n");
2875952343eSKyle Evans 		return (EX_USAGE);
2885952343eSKyle Evans 	}
2895952343eSKyle Evans 
2905952343eSKyle Evans 	be_export(be, bootenv, STDOUT_FILENO);
2915952343eSKyle Evans 
2925952343eSKyle Evans 	return (0);
2935952343eSKyle Evans }
2945952343eSKyle Evans 
2955952343eSKyle Evans 
2965952343eSKyle Evans static int
2979e004b21SKyle Evans bectl_cmd_import(int argc, char *argv[])
2985952343eSKyle Evans {
2995952343eSKyle Evans 	char *bootenv;
3005952343eSKyle Evans 	int err;
3015952343eSKyle Evans 
3025952343eSKyle Evans 	if (argc == 1) {
3032c848957SKyle Evans 		fprintf(stderr, "bectl import: missing boot environment name\n");
3045952343eSKyle Evans 		return (usage(false));
3055952343eSKyle Evans 	}
3065952343eSKyle Evans 
3075952343eSKyle Evans 	if (argc > 2) {
3082c848957SKyle Evans 		fprintf(stderr, "bectl import: extra arguments provided\n");
3095952343eSKyle Evans 		return (usage(false));
3105952343eSKyle Evans 	}
3115952343eSKyle Evans 
3125952343eSKyle Evans 	bootenv = argv[1];
3135952343eSKyle Evans 
3145952343eSKyle Evans 	if (isatty(STDIN_FILENO)) {
3152c848957SKyle Evans 		fprintf(stderr, "bectl import: input can not be from terminal\n");
3165952343eSKyle Evans 		return (EX_USAGE);
3175952343eSKyle Evans 	}
3185952343eSKyle Evans 
3195952343eSKyle Evans 	err = be_import(be, bootenv, STDIN_FILENO);
3205952343eSKyle Evans 
3215952343eSKyle Evans 	return (err);
3225952343eSKyle Evans }
3235952343eSKyle Evans 
3243d1a1f2cSKyle Evans #if SOON
3255952343eSKyle Evans static int
3269e004b21SKyle Evans bectl_cmd_add(int argc, char *argv[])
3275952343eSKyle Evans {
3285952343eSKyle Evans 
3295952343eSKyle Evans 	if (argc < 2) {
3302c848957SKyle Evans 		fprintf(stderr, "bectl add: must provide at least one path\n");
3315952343eSKyle Evans 		return (usage(false));
3325952343eSKyle Evans 	}
3335952343eSKyle Evans 
3345952343eSKyle Evans 	for (int i = 1; i < argc; ++i) {
3355952343eSKyle Evans 		printf("arg %d: %s\n", i, argv[i]);
33616a10da8SKyle Evans 		/* XXX TODO catch err */
3375952343eSKyle Evans 		be_add_child(be, argv[i], true);
3385952343eSKyle Evans 	}
3395952343eSKyle Evans 
3405952343eSKyle Evans 	return (0);
3415952343eSKyle Evans }
3423d1a1f2cSKyle Evans #endif
3435952343eSKyle Evans 
3445952343eSKyle Evans static int
3459e004b21SKyle Evans bectl_cmd_destroy(int argc, char *argv[])
3465952343eSKyle Evans {
34777b4126cSKyle Evans 	nvlist_t *props;
34877b4126cSKyle Evans 	char *origin, *target, targetds[BE_MAXPATHLEN];
34977b4126cSKyle Evans 	int err, flags, opt;
3505952343eSKyle Evans 
35177b4126cSKyle Evans 	flags = 0;
35277b4126cSKyle Evans 	while ((opt = getopt(argc, argv, "Fo")) != -1) {
3535952343eSKyle Evans 		switch (opt) {
3545952343eSKyle Evans 		case 'F':
35577b4126cSKyle Evans 			flags |= BE_DESTROY_FORCE;
35677b4126cSKyle Evans 			break;
35777b4126cSKyle Evans 		case 'o':
35877b4126cSKyle Evans 			flags |= BE_DESTROY_ORIGIN;
3595952343eSKyle Evans 			break;
3605952343eSKyle Evans 		default:
3612c848957SKyle Evans 			fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
3625952343eSKyle Evans 			    optopt);
3635952343eSKyle Evans 			return (usage(false));
3645952343eSKyle Evans 		}
3655952343eSKyle Evans 	}
3665952343eSKyle Evans 
3675952343eSKyle Evans 	argc -= optind;
3685952343eSKyle Evans 	argv += optind;
3695952343eSKyle Evans 
3705952343eSKyle Evans 	if (argc != 1) {
3712c848957SKyle Evans 		fprintf(stderr, "bectl destroy: wrong number of arguments\n");
3725952343eSKyle Evans 		return (usage(false));
3735952343eSKyle Evans 	}
3745952343eSKyle Evans 
3755952343eSKyle Evans 	target = argv[0];
3765952343eSKyle Evans 
37777b4126cSKyle Evans 	/* We'll emit a notice if there's an origin to be cleaned up */
37877b4126cSKyle Evans 	if ((flags & BE_DESTROY_ORIGIN) == 0 && strchr(target, '@') == NULL) {
37977b4126cSKyle Evans 		if (be_root_concat(be, target, targetds) != 0)
38077b4126cSKyle Evans 			goto destroy;
38177b4126cSKyle Evans 		if (be_prop_list_alloc(&props) != 0)
38277b4126cSKyle Evans 			goto destroy;
38377b4126cSKyle Evans 		if (be_get_dataset_props(be, targetds, props) != 0) {
38477b4126cSKyle Evans 			be_prop_list_free(props);
38577b4126cSKyle Evans 			goto destroy;
38677b4126cSKyle Evans 		}
38777b4126cSKyle Evans 		if (nvlist_lookup_string(props, "origin", &origin) == 0)
38877b4126cSKyle Evans 			fprintf(stderr, "bectl destroy: leaving origin '%s' intact\n",
38977b4126cSKyle Evans 			    origin);
39077b4126cSKyle Evans 		be_prop_list_free(props);
39177b4126cSKyle Evans 	}
39277b4126cSKyle Evans 
39377b4126cSKyle Evans destroy:
39477b4126cSKyle Evans 	err = be_destroy(be, target, flags);
3955952343eSKyle Evans 
3965952343eSKyle Evans 	return (err);
3975952343eSKyle Evans }
3985952343eSKyle Evans 
3995952343eSKyle Evans static int
4009e004b21SKyle Evans bectl_cmd_mount(int argc, char *argv[])
4015952343eSKyle Evans {
4025952343eSKyle Evans 	char result_loc[BE_MAXPATHLEN];
40316a10da8SKyle Evans 	char *bootenv, *mountpoint;
4040a603a6eSKyle Evans 	int err, mntflags;
4055952343eSKyle Evans 
4060a603a6eSKyle Evans 	/* XXX TODO: Allow shallow */
4070a603a6eSKyle Evans 	mntflags = BE_MNT_DEEP;
4085952343eSKyle Evans 	if (argc < 2) {
4092c848957SKyle Evans 		fprintf(stderr, "bectl mount: missing argument(s)\n");
4105952343eSKyle Evans 		return (usage(false));
4115952343eSKyle Evans 	}
4125952343eSKyle Evans 
4135952343eSKyle Evans 	if (argc > 3) {
4142c848957SKyle Evans 		fprintf(stderr, "bectl mount: too many arguments\n");
4155952343eSKyle Evans 		return (usage(false));
4165952343eSKyle Evans 	}
4175952343eSKyle Evans 
4185952343eSKyle Evans 	bootenv = argv[1];
4195952343eSKyle Evans 	mountpoint = ((argc == 3) ? argv[2] : NULL);
4205952343eSKyle Evans 
4210a603a6eSKyle Evans 	err = be_mount(be, bootenv, mountpoint, mntflags, result_loc);
4225952343eSKyle Evans 
4235952343eSKyle Evans 	switch (err) {
4245952343eSKyle Evans 	case BE_ERR_SUCCESS:
4255952343eSKyle Evans 		printf("successfully mounted %s at %s\n", bootenv, result_loc);
4265952343eSKyle Evans 		break;
4275952343eSKyle Evans 	default:
4285952343eSKyle Evans 		fprintf(stderr,
4295952343eSKyle Evans 		    (argc == 3) ? "failed to mount bootenv %s at %s\n" :
4305952343eSKyle Evans 		    "failed to mount bootenv %s at temporary path %s\n",
4315952343eSKyle Evans 		    bootenv, mountpoint);
4325952343eSKyle Evans 	}
4335952343eSKyle Evans 
4345952343eSKyle Evans 	return (err);
4355952343eSKyle Evans }
4365952343eSKyle Evans 
4375952343eSKyle Evans 
4385952343eSKyle Evans static int
4399e004b21SKyle Evans bectl_cmd_rename(int argc, char *argv[])
4405952343eSKyle Evans {
44116a10da8SKyle Evans 	char *dest, *src;
4425952343eSKyle Evans 	int err;
4435952343eSKyle Evans 
4445952343eSKyle Evans 	if (argc < 3) {
4452c848957SKyle Evans 		fprintf(stderr, "bectl rename: missing argument\n");
4465952343eSKyle Evans 		return (usage(false));
4475952343eSKyle Evans 	}
4485952343eSKyle Evans 
4495952343eSKyle Evans 	if (argc > 3) {
4502c848957SKyle Evans 		fprintf(stderr, "bectl rename: too many arguments\n");
4515952343eSKyle Evans 		return (usage(false));
4525952343eSKyle Evans 	}
4535952343eSKyle Evans 
4545952343eSKyle Evans 	src = argv[1];
4555952343eSKyle Evans 	dest = argv[2];
4565952343eSKyle Evans 
4575952343eSKyle Evans 	err = be_rename(be, src, dest);
4585952343eSKyle Evans 
4595952343eSKyle Evans 	switch (err) {
4605952343eSKyle Evans 	case BE_ERR_SUCCESS:
4615952343eSKyle Evans 		break;
4625952343eSKyle Evans 	default:
4635952343eSKyle Evans 		fprintf(stderr, "failed to rename bootenv %s to %s\n",
4645952343eSKyle Evans 		    src, dest);
4655952343eSKyle Evans 	}
4665952343eSKyle Evans 
4675952343eSKyle Evans 	return (0);
4685952343eSKyle Evans }
4695952343eSKyle Evans 
470ad765da4SKyle Evans static int
4719e004b21SKyle Evans bectl_cmd_unmount(int argc, char *argv[])
4725952343eSKyle Evans {
47316a10da8SKyle Evans 	char *bootenv, *cmd;
4745952343eSKyle Evans 	int err, flags, opt;
4755952343eSKyle Evans 
4765952343eSKyle Evans 	/* Store alias used */
4775952343eSKyle Evans 	cmd = argv[0];
4785952343eSKyle Evans 
4795952343eSKyle Evans 	flags = 0;
4805952343eSKyle Evans 	while ((opt = getopt(argc, argv, "f")) != -1) {
4815952343eSKyle Evans 		switch (opt) {
4825952343eSKyle Evans 		case 'f':
4835952343eSKyle Evans 			flags |= BE_MNT_FORCE;
4845952343eSKyle Evans 			break;
4855952343eSKyle Evans 		default:
4862c848957SKyle Evans 			fprintf(stderr, "bectl %s: unknown option '-%c'\n",
4875952343eSKyle Evans 			    cmd, optopt);
4885952343eSKyle Evans 			return (usage(false));
4895952343eSKyle Evans 		}
4905952343eSKyle Evans 	}
4915952343eSKyle Evans 
4925952343eSKyle Evans 	argc -= optind;
4935952343eSKyle Evans 	argv += optind;
4945952343eSKyle Evans 
4955952343eSKyle Evans 	if (argc != 1) {
4962c848957SKyle Evans 		fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
4975952343eSKyle Evans 		return (usage(false));
4985952343eSKyle Evans 	}
4995952343eSKyle Evans 
5005952343eSKyle Evans 	bootenv = argv[0];
5015952343eSKyle Evans 
5025952343eSKyle Evans 	err = be_unmount(be, bootenv, flags);
5035952343eSKyle Evans 
5045952343eSKyle Evans 	switch (err) {
5055952343eSKyle Evans 	case BE_ERR_SUCCESS:
5065952343eSKyle Evans 		break;
5075952343eSKyle Evans 	default:
5085952343eSKyle Evans 		fprintf(stderr, "failed to unmount bootenv %s\n", bootenv);
5095952343eSKyle Evans 	}
5105952343eSKyle Evans 
5115952343eSKyle Evans 	return (err);
5125952343eSKyle Evans }
5135952343eSKyle Evans 
514*490e13c1SKyle Evans static int
515*490e13c1SKyle Evans bectl_cmd_check(int argc, char *argv[] __unused)
516*490e13c1SKyle Evans {
517*490e13c1SKyle Evans 
518*490e13c1SKyle Evans 	/* The command is left as argv[0] */
519*490e13c1SKyle Evans 	if (argc != 1) {
520*490e13c1SKyle Evans 		fprintf(stderr, "bectl check: wrong number of arguments\n");
521*490e13c1SKyle Evans 		return (usage(false));
522*490e13c1SKyle Evans 	}
523*490e13c1SKyle Evans 
524*490e13c1SKyle Evans 	return (0);
525*490e13c1SKyle Evans }
5265952343eSKyle Evans 
5275952343eSKyle Evans int
5285952343eSKyle Evans main(int argc, char *argv[])
5295952343eSKyle Evans {
530*490e13c1SKyle Evans 	struct command_map_entry *cmd;
531b29bf2f8SKyle Evans 	const char *command;
532cc624025SKyle Evans 	char *root;
533*490e13c1SKyle Evans 	int rc;
5345952343eSKyle Evans 
535*490e13c1SKyle Evans 	cmd = NULL;
536cc624025SKyle Evans 	root = NULL;
5378369ba42SKyle Evans 	if (argc < 2)
5385952343eSKyle Evans 		return (usage(false));
5395952343eSKyle Evans 
540cc624025SKyle Evans 	if (strcmp(argv[1], "-r") == 0) {
541cc624025SKyle Evans 		if (argc < 4)
542cc624025SKyle Evans 			return (usage(false));
543cc624025SKyle Evans 		root = strdup(argv[2]);
544cc624025SKyle Evans 		command = argv[3];
545cc624025SKyle Evans 		argc -= 3;
546cc624025SKyle Evans 		argv += 3;
547cc624025SKyle Evans 	} else {
5485952343eSKyle Evans 		command = argv[1];
549cc624025SKyle Evans 		argc -= 1;
550cc624025SKyle Evans 		argv += 1;
551cc624025SKyle Evans 	}
5525952343eSKyle Evans 
5535952343eSKyle Evans 	/* Handle command aliases */
55416a10da8SKyle Evans 	if (strcmp(command, "umount") == 0)
5555952343eSKyle Evans 		command = "unmount";
5565952343eSKyle Evans 
55716a10da8SKyle Evans 	if (strcmp(command, "ujail") == 0)
5585952343eSKyle Evans 		command = "unjail";
5595952343eSKyle Evans 
56016a10da8SKyle Evans 	if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
5615952343eSKyle Evans 		return (usage(true));
5625952343eSKyle Evans 
563*490e13c1SKyle Evans 	if ((cmd = get_cmd_info(command)) == NULL) {
5645952343eSKyle Evans 		fprintf(stderr, "unknown command: %s\n", command);
5655952343eSKyle Evans 		return (usage(false));
5665952343eSKyle Evans 	}
5675952343eSKyle Evans 
568cc624025SKyle Evans 	if ((be = libbe_init(root)) == NULL)
5695952343eSKyle Evans 		return (-1);
5705952343eSKyle Evans 
571*490e13c1SKyle Evans 	libbe_print_on_error(be, !cmd->silent);
5725952343eSKyle Evans 
573*490e13c1SKyle Evans 	rc = cmd->fn(argc, argv);
5745952343eSKyle Evans 
5755952343eSKyle Evans 	libbe_close(be);
5765952343eSKyle Evans 	return (rc);
5775952343eSKyle Evans }
578