1337414Skevans/*-
2337414Skevans * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3336668Skevans *
4336668Skevans * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
5336668Skevans *
6336668Skevans * Redistribution and use in source and binary forms, with or without
7336668Skevans * modification, are permitted provided that the following conditions
8336668Skevans * are met:
9336668Skevans * 1. Redistributions of source code must retain the above copyright
10336668Skevans *    notice, this list of conditions and the following disclaimer.
11336668Skevans * 2. Redistributions in binary form must reproduce the above copyright
12336668Skevans *    notice, this list of conditions and the following disclaimer in the
13336668Skevans *    documentation and/or other materials provided with the distribution.
14336668Skevans *
15336668Skevans * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16336668Skevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17336668Skevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18336668Skevans * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19336668Skevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20336668Skevans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21336668Skevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22336668Skevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23336668Skevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24336668Skevans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25336668Skevans * SUCH DAMAGE.
26336668Skevans */
27336668Skevans
28337416Skevans#include <sys/cdefs.h>
29337416Skevans__FBSDID("$FreeBSD: stable/11/sbin/bectl/bectl.c 357667 2020-02-07 21:57:27Z kevans $");
30337416Skevans
31336668Skevans#include <sys/param.h>
32336668Skevans#include <sys/mount.h>
33336668Skevans#include <errno.h>
34336747Skevans#include <libutil.h>
35336668Skevans#include <stdbool.h>
36336668Skevans#include <stdio.h>
37336668Skevans#include <stdint.h>
38336668Skevans#include <stdlib.h>
39336668Skevans#include <string.h>
40336668Skevans#include <sysexits.h>
41336747Skevans#include <time.h>
42336668Skevans#include <unistd.h>
43336668Skevans
44336668Skevans#include <be.h>
45336668Skevans
46337368Skevans#include "bectl.h"
47337368Skevans
48336694Skevansstatic int bectl_cmd_activate(int argc, char *argv[]);
49352348Skevansstatic int bectl_cmd_check(int argc, char *argv[]);
50336694Skevansstatic int bectl_cmd_create(int argc, char *argv[]);
51336694Skevansstatic int bectl_cmd_destroy(int argc, char *argv[]);
52336694Skevansstatic int bectl_cmd_export(int argc, char *argv[]);
53336694Skevansstatic int bectl_cmd_import(int argc, char *argv[]);
54337596Skevans#if SOON
55336694Skevansstatic int bectl_cmd_add(int argc, char *argv[]);
56337596Skevans#endif
57336694Skevansstatic int bectl_cmd_mount(int argc, char *argv[]);
58336694Skevansstatic int bectl_cmd_rename(int argc, char *argv[]);
59336694Skevansstatic int bectl_cmd_unmount(int argc, char *argv[]);
60336668Skevans
61337368Skevanslibbe_handle_t *be;
62336668Skevans
63337368Skevansint
64336668Skevansusage(bool explicit)
65336668Skevans{
66336703Skevans	FILE *fp;
67336668Skevans
68336703Skevans	fp =  explicit ? stdout : stderr;
69346429Skevans	fprintf(fp, "%s",
70346429Skevans	    "usage:\tbectl {-h | -? | subcommand [args...]}\n"
71346429Skevans#if SOON
72346429Skevans	    "\tbectl add (path)*\n"
73346429Skevans#endif
74336694Skevans	    "\tbectl activate [-t] beName\n"
75352348Skevans	    "\tbectl check\n"
76346429Skevans	    "\tbectl create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n"
77346429Skevans	    "\tbectl create [-r] beName@snapshot\n"
78346429Skevans	    "\tbectl destroy [-F] {beName | beName@snapshot}\n"
79336694Skevans	    "\tbectl export sourceBe\n"
80336694Skevans	    "\tbectl import targetBe\n"
81346429Skevans	    "\tbectl jail {-b | -U} [{-o key=value | -u key}]... "
82346429Skevans	    "{jailID | jailName}\n"
83346429Skevans	    "\t      bootenv [utility [argument ...]]\n"
84352088Skevans	    "\tbectl list [-DHas] [{-c property | -C property}]\n"
85336694Skevans	    "\tbectl mount beName [mountpoint]\n"
86336694Skevans	    "\tbectl rename origBeName newBeName\n"
87346429Skevans	    "\tbectl {ujail | unjail} {jailID | jailName} bootenv\n"
88346429Skevans	    "\tbectl {umount | unmount} [-f] beName\n");
89336668Skevans
90336668Skevans	return (explicit ? 0 : EX_USAGE);
91336668Skevans}
92336668Skevans
93336668Skevans
94336668Skevans/*
95336668Skevans * Represents a relationship between the command name and the parser action
96336668Skevans * that handles it.
97336668Skevans */
98336668Skevansstruct command_map_entry {
99336668Skevans	const char *command;
100336668Skevans	int (*fn)(int argc, char *argv[]);
101352348Skevans	/* True if libbe_print_on_error should be disabled */
102352348Skevans	bool silent;
103336668Skevans};
104336668Skevans
105336668Skevansstatic struct command_map_entry command_map[] =
106336668Skevans{
107352348Skevans	{ "activate", bectl_cmd_activate,false   },
108352348Skevans	{ "create",   bectl_cmd_create,  false   },
109352348Skevans	{ "destroy",  bectl_cmd_destroy, false   },
110352348Skevans	{ "export",   bectl_cmd_export,  false   },
111352348Skevans	{ "import",   bectl_cmd_import,  false   },
112337596Skevans#if SOON
113352348Skevans	{ "add",      bectl_cmd_add,     false   },
114337596Skevans#endif
115352348Skevans	{ "jail",     bectl_cmd_jail,    false   },
116352348Skevans	{ "list",     bectl_cmd_list,    false   },
117352348Skevans	{ "mount",    bectl_cmd_mount,   false   },
118352348Skevans	{ "rename",   bectl_cmd_rename,  false   },
119352348Skevans	{ "unjail",   bectl_cmd_unjail,  false   },
120352348Skevans	{ "unmount",  bectl_cmd_unmount, false   },
121352348Skevans	{ "check",    bectl_cmd_check,   true    },
122336668Skevans};
123336668Skevans
124352348Skevansstatic struct command_map_entry *
125352348Skevansget_cmd_info(const char *cmd)
126336668Skevans{
127352348Skevans	size_t i;
128336668Skevans
129352348Skevans	for (i = 0; i < nitems(command_map); ++i) {
130352348Skevans		if (strcmp(cmd, command_map[i].command) == 0)
131352348Skevans			return (&command_map[i]);
132336668Skevans	}
133336668Skevans
134352348Skevans	return (NULL);
135336668Skevans}
136336668Skevans
137336668Skevans
138336668Skevansstatic int
139336694Skevansbectl_cmd_activate(int argc, char *argv[])
140336668Skevans{
141336668Skevans	int err, opt;
142336668Skevans	bool temp;
143336668Skevans
144336668Skevans	temp = false;
145336668Skevans	while ((opt = getopt(argc, argv, "t")) != -1) {
146336668Skevans		switch (opt) {
147336668Skevans		case 't':
148336668Skevans			temp = true;
149336668Skevans			break;
150336668Skevans		default:
151336702Skevans			fprintf(stderr, "bectl activate: unknown option '-%c'\n",
152336668Skevans			    optopt);
153336668Skevans			return (usage(false));
154336668Skevans		}
155336668Skevans	}
156336668Skevans
157336668Skevans	argc -= optind;
158336668Skevans	argv += optind;
159336668Skevans
160336668Skevans	if (argc != 1) {
161336702Skevans		fprintf(stderr, "bectl activate: wrong number of arguments\n");
162336668Skevans		return (usage(false));
163336668Skevans	}
164336668Skevans
165336668Skevans
166336668Skevans	/* activate logic goes here */
167336703Skevans	if ((err = be_activate(be, argv[0], temp)) != 0)
168336703Skevans		/* XXX TODO: more specific error msg based on err */
169336668Skevans		printf("did not successfully activate boot environment %s\n",
170336668Skevans		    argv[0]);
171336703Skevans	else
172336668Skevans		printf("successfully activated boot environment %s\n", argv[0]);
173336668Skevans
174336703Skevans	if (temp)
175336668Skevans		printf("for next boot\n");
176336668Skevans
177336668Skevans	return (err);
178336668Skevans}
179336668Skevans
180336668Skevans
181336703Skevans/*
182336703Skevans * TODO: when only one arg is given, and it contains an "@" the this should
183336703Skevans * create that snapshot
184336703Skevans */
185336668Skevansstatic int
186336694Skevansbectl_cmd_create(int argc, char *argv[])
187336668Skevans{
188350344Skevans	char snapshot[BE_MAXPATHLEN];
189350344Skevans	char *atpos, *bootenv, *snapname;
190336668Skevans	int err, opt;
191346429Skevans	bool recursive;
192336668Skevans
193336668Skevans	snapname = NULL;
194346429Skevans	recursive = false;
195346429Skevans	while ((opt = getopt(argc, argv, "e:r")) != -1) {
196336668Skevans		switch (opt) {
197336668Skevans		case 'e':
198336668Skevans			snapname = optarg;
199336668Skevans			break;
200346429Skevans		case 'r':
201346429Skevans			recursive = true;
202346429Skevans			break;
203336668Skevans		default:
204336702Skevans			fprintf(stderr, "bectl create: unknown option '-%c'\n",
205336668Skevans			    optopt);
206336668Skevans			return (usage(false));
207336668Skevans		}
208336668Skevans	}
209336668Skevans
210336668Skevans	argc -= optind;
211336668Skevans	argv += optind;
212336668Skevans
213336668Skevans	if (argc != 1) {
214336702Skevans		fprintf(stderr, "bectl create: wrong number of arguments\n");
215336668Skevans		return (usage(false));
216336668Skevans	}
217336668Skevans
218336668Skevans	bootenv = *argv;
219350344Skevans
220350344Skevans	err = BE_ERR_SUCCESS;
221346429Skevans	if ((atpos = strchr(bootenv, '@')) != NULL) {
222346429Skevans		/*
223346429Skevans		 * This is the "create a snapshot variant". No new boot
224346429Skevans		 * environment is to be created here.
225346429Skevans		 */
226346429Skevans		*atpos++ = '\0';
227346429Skevans		err = be_snapshot(be, bootenv, atpos, recursive, NULL);
228350344Skevans	} else {
229350344Skevans		if (snapname == NULL)
230350344Skevans			/* Create from currently booted BE */
231350344Skevans			err = be_snapshot(be, be_active_path(be), NULL,
232350344Skevans			    recursive, snapshot);
233350344Skevans		else if (strchr(snapname, '@') != NULL)
234350344Skevans			/* Create from given snapshot */
235350344Skevans			strlcpy(snapshot, snapname, sizeof(snapshot));
236336703Skevans		else
237350344Skevans			/* Create from given BE */
238350344Skevans			err = be_snapshot(be, snapname, NULL, recursive,
239350344Skevans			    snapshot);
240350344Skevans
241350344Skevans		if (err == BE_ERR_SUCCESS)
242350344Skevans			err = be_create_depth(be, bootenv, snapshot,
243350344Skevans					      recursive == true ? -1 : 0);
244336668Skevans	}
245336668Skevans
246336668Skevans	switch (err) {
247336668Skevans	case BE_ERR_SUCCESS:
248336668Skevans		break;
249336668Skevans	default:
250346429Skevans		if (atpos != NULL)
251336668Skevans			fprintf(stderr,
252346429Skevans			    "failed to create a snapshot '%s' of '%s'\n",
253346429Skevans			    atpos, bootenv);
254346429Skevans		else if (snapname == NULL)
255346429Skevans			fprintf(stderr,
256336668Skevans			    "failed to create bootenv %s\n", bootenv);
257336703Skevans		else
258336668Skevans			fprintf(stderr,
259336668Skevans			    "failed to create bootenv %s from snapshot %s\n",
260336668Skevans			    bootenv, snapname);
261336668Skevans	}
262336668Skevans
263336668Skevans	return (err);
264336668Skevans}
265336668Skevans
266336668Skevans
267336668Skevansstatic int
268336694Skevansbectl_cmd_export(int argc, char *argv[])
269336668Skevans{
270336703Skevans	char *bootenv;
271336668Skevans
272336668Skevans	if (argc == 1) {
273336702Skevans		fprintf(stderr, "bectl export: missing boot environment name\n");
274336668Skevans		return (usage(false));
275336668Skevans	}
276336668Skevans
277336668Skevans	if (argc > 2) {
278336702Skevans		fprintf(stderr, "bectl export: extra arguments provided\n");
279336668Skevans		return (usage(false));
280336668Skevans	}
281336668Skevans
282336668Skevans	bootenv = argv[1];
283336668Skevans
284336668Skevans	if (isatty(STDOUT_FILENO)) {
285336702Skevans		fprintf(stderr, "bectl export: must redirect output\n");
286336668Skevans		return (EX_USAGE);
287336668Skevans	}
288336668Skevans
289336668Skevans	be_export(be, bootenv, STDOUT_FILENO);
290336668Skevans
291336668Skevans	return (0);
292336668Skevans}
293336668Skevans
294336668Skevans
295336668Skevansstatic int
296336694Skevansbectl_cmd_import(int argc, char *argv[])
297336668Skevans{
298336668Skevans	char *bootenv;
299336668Skevans	int err;
300336668Skevans
301336668Skevans	if (argc == 1) {
302336702Skevans		fprintf(stderr, "bectl import: missing boot environment name\n");
303336668Skevans		return (usage(false));
304336668Skevans	}
305336668Skevans
306336668Skevans	if (argc > 2) {
307336702Skevans		fprintf(stderr, "bectl import: extra arguments provided\n");
308336668Skevans		return (usage(false));
309336668Skevans	}
310336668Skevans
311336668Skevans	bootenv = argv[1];
312336668Skevans
313336668Skevans	if (isatty(STDIN_FILENO)) {
314336702Skevans		fprintf(stderr, "bectl import: input can not be from terminal\n");
315336668Skevans		return (EX_USAGE);
316336668Skevans	}
317336668Skevans
318336668Skevans	err = be_import(be, bootenv, STDIN_FILENO);
319336668Skevans
320336668Skevans	return (err);
321336668Skevans}
322336668Skevans
323337596Skevans#if SOON
324336668Skevansstatic int
325336694Skevansbectl_cmd_add(int argc, char *argv[])
326336668Skevans{
327336668Skevans
328336668Skevans	if (argc < 2) {
329336702Skevans		fprintf(stderr, "bectl add: must provide at least one path\n");
330336668Skevans		return (usage(false));
331336668Skevans	}
332336668Skevans
333336668Skevans	for (int i = 1; i < argc; ++i) {
334336668Skevans		printf("arg %d: %s\n", i, argv[i]);
335336703Skevans		/* XXX TODO catch err */
336336668Skevans		be_add_child(be, argv[i], true);
337336668Skevans	}
338336668Skevans
339336668Skevans	return (0);
340336668Skevans}
341337596Skevans#endif
342336668Skevans
343336668Skevansstatic int
344336694Skevansbectl_cmd_destroy(int argc, char *argv[])
345336668Skevans{
346346429Skevans	nvlist_t *props;
347346429Skevans	char *origin, *target, targetds[BE_MAXPATHLEN];
348346429Skevans	int err, flags, opt;
349336668Skevans
350346429Skevans	flags = 0;
351346429Skevans	while ((opt = getopt(argc, argv, "Fo")) != -1) {
352336668Skevans		switch (opt) {
353336668Skevans		case 'F':
354346429Skevans			flags |= BE_DESTROY_FORCE;
355336668Skevans			break;
356346429Skevans		case 'o':
357346429Skevans			flags |= BE_DESTROY_ORIGIN;
358346429Skevans			break;
359336668Skevans		default:
360336702Skevans			fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
361336668Skevans			    optopt);
362336668Skevans			return (usage(false));
363336668Skevans		}
364336668Skevans	}
365336668Skevans
366336668Skevans	argc -= optind;
367336668Skevans	argv += optind;
368336668Skevans
369336668Skevans	if (argc != 1) {
370336702Skevans		fprintf(stderr, "bectl destroy: wrong number of arguments\n");
371336668Skevans		return (usage(false));
372336668Skevans	}
373336668Skevans
374336668Skevans	target = argv[0];
375336668Skevans
376346429Skevans	/* We'll emit a notice if there's an origin to be cleaned up */
377346429Skevans	if ((flags & BE_DESTROY_ORIGIN) == 0 && strchr(target, '@') == NULL) {
378353784Skevans		flags |= BE_DESTROY_AUTOORIGIN;
379346429Skevans		if (be_root_concat(be, target, targetds) != 0)
380346429Skevans			goto destroy;
381346429Skevans		if (be_prop_list_alloc(&props) != 0)
382346429Skevans			goto destroy;
383346429Skevans		if (be_get_dataset_props(be, targetds, props) != 0) {
384346429Skevans			be_prop_list_free(props);
385346429Skevans			goto destroy;
386346429Skevans		}
387353784Skevans		if (nvlist_lookup_string(props, "origin", &origin) == 0 &&
388353784Skevans		    !be_is_auto_snapshot_name(be, origin))
389346429Skevans			fprintf(stderr, "bectl destroy: leaving origin '%s' intact\n",
390346429Skevans			    origin);
391346429Skevans		be_prop_list_free(props);
392346429Skevans	}
393336668Skevans
394346429Skevansdestroy:
395346429Skevans	err = be_destroy(be, target, flags);
396346429Skevans
397336668Skevans	return (err);
398336668Skevans}
399336668Skevans
400337358Skevansstatic int
401336694Skevansbectl_cmd_mount(int argc, char *argv[])
402336668Skevans{
403336703Skevans	char result_loc[BE_MAXPATHLEN];
404336703Skevans	char *bootenv, *mountpoint;
405346429Skevans	int err, mntflags;
406336668Skevans
407346429Skevans	/* XXX TODO: Allow shallow */
408346429Skevans	mntflags = BE_MNT_DEEP;
409336668Skevans	if (argc < 2) {
410336702Skevans		fprintf(stderr, "bectl mount: missing argument(s)\n");
411336668Skevans		return (usage(false));
412336668Skevans	}
413336668Skevans
414336668Skevans	if (argc > 3) {
415336702Skevans		fprintf(stderr, "bectl mount: too many arguments\n");
416336668Skevans		return (usage(false));
417336668Skevans	}
418336668Skevans
419336668Skevans	bootenv = argv[1];
420336668Skevans	mountpoint = ((argc == 3) ? argv[2] : NULL);
421336668Skevans
422346429Skevans	err = be_mount(be, bootenv, mountpoint, mntflags, result_loc);
423336668Skevans
424336668Skevans	switch (err) {
425336668Skevans	case BE_ERR_SUCCESS:
426336668Skevans		printf("successfully mounted %s at %s\n", bootenv, result_loc);
427336668Skevans		break;
428336668Skevans	default:
429336668Skevans		fprintf(stderr,
430336668Skevans		    (argc == 3) ? "failed to mount bootenv %s at %s\n" :
431336668Skevans		    "failed to mount bootenv %s at temporary path %s\n",
432336668Skevans		    bootenv, mountpoint);
433336668Skevans	}
434336668Skevans
435336668Skevans	return (err);
436336668Skevans}
437336668Skevans
438336668Skevans
439336668Skevansstatic int
440336694Skevansbectl_cmd_rename(int argc, char *argv[])
441336668Skevans{
442336703Skevans	char *dest, *src;
443336668Skevans	int err;
444336668Skevans
445336668Skevans	if (argc < 3) {
446336702Skevans		fprintf(stderr, "bectl rename: missing argument\n");
447336668Skevans		return (usage(false));
448336668Skevans	}
449336668Skevans
450336668Skevans	if (argc > 3) {
451336702Skevans		fprintf(stderr, "bectl rename: too many arguments\n");
452336668Skevans		return (usage(false));
453336668Skevans	}
454336668Skevans
455336668Skevans	src = argv[1];
456336668Skevans	dest = argv[2];
457336668Skevans
458336668Skevans	err = be_rename(be, src, dest);
459336668Skevans
460336668Skevans	switch (err) {
461336668Skevans	case BE_ERR_SUCCESS:
462336668Skevans		break;
463336668Skevans	default:
464336668Skevans		fprintf(stderr, "failed to rename bootenv %s to %s\n",
465336668Skevans		    src, dest);
466336668Skevans	}
467336668Skevans
468336668Skevans	return (0);
469336668Skevans}
470336668Skevans
471336730Skevansstatic int
472336694Skevansbectl_cmd_unmount(int argc, char *argv[])
473336668Skevans{
474336703Skevans	char *bootenv, *cmd;
475336668Skevans	int err, flags, opt;
476336668Skevans
477336668Skevans	/* Store alias used */
478336668Skevans	cmd = argv[0];
479336668Skevans
480336668Skevans	flags = 0;
481336668Skevans	while ((opt = getopt(argc, argv, "f")) != -1) {
482336668Skevans		switch (opt) {
483336668Skevans		case 'f':
484336668Skevans			flags |= BE_MNT_FORCE;
485336668Skevans			break;
486336668Skevans		default:
487336702Skevans			fprintf(stderr, "bectl %s: unknown option '-%c'\n",
488336668Skevans			    cmd, optopt);
489336668Skevans			return (usage(false));
490336668Skevans		}
491336668Skevans	}
492336668Skevans
493336668Skevans	argc -= optind;
494336668Skevans	argv += optind;
495336668Skevans
496336668Skevans	if (argc != 1) {
497336702Skevans		fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
498336668Skevans		return (usage(false));
499336668Skevans	}
500336668Skevans
501336668Skevans	bootenv = argv[0];
502336668Skevans
503336668Skevans	err = be_unmount(be, bootenv, flags);
504336668Skevans
505336668Skevans	switch (err) {
506336668Skevans	case BE_ERR_SUCCESS:
507336668Skevans		break;
508336668Skevans	default:
509336668Skevans		fprintf(stderr, "failed to unmount bootenv %s\n", bootenv);
510336668Skevans	}
511336668Skevans
512336668Skevans	return (err);
513336668Skevans}
514336668Skevans
515352348Skevansstatic int
516352348Skevansbectl_cmd_check(int argc, char *argv[] __unused)
517352348Skevans{
518336668Skevans
519352348Skevans	/* The command is left as argv[0] */
520352348Skevans	if (argc != 1) {
521352348Skevans		fprintf(stderr, "bectl check: wrong number of arguments\n");
522352348Skevans		return (usage(false));
523352348Skevans	}
524352348Skevans
525352348Skevans	return (0);
526352348Skevans}
527352348Skevans
528336668Skevansint
529336668Skevansmain(int argc, char *argv[])
530336668Skevans{
531352348Skevans	struct command_map_entry *cmd;
532336710Skevans	const char *command;
533346429Skevans	char *root;
534352348Skevans	int rc;
535336668Skevans
536352348Skevans	cmd = NULL;
537346429Skevans	root = NULL;
538346429Skevans	if (argc < 2)
539336668Skevans		return (usage(false));
540346429Skevans
541346429Skevans	if (strcmp(argv[1], "-r") == 0) {
542346429Skevans		if (argc < 4)
543346429Skevans			return (usage(false));
544346429Skevans		root = strdup(argv[2]);
545346429Skevans		command = argv[3];
546346429Skevans		argc -= 3;
547346429Skevans		argv += 3;
548346429Skevans	} else {
549346429Skevans		command = argv[1];
550346429Skevans		argc -= 1;
551346429Skevans		argv += 1;
552336668Skevans	}
553336668Skevans
554336668Skevans	/* Handle command aliases */
555336703Skevans	if (strcmp(command, "umount") == 0)
556336668Skevans		command = "unmount";
557336668Skevans
558336703Skevans	if (strcmp(command, "ujail") == 0)
559336668Skevans		command = "unjail";
560336668Skevans
561336703Skevans	if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
562336668Skevans		return (usage(true));
563336668Skevans
564352348Skevans	if ((cmd = get_cmd_info(command)) == NULL) {
565336668Skevans		fprintf(stderr, "unknown command: %s\n", command);
566336668Skevans		return (usage(false));
567336668Skevans	}
568336668Skevans
569346429Skevans	if ((be = libbe_init(root)) == NULL)
570336668Skevans		return (-1);
571336668Skevans
572352348Skevans	libbe_print_on_error(be, !cmd->silent);
573336668Skevans
574352348Skevans	rc = cmd->fn(argc, argv);
575336668Skevans
576336668Skevans	libbe_close(be);
577336668Skevans	return (rc);
578336668Skevans}
579