1e449ef0kevans/*-
2e449ef0kevans * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3f741758kevans *
4f741758kevans * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
5f741758kevans *
6f741758kevans * Redistribution and use in source and binary forms, with or without
7f741758kevans * modification, are permitted provided that the following conditions
8f741758kevans * are met:
9f741758kevans * 1. Redistributions of source code must retain the above copyright
10f741758kevans *    notice, this list of conditions and the following disclaimer.
11f741758kevans * 2. Redistributions in binary form must reproduce the above copyright
12f741758kevans *    notice, this list of conditions and the following disclaimer in the
13f741758kevans *    documentation and/or other materials provided with the distribution.
14f741758kevans *
15f741758kevans * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16f741758kevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17f741758kevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18f741758kevans * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19f741758kevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20f741758kevans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21f741758kevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22f741758kevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23f741758kevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24f741758kevans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25f741758kevans * SUCH DAMAGE.
26f741758kevans */
27f741758kevans
287c587a9kevans#include <sys/cdefs.h>
297c587a9kevans__FBSDID("$FreeBSD$");
307c587a9kevans
31f741758kevans#include <sys/param.h>
32f741758kevans#include <sys/mount.h>
33f741758kevans#include <errno.h>
34ccd45e1kevans#include <libutil.h>
35f741758kevans#include <stdbool.h>
36f741758kevans#include <stdio.h>
37f741758kevans#include <stdint.h>
38f741758kevans#include <stdlib.h>
39f741758kevans#include <string.h>
40f741758kevans#include <sysexits.h>
41ccd45e1kevans#include <time.h>
42f741758kevans#include <unistd.h>
43f741758kevans
44f741758kevans#include <be.h>
45f741758kevans
46b6064a8kevans#include "bectl.h"
47b6064a8kevans
48b45600dkevansstatic int bectl_cmd_activate(int argc, char *argv[]);
49add96fbkevansstatic int bectl_cmd_check(int argc, char *argv[]);
50b45600dkevansstatic int bectl_cmd_create(int argc, char *argv[]);
51b45600dkevansstatic int bectl_cmd_destroy(int argc, char *argv[]);
52b45600dkevansstatic int bectl_cmd_export(int argc, char *argv[]);
53b45600dkevansstatic int bectl_cmd_import(int argc, char *argv[]);
5492afc1fkevans#if SOON
55b45600dkevansstatic int bectl_cmd_add(int argc, char *argv[]);
5692afc1fkevans#endif
57b45600dkevansstatic int bectl_cmd_mount(int argc, char *argv[]);
58b45600dkevansstatic int bectl_cmd_rename(int argc, char *argv[]);
59b45600dkevansstatic int bectl_cmd_unmount(int argc, char *argv[]);
60f741758kevans
61b6064a8kevanslibbe_handle_t *be;
62f741758kevans
631477dd8mmacyint aok;
641477dd8mmacy
65b6064a8kevansint
66f741758kevansusage(bool explicit)
67f741758kevans{
68b4e1235kevans	FILE *fp;
69f741758kevans
70b4e1235kevans	fp =  explicit ? stdout : stderr;
716cc50bayuripv	fprintf(fp, "%s",
72df7a83dbcr	    "Usage:\tbectl {-h | -? | subcommand [args...]}\n"
736cc50bayuripv#if SOON
746cc50bayuripv	    "\tbectl add (path)*\n"
756cc50bayuripv#endif
76b45600dkevans	    "\tbectl activate [-t] beName\n"
77a9be09ftsoome	    "\tbectl activate [-T]\n"
78add96fbkevans	    "\tbectl check\n"
796cc50bayuripv	    "\tbectl create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n"
806cc50bayuripv	    "\tbectl create [-r] beName@snapshot\n"
819bd716fkevans	    "\tbectl destroy [-F] {beName | beName@snapshot}\n"
82b45600dkevans	    "\tbectl export sourceBe\n"
83b45600dkevans	    "\tbectl import targetBe\n"
846cc50bayuripv	    "\tbectl jail {-b | -U} [{-o key=value | -u key}]... "
856cc50bayuripv	    "{jailID | jailName}\n"
866cc50bayuripv	    "\t      bootenv [utility [argument ...]]\n"
87a7bf7adkevans	    "\tbectl list [-DHas] [{-c property | -C property}]\n"
88b45600dkevans	    "\tbectl mount beName [mountpoint]\n"
89b45600dkevans	    "\tbectl rename origBeName newBeName\n"
906cc50bayuripv	    "\tbectl {ujail | unjail} {jailID | jailName} bootenv\n"
919bd716fkevans	    "\tbectl {umount | unmount} [-f] beName\n");
92f741758kevans
93f741758kevans	return (explicit ? 0 : EX_USAGE);
94f741758kevans}
95f741758kevans
96f741758kevans
97f741758kevans/*
98f741758kevans * Represents a relationship between the command name and the parser action
99f741758kevans * that handles it.
100f741758kevans */
101f741758kevansstruct command_map_entry {
102f741758kevans	const char *command;
103f741758kevans	int (*fn)(int argc, char *argv[]);
104add96fbkevans	/* True if libbe_print_on_error should be disabled */
105add96fbkevans	bool silent;
106f741758kevans};
107f741758kevans
108f741758kevansstatic struct command_map_entry command_map[] =
109f741758kevans{
110add96fbkevans	{ "activate", bectl_cmd_activate,false   },
111add96fbkevans	{ "create",   bectl_cmd_create,  false   },
112add96fbkevans	{ "destroy",  bectl_cmd_destroy, false   },
113add96fbkevans	{ "export",   bectl_cmd_export,  false   },
114add96fbkevans	{ "import",   bectl_cmd_import,  false   },
11592afc1fkevans#if SOON
116add96fbkevans	{ "add",      bectl_cmd_add,     false   },
11792afc1fkevans#endif
118add96fbkevans	{ "jail",     bectl_cmd_jail,    false   },
119add96fbkevans	{ "list",     bectl_cmd_list,    false   },
120add96fbkevans	{ "mount",    bectl_cmd_mount,   false   },
121add96fbkevans	{ "rename",   bectl_cmd_rename,  false   },
122add96fbkevans	{ "unjail",   bectl_cmd_unjail,  false   },
123add96fbkevans	{ "unmount",  bectl_cmd_unmount, false   },
124add96fbkevans	{ "check",    bectl_cmd_check,   true    },
125f741758kevans};
126f741758kevans
127add96fbkevansstatic struct command_map_entry *
128add96fbkevansget_cmd_info(const char *cmd)
129f741758kevans{
130add96fbkevans	size_t i;
131f741758kevans
132add96fbkevans	for (i = 0; i < nitems(command_map); ++i) {
133add96fbkevans		if (strcmp(cmd, command_map[i].command) == 0)
134add96fbkevans			return (&command_map[i]);
135f741758kevans	}
136f741758kevans
137add96fbkevans	return (NULL);
138f741758kevans}
139f741758kevans
140f741758kevans
141f741758kevansstatic int
142b45600dkevansbectl_cmd_activate(int argc, char *argv[])
143f741758kevans{
144f741758kevans	int err, opt;
145a9be09ftsoome	bool temp, reset;
146f741758kevans
147f741758kevans	temp = false;
148a9be09ftsoome	reset = false;
149a9be09ftsoome	while ((opt = getopt(argc, argv, "tT")) != -1) {
150f741758kevans		switch (opt) {
151f741758kevans		case 't':
152a9be09ftsoome			if (reset)
153a9be09ftsoome				return (usage(false));
154f741758kevans			temp = true;
155f741758kevans			break;
156a9be09ftsoome		case 'T':
157a9be09ftsoome			if (temp)
158a9be09ftsoome				return (usage(false));
159a9be09ftsoome			reset = true;
160a9be09ftsoome			break;
161f741758kevans		default:
162193203bkevans			fprintf(stderr, "bectl activate: unknown option '-%c'\n",
163f741758kevans			    optopt);
164f741758kevans			return (usage(false));
165f741758kevans		}
166f741758kevans	}
167f741758kevans
168f741758kevans	argc -= optind;
169f741758kevans	argv += optind;
170f741758kevans
171a9be09ftsoome	if (argc != 1 && (!reset || argc != 0)) {
172193203bkevans		fprintf(stderr, "bectl activate: wrong number of arguments\n");
173f741758kevans		return (usage(false));
174f741758kevans	}
175f741758kevans
176a9be09ftsoome	if (reset) {
177a9be09ftsoome		if ((err = be_deactivate(be, NULL, reset)) == 0)
178a9be09ftsoome			printf("Temporary activation removed\n");
179a9be09ftsoome		else
180a9be09ftsoome			printf("Failed to remove temporary activation\n");
181a9be09ftsoome		return (err);
182a9be09ftsoome	}
183f741758kevans
184f741758kevans	/* activate logic goes here */
185b4e1235kevans	if ((err = be_activate(be, argv[0], temp)) != 0)
186b4e1235kevans		/* XXX TODO: more specific error msg based on err */
187df7a83dbcr		printf("Did not successfully activate boot environment %s\n",
188f741758kevans		    argv[0]);
189b4e1235kevans	else
190df7a83dbcr		printf("Successfully activated boot environment %s\n", argv[0]);
191f741758kevans
192b4e1235kevans	if (temp)
193f741758kevans		printf("for next boot\n");
194f741758kevans
195f741758kevans	return (err);
196f741758kevans}
197f741758kevans
198f741758kevans
199b4e1235kevans/*
200b4e1235kevans * TODO: when only one arg is given, and it contains an "@" the this should
201b4e1235kevans * create that snapshot
202b4e1235kevans */
203f741758kevansstatic int
204b45600dkevansbectl_cmd_create(int argc, char *argv[])
205f741758kevans{
2069833cb4kevans	char snapshot[BE_MAXPATHLEN];
2079833cb4kevans	char *atpos, *bootenv, *snapname;
208f741758kevans	int err, opt;
2092018f9bkevans	bool recursive;
210f741758kevans
211f741758kevans	snapname = NULL;
2122018f9bkevans	recursive = false;
2134cdb717kevans	while ((opt = getopt(argc, argv, "e:r")) != -1) {
214f741758kevans		switch (opt) {
215f741758kevans		case 'e':
216f741758kevans			snapname = optarg;
217f741758kevans			break;
2182018f9bkevans		case 'r':
2192018f9bkevans			recursive = true;
2204cdb717kevans			break;
221f741758kevans		default:
222193203bkevans			fprintf(stderr, "bectl create: unknown option '-%c'\n",
223f741758kevans			    optopt);
224f741758kevans			return (usage(false));
225f741758kevans		}
226f741758kevans	}
227f741758kevans
228f741758kevans	argc -= optind;
229f741758kevans	argv += optind;
230f741758kevans
231f741758kevans	if (argc != 1) {
232193203bkevans		fprintf(stderr, "bectl create: wrong number of arguments\n");
233f741758kevans		return (usage(false));
234f741758kevans	}
235f741758kevans
236f741758kevans	bootenv = *argv;
2379833cb4kevans
2389833cb4kevans	err = BE_ERR_SUCCESS;
2392018f9bkevans	if ((atpos = strchr(bootenv, '@')) != NULL) {
2402018f9bkevans		/*
2412018f9bkevans		 * This is the "create a snapshot variant". No new boot
2422018f9bkevans		 * environment is to be created here.
2432018f9bkevans		 */
2442018f9bkevans		*atpos++ = '\0';
2452018f9bkevans		err = be_snapshot(be, bootenv, atpos, recursive, NULL);
246f741758kevans	} else {
2479833cb4kevans		if (snapname == NULL)
2489833cb4kevans			/* Create from currently booted BE */
2499833cb4kevans			err = be_snapshot(be, be_active_path(be), NULL,
2509833cb4kevans			    recursive, snapshot);
2519833cb4kevans		else if (strchr(snapname, '@') != NULL)
2529833cb4kevans			/* Create from given snapshot */
2539833cb4kevans			strlcpy(snapshot, snapname, sizeof(snapshot));
2549833cb4kevans		else
2559833cb4kevans			/* Create from given BE */
2569833cb4kevans			err = be_snapshot(be, snapname, NULL, recursive,
2579833cb4kevans			    snapshot);
2589833cb4kevans
2599833cb4kevans		if (err == BE_ERR_SUCCESS)
2609833cb4kevans			err = be_create_depth(be, bootenv, snapshot,
2619833cb4kevans					      recursive == true ? -1 : 0);
262f741758kevans	}
263f741758kevans
264f741758kevans	switch (err) {
265f741758kevans	case BE_ERR_SUCCESS:
266f741758kevans		break;
267f741758kevans	default:
2682018f9bkevans		if (atpos != NULL)
2692018f9bkevans			fprintf(stderr,
270df7a83dbcr			    "Failed to create a snapshot '%s' of '%s'\n",
2712018f9bkevans			    atpos, bootenv);
2722018f9bkevans		else if (snapname == NULL)
273f741758kevans			fprintf(stderr,
274df7a83dbcr			    "Failed to create bootenv %s\n", bootenv);
275b4e1235kevans		else
276f741758kevans			fprintf(stderr,
277df7a83dbcr			    "Failed to create bootenv %s from snapshot %s\n",
278f741758kevans			    bootenv, snapname);
279f741758kevans	}
280f741758kevans
281f741758kevans	return (err);
282f741758kevans}
283f741758kevans
284f741758kevans
285f741758kevansstatic int
286b45600dkevansbectl_cmd_export(int argc, char *argv[])
287f741758kevans{
288f741758kevans	char *bootenv;
289f741758kevans
290f741758kevans	if (argc == 1) {
291193203bkevans		fprintf(stderr, "bectl export: missing boot environment name\n");
292f741758kevans		return (usage(false));
293f741758kevans	}
294f741758kevans
295f741758kevans	if (argc > 2) {
296193203bkevans		fprintf(stderr, "bectl export: extra arguments provided\n");
297f741758kevans		return (usage(false));
298f741758kevans	}
299f741758kevans
300f741758kevans	bootenv = argv[1];
301f741758kevans
302f741758kevans	if (isatty(STDOUT_FILENO)) {
303193203bkevans		fprintf(stderr, "bectl export: must redirect output\n");
304f741758kevans		return (EX_USAGE);
305f741758kevans	}
306f741758kevans
307f741758kevans	be_export(be, bootenv, STDOUT_FILENO);
308f741758kevans
309f741758kevans	return (0);
310f741758kevans}
311f741758kevans
312f741758kevans
313f741758kevansstatic int
314b45600dkevansbectl_cmd_import(int argc, char *argv[])
315f741758kevans{
316f741758kevans	char *bootenv;
317f741758kevans	int err;
318f741758kevans
319f741758kevans	if (argc == 1) {
320193203bkevans		fprintf(stderr, "bectl import: missing boot environment name\n");
321f741758kevans		return (usage(false));
322f741758kevans	}
323f741758kevans
324f741758kevans	if (argc > 2) {
325193203bkevans		fprintf(stderr, "bectl import: extra arguments provided\n");
326f741758kevans		return (usage(false));
327f741758kevans	}
328f741758kevans
329f741758kevans	bootenv = argv[1];
330f741758kevans
331f741758kevans	if (isatty(STDIN_FILENO)) {
332193203bkevans		fprintf(stderr, "bectl import: input can not be from terminal\n");
333f741758kevans		return (EX_USAGE);
334f741758kevans	}
335f741758kevans
336f741758kevans	err = be_import(be, bootenv, STDIN_FILENO);
337f741758kevans
338f741758kevans	return (err);
339f741758kevans}
340f741758kevans
34192afc1fkevans#if SOON
342f741758kevansstatic int
343b45600dkevansbectl_cmd_add(int argc, char *argv[])
344f741758kevans{
345f741758kevans
346f741758kevans	if (argc < 2) {
347193203bkevans		fprintf(stderr, "bectl add: must provide at least one path\n");
348f741758kevans		return (usage(false));
349f741758kevans	}
350f741758kevans
351f741758kevans	for (int i = 1; i < argc; ++i) {
352f741758kevans		printf("arg %d: %s\n", i, argv[i]);
353b4e1235kevans		/* XXX TODO catch err */
354f741758kevans		be_add_child(be, argv[i], true);
355f741758kevans	}
356f741758kevans
357f741758kevans	return (0);
358f741758kevans}
35992afc1fkevans#endif
360f741758kevans
361f741758kevansstatic int
362b45600dkevansbectl_cmd_destroy(int argc, char *argv[])
363f741758kevans{
3644d060aakevans	nvlist_t *props;
3654d060aakevans	char *origin, *target, targetds[BE_MAXPATHLEN];
3664d060aakevans	int err, flags, opt;
367f741758kevans
3684d060aakevans	flags = 0;
3694d060aakevans	while ((opt = getopt(argc, argv, "Fo")) != -1) {
370f741758kevans		switch (opt) {
371f741758kevans		case 'F':
3724d060aakevans			flags |= BE_DESTROY_FORCE;
3734d060aakevans			break;
3744d060aakevans		case 'o':
3754d060aakevans			flags |= BE_DESTROY_ORIGIN;
376f741758kevans			break;
377f741758kevans		default:
378193203bkevans			fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
379f741758kevans			    optopt);
380f741758kevans			return (usage(false));
381f741758kevans		}
382f741758kevans	}
383f741758kevans
384f741758kevans	argc -= optind;
385f741758kevans	argv += optind;
386f741758kevans
387f741758kevans	if (argc != 1) {
388193203bkevans		fprintf(stderr, "bectl destroy: wrong number of arguments\n");
389f741758kevans		return (usage(false));
390f741758kevans	}
391f741758kevans
392f741758kevans	target = argv[0];
393f741758kevans
3944d060aakevans	/* We'll emit a notice if there's an origin to be cleaned up */
3954d060aakevans	if ((flags & BE_DESTROY_ORIGIN) == 0 && strchr(target, '@') == NULL) {
3962550d91kevans		flags |= BE_DESTROY_AUTOORIGIN;
3974d060aakevans		if (be_root_concat(be, target, targetds) != 0)
3984d060aakevans			goto destroy;
3994d060aakevans		if (be_prop_list_alloc(&props) != 0)
4004d060aakevans			goto destroy;
4014d060aakevans		if (be_get_dataset_props(be, targetds, props) != 0) {
4024d060aakevans			be_prop_list_free(props);
4034d060aakevans			goto destroy;
4044d060aakevans		}
4052550d91kevans		if (nvlist_lookup_string(props, "origin", &origin) == 0 &&
4062550d91kevans		    !be_is_auto_snapshot_name(be, origin))
4074d060aakevans			fprintf(stderr, "bectl destroy: leaving origin '%s' intact\n",
4084d060aakevans			    origin);
4094d060aakevans		be_prop_list_free(props);
4104d060aakevans	}
4114d060aakevans
4124d060aakevansdestroy:
4134d060aakevans	err = be_destroy(be, target, flags);
414f741758kevans
415f741758kevans	return (err);
416f741758kevans}
417f741758kevans
418f741758kevansstatic int
419b45600dkevansbectl_cmd_mount(int argc, char *argv[])
420f741758kevans{
421f741758kevans	char result_loc[BE_MAXPATHLEN];
422b4e1235kevans	char *bootenv, *mountpoint;
423c8fc070kevans	int err, mntflags;
424f741758kevans
425c8fc070kevans	/* XXX TODO: Allow shallow */
426c8fc070kevans	mntflags = BE_MNT_DEEP;
427f741758kevans	if (argc < 2) {
428193203bkevans		fprintf(stderr, "bectl mount: missing argument(s)\n");
429f741758kevans		return (usage(false));
430f741758kevans	}
431f741758kevans
432f741758kevans	if (argc > 3) {
433193203bkevans		fprintf(stderr, "bectl mount: too many arguments\n");
434f741758kevans		return (usage(false));
435f741758kevans	}
436f741758kevans
437f741758kevans	bootenv = argv[1];
438f741758kevans	mountpoint = ((argc == 3) ? argv[2] : NULL);
439f741758kevans
440c8fc070kevans	err = be_mount(be, bootenv, mountpoint, mntflags, result_loc);
441f741758kevans
442f741758kevans	switch (err) {
443f741758kevans	case BE_ERR_SUCCESS:
444df7a83dbcr		printf("Successfully mounted %s at %s\n", bootenv, result_loc);
445f741758kevans		break;
446f741758kevans	default:
447f741758kevans		fprintf(stderr,
448df7a83dbcr		    (argc == 3) ? "Failed to mount bootenv %s at %s\n" :
449df7a83dbcr		    "Failed to mount bootenv %s at temporary path %s\n",
450f741758kevans		    bootenv, mountpoint);
451f741758kevans	}
452f741758kevans
453f741758kevans	return (err);
454f741758kevans}
455f741758kevans
456f741758kevans
457f741758kevansstatic int
458b45600dkevansbectl_cmd_rename(int argc, char *argv[])
459f741758kevans{
460b4e1235kevans	char *dest, *src;
461f741758kevans	int err;
462f741758kevans
463f741758kevans	if (argc < 3) {
464193203bkevans		fprintf(stderr, "bectl rename: missing argument\n");
465f741758kevans		return (usage(false));
466f741758kevans	}
467f741758kevans
468f741758kevans	if (argc > 3) {
469193203bkevans		fprintf(stderr, "bectl rename: too many arguments\n");
470f741758kevans		return (usage(false));
471f741758kevans	}
472f741758kevans
473f741758kevans	src = argv[1];
474f741758kevans	dest = argv[2];
475f741758kevans
476f741758kevans	err = be_rename(be, src, dest);
477f741758kevans
478f741758kevans	switch (err) {
479f741758kevans	case BE_ERR_SUCCESS:
480f741758kevans		break;
481f741758kevans	default:
482df7a83dbcr		fprintf(stderr, "Failed to rename bootenv %s to %s\n",
483f741758kevans		    src, dest);
484f741758kevans	}
485f741758kevans
486f741758kevans	return (0);
487f741758kevans}
488f741758kevans
48938d4afekevansstatic int
490b45600dkevansbectl_cmd_unmount(int argc, char *argv[])
491f741758kevans{
492b4e1235kevans	char *bootenv, *cmd;
493f741758kevans	int err, flags, opt;
494f741758kevans
495f741758kevans	/* Store alias used */
496f741758kevans	cmd = argv[0];
497f741758kevans
498f741758kevans	flags = 0;
499f741758kevans	while ((opt = getopt(argc, argv, "f")) != -1) {
500f741758kevans		switch (opt) {
501f741758kevans		case 'f':
502f741758kevans			flags |= BE_MNT_FORCE;
503f741758kevans			break;
504f741758kevans		default:
505193203bkevans			fprintf(stderr, "bectl %s: unknown option '-%c'\n",
506f741758kevans			    cmd, optopt);
507f741758kevans			return (usage(false));
508f741758kevans		}
509f741758kevans	}
510f741758kevans
511f741758kevans	argc -= optind;
512f741758kevans	argv += optind;
513f741758kevans
514f741758kevans	if (argc != 1) {
515193203bkevans		fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
516f741758kevans		return (usage(false));
517f741758kevans	}
518f741758kevans
519f741758kevans	bootenv = argv[0];
520f741758kevans
521f741758kevans	err = be_unmount(be, bootenv, flags);
522f741758kevans
523f741758kevans	switch (err) {
524f741758kevans	case BE_ERR_SUCCESS:
525f741758kevans		break;
526f741758kevans	default:
527df7a83dbcr		fprintf(stderr, "Failed to unmount bootenv %s\n", bootenv);
528f741758kevans	}
529f741758kevans
530f741758kevans	return (err);
531f741758kevans}
532f741758kevans
533add96fbkevansstatic int
534add96fbkevansbectl_cmd_check(int argc, char *argv[] __unused)
535add96fbkevans{
536add96fbkevans
537add96fbkevans	/* The command is left as argv[0] */
538add96fbkevans	if (argc != 1) {
539add96fbkevans		fprintf(stderr, "bectl check: wrong number of arguments\n");
540add96fbkevans		return (usage(false));
541add96fbkevans	}
542add96fbkevans
543add96fbkevans	return (0);
544add96fbkevans}
545f741758kevans
546f741758kevansint
547f741758kevansmain(int argc, char *argv[])
548f741758kevans{
549add96fbkevans	struct command_map_entry *cmd;
550fa415c9kevans	const char *command;
5512590521kevans	char *root;
552add96fbkevans	int rc;
553f741758kevans
554add96fbkevans	cmd = NULL;
5552590521kevans	root = NULL;
55651650c5kevans	if (argc < 2)
557f741758kevans		return (usage(false));
558f741758kevans
5592590521kevans	if (strcmp(argv[1], "-r") == 0) {
5602590521kevans		if (argc < 4)
5612590521kevans			return (usage(false));
5622590521kevans		root = strdup(argv[2]);
5632590521kevans		command = argv[3];
5642590521kevans		argc -= 3;
5652590521kevans		argv += 3;
5662590521kevans	} else {
5672590521kevans		command = argv[1];
5682590521kevans		argc -= 1;
5692590521kevans		argv += 1;
5702590521kevans	}
571f741758kevans
572f741758kevans	/* Handle command aliases */
573b4e1235kevans	if (strcmp(command, "umount") == 0)
574f741758kevans		command = "unmount";
575f741758kevans
576b4e1235kevans	if (strcmp(command, "ujail") == 0)
577f741758kevans		command = "unjail";
578f741758kevans
579b4e1235kevans	if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
580f741758kevans		return (usage(true));
581f741758kevans
582add96fbkevans	if ((cmd = get_cmd_info(command)) == NULL) {
583df7a83dbcr		fprintf(stderr, "Unknown command: %s\n", command);
584f741758kevans		return (usage(false));
585f741758kevans	}
586f741758kevans
5872590521kevans	if ((be = libbe_init(root)) == NULL)
588f741758kevans		return (-1);
589f741758kevans
590add96fbkevans	libbe_print_on_error(be, !cmd->silent);
591f741758kevans
592add96fbkevans	rc = cmd->fn(argc, argv);
593f741758kevans
594f741758kevans	libbe_close(be);
595f741758kevans	return (rc);
596f741758kevans}
597