bectl.c revision 352348
1337414Skevans/*-
2337414Skevans * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3336668Skevans *
4336668Skevans * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
5336668Skevans * All rights reserved.
6336668Skevans *
7336668Skevans * Redistribution and use in source and binary forms, with or without
8336668Skevans * modification, are permitted provided that the following conditions
9336668Skevans * are met:
10336668Skevans * 1. Redistributions of source code must retain the above copyright
11336668Skevans *    notice, this list of conditions and the following disclaimer.
12336668Skevans * 2. Redistributions in binary form must reproduce the above copyright
13336668Skevans *    notice, this list of conditions and the following disclaimer in the
14336668Skevans *    documentation and/or other materials provided with the distribution.
15336668Skevans *
16336668Skevans * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17336668Skevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18336668Skevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19336668Skevans * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20336668Skevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21336668Skevans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22336668Skevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23336668Skevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24336668Skevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25336668Skevans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26336668Skevans * SUCH DAMAGE.
27336668Skevans */
28336668Skevans
29337416Skevans#include <sys/cdefs.h>
30337416Skevans__FBSDID("$FreeBSD: stable/11/sbin/bectl/bectl.c 352348 2019-09-15 02:46:40Z kevans $");
31337416Skevans
32336668Skevans#include <sys/param.h>
33336668Skevans#include <sys/mount.h>
34336668Skevans#include <errno.h>
35336747Skevans#include <libutil.h>
36336668Skevans#include <stdbool.h>
37336668Skevans#include <stdio.h>
38336668Skevans#include <stdint.h>
39336668Skevans#include <stdlib.h>
40336668Skevans#include <string.h>
41336668Skevans#include <sysexits.h>
42336747Skevans#include <time.h>
43336668Skevans#include <unistd.h>
44336668Skevans
45336668Skevans#include <be.h>
46336668Skevans
47337368Skevans#include "bectl.h"
48337368Skevans
49336694Skevansstatic int bectl_cmd_activate(int argc, char *argv[]);
50352348Skevansstatic int bectl_cmd_check(int argc, char *argv[]);
51336694Skevansstatic int bectl_cmd_create(int argc, char *argv[]);
52336694Skevansstatic int bectl_cmd_destroy(int argc, char *argv[]);
53336694Skevansstatic int bectl_cmd_export(int argc, char *argv[]);
54336694Skevansstatic int bectl_cmd_import(int argc, char *argv[]);
55337596Skevans#if SOON
56336694Skevansstatic int bectl_cmd_add(int argc, char *argv[]);
57337596Skevans#endif
58336694Skevansstatic int bectl_cmd_mount(int argc, char *argv[]);
59336694Skevansstatic int bectl_cmd_rename(int argc, char *argv[]);
60336694Skevansstatic int bectl_cmd_unmount(int argc, char *argv[]);
61336668Skevans
62337368Skevanslibbe_handle_t *be;
63336668Skevans
64337368Skevansint
65336668Skevansusage(bool explicit)
66336668Skevans{
67336703Skevans	FILE *fp;
68336668Skevans
69336703Skevans	fp =  explicit ? stdout : stderr;
70346429Skevans	fprintf(fp, "%s",
71346429Skevans	    "usage:\tbectl {-h | -? | subcommand [args...]}\n"
72346429Skevans#if SOON
73346429Skevans	    "\tbectl add (path)*\n"
74346429Skevans#endif
75336694Skevans	    "\tbectl activate [-t] beName\n"
76352348Skevans	    "\tbectl check\n"
77346429Skevans	    "\tbectl create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n"
78346429Skevans	    "\tbectl create [-r] beName@snapshot\n"
79346429Skevans	    "\tbectl destroy [-F] {beName | beName@snapshot}\n"
80336694Skevans	    "\tbectl export sourceBe\n"
81336694Skevans	    "\tbectl import targetBe\n"
82346429Skevans	    "\tbectl jail {-b | -U} [{-o key=value | -u key}]... "
83346429Skevans	    "{jailID | jailName}\n"
84346429Skevans	    "\t      bootenv [utility [argument ...]]\n"
85352088Skevans	    "\tbectl list [-DHas] [{-c property | -C property}]\n"
86336694Skevans	    "\tbectl mount beName [mountpoint]\n"
87336694Skevans	    "\tbectl rename origBeName newBeName\n"
88346429Skevans	    "\tbectl {ujail | unjail} {jailID | jailName} bootenv\n"
89346429Skevans	    "\tbectl {umount | unmount} [-f] beName\n");
90336668Skevans
91336668Skevans	return (explicit ? 0 : EX_USAGE);
92336668Skevans}
93336668Skevans
94336668Skevans
95336668Skevans/*
96336668Skevans * Represents a relationship between the command name and the parser action
97336668Skevans * that handles it.
98336668Skevans */
99336668Skevansstruct command_map_entry {
100336668Skevans	const char *command;
101336668Skevans	int (*fn)(int argc, char *argv[]);
102352348Skevans	/* True if libbe_print_on_error should be disabled */
103352348Skevans	bool silent;
104336668Skevans};
105336668Skevans
106336668Skevansstatic struct command_map_entry command_map[] =
107336668Skevans{
108352348Skevans	{ "activate", bectl_cmd_activate,false   },
109352348Skevans	{ "create",   bectl_cmd_create,  false   },
110352348Skevans	{ "destroy",  bectl_cmd_destroy, false   },
111352348Skevans	{ "export",   bectl_cmd_export,  false   },
112352348Skevans	{ "import",   bectl_cmd_import,  false   },
113337596Skevans#if SOON
114352348Skevans	{ "add",      bectl_cmd_add,     false   },
115337596Skevans#endif
116352348Skevans	{ "jail",     bectl_cmd_jail,    false   },
117352348Skevans	{ "list",     bectl_cmd_list,    false   },
118352348Skevans	{ "mount",    bectl_cmd_mount,   false   },
119352348Skevans	{ "rename",   bectl_cmd_rename,  false   },
120352348Skevans	{ "unjail",   bectl_cmd_unjail,  false   },
121352348Skevans	{ "unmount",  bectl_cmd_unmount, false   },
122352348Skevans	{ "check",    bectl_cmd_check,   true    },
123336668Skevans};
124336668Skevans
125352348Skevansstatic struct command_map_entry *
126352348Skevansget_cmd_info(const char *cmd)
127336668Skevans{
128352348Skevans	size_t i;
129336668Skevans
130352348Skevans	for (i = 0; i < nitems(command_map); ++i) {
131352348Skevans		if (strcmp(cmd, command_map[i].command) == 0)
132352348Skevans			return (&command_map[i]);
133336668Skevans	}
134336668Skevans
135352348Skevans	return (NULL);
136336668Skevans}
137336668Skevans
138336668Skevans
139336668Skevansstatic int
140336694Skevansbectl_cmd_activate(int argc, char *argv[])
141336668Skevans{
142336668Skevans	int err, opt;
143336668Skevans	bool temp;
144336668Skevans
145336668Skevans	temp = false;
146336668Skevans	while ((opt = getopt(argc, argv, "t")) != -1) {
147336668Skevans		switch (opt) {
148336668Skevans		case 't':
149336668Skevans			temp = true;
150336668Skevans			break;
151336668Skevans		default:
152336702Skevans			fprintf(stderr, "bectl activate: unknown option '-%c'\n",
153336668Skevans			    optopt);
154336668Skevans			return (usage(false));
155336668Skevans		}
156336668Skevans	}
157336668Skevans
158336668Skevans	argc -= optind;
159336668Skevans	argv += optind;
160336668Skevans
161336668Skevans	if (argc != 1) {
162336702Skevans		fprintf(stderr, "bectl activate: wrong number of arguments\n");
163336668Skevans		return (usage(false));
164336668Skevans	}
165336668Skevans
166336668Skevans
167336668Skevans	/* activate logic goes here */
168336703Skevans	if ((err = be_activate(be, argv[0], temp)) != 0)
169336703Skevans		/* XXX TODO: more specific error msg based on err */
170336668Skevans		printf("did not successfully activate boot environment %s\n",
171336668Skevans		    argv[0]);
172336703Skevans	else
173336668Skevans		printf("successfully activated boot environment %s\n", argv[0]);
174336668Skevans
175336703Skevans	if (temp)
176336668Skevans		printf("for next boot\n");
177336668Skevans
178336668Skevans	return (err);
179336668Skevans}
180336668Skevans
181336668Skevans
182336703Skevans/*
183336703Skevans * TODO: when only one arg is given, and it contains an "@" the this should
184336703Skevans * create that snapshot
185336703Skevans */
186336668Skevansstatic int
187336694Skevansbectl_cmd_create(int argc, char *argv[])
188336668Skevans{
189350344Skevans	char snapshot[BE_MAXPATHLEN];
190350344Skevans	char *atpos, *bootenv, *snapname;
191336668Skevans	int err, opt;
192346429Skevans	bool recursive;
193336668Skevans
194336668Skevans	snapname = NULL;
195346429Skevans	recursive = false;
196346429Skevans	while ((opt = getopt(argc, argv, "e:r")) != -1) {
197336668Skevans		switch (opt) {
198336668Skevans		case 'e':
199336668Skevans			snapname = optarg;
200336668Skevans			break;
201346429Skevans		case 'r':
202346429Skevans			recursive = true;
203346429Skevans			break;
204336668Skevans		default:
205336702Skevans			fprintf(stderr, "bectl create: unknown option '-%c'\n",
206336668Skevans			    optopt);
207336668Skevans			return (usage(false));
208336668Skevans		}
209336668Skevans	}
210336668Skevans
211336668Skevans	argc -= optind;
212336668Skevans	argv += optind;
213336668Skevans
214336668Skevans	if (argc != 1) {
215336702Skevans		fprintf(stderr, "bectl create: wrong number of arguments\n");
216336668Skevans		return (usage(false));
217336668Skevans	}
218336668Skevans
219336668Skevans	bootenv = *argv;
220350344Skevans
221350344Skevans	err = BE_ERR_SUCCESS;
222346429Skevans	if ((atpos = strchr(bootenv, '@')) != NULL) {
223346429Skevans		/*
224346429Skevans		 * This is the "create a snapshot variant". No new boot
225346429Skevans		 * environment is to be created here.
226346429Skevans		 */
227346429Skevans		*atpos++ = '\0';
228346429Skevans		err = be_snapshot(be, bootenv, atpos, recursive, NULL);
229350344Skevans	} else {
230350344Skevans		if (snapname == NULL)
231350344Skevans			/* Create from currently booted BE */
232350344Skevans			err = be_snapshot(be, be_active_path(be), NULL,
233350344Skevans			    recursive, snapshot);
234350344Skevans		else if (strchr(snapname, '@') != NULL)
235350344Skevans			/* Create from given snapshot */
236350344Skevans			strlcpy(snapshot, snapname, sizeof(snapshot));
237336703Skevans		else
238350344Skevans			/* Create from given BE */
239350344Skevans			err = be_snapshot(be, snapname, NULL, recursive,
240350344Skevans			    snapshot);
241350344Skevans
242350344Skevans		if (err == BE_ERR_SUCCESS)
243350344Skevans			err = be_create_depth(be, bootenv, snapshot,
244350344Skevans					      recursive == true ? -1 : 0);
245336668Skevans	}
246336668Skevans
247336668Skevans	switch (err) {
248336668Skevans	case BE_ERR_SUCCESS:
249336668Skevans		break;
250336668Skevans	default:
251346429Skevans		if (atpos != NULL)
252336668Skevans			fprintf(stderr,
253346429Skevans			    "failed to create a snapshot '%s' of '%s'\n",
254346429Skevans			    atpos, bootenv);
255346429Skevans		else if (snapname == NULL)
256346429Skevans			fprintf(stderr,
257336668Skevans			    "failed to create bootenv %s\n", bootenv);
258336703Skevans		else
259336668Skevans			fprintf(stderr,
260336668Skevans			    "failed to create bootenv %s from snapshot %s\n",
261336668Skevans			    bootenv, snapname);
262336668Skevans	}
263336668Skevans
264336668Skevans	return (err);
265336668Skevans}
266336668Skevans
267336668Skevans
268336668Skevansstatic int
269336694Skevansbectl_cmd_export(int argc, char *argv[])
270336668Skevans{
271336703Skevans	char *bootenv;
272336668Skevans
273336668Skevans	if (argc == 1) {
274336702Skevans		fprintf(stderr, "bectl export: missing boot environment name\n");
275336668Skevans		return (usage(false));
276336668Skevans	}
277336668Skevans
278336668Skevans	if (argc > 2) {
279336702Skevans		fprintf(stderr, "bectl export: extra arguments provided\n");
280336668Skevans		return (usage(false));
281336668Skevans	}
282336668Skevans
283336668Skevans	bootenv = argv[1];
284336668Skevans
285336668Skevans	if (isatty(STDOUT_FILENO)) {
286336702Skevans		fprintf(stderr, "bectl export: must redirect output\n");
287336668Skevans		return (EX_USAGE);
288336668Skevans	}
289336668Skevans
290336668Skevans	be_export(be, bootenv, STDOUT_FILENO);
291336668Skevans
292336668Skevans	return (0);
293336668Skevans}
294336668Skevans
295336668Skevans
296336668Skevansstatic int
297336694Skevansbectl_cmd_import(int argc, char *argv[])
298336668Skevans{
299336668Skevans	char *bootenv;
300336668Skevans	int err;
301336668Skevans
302336668Skevans	if (argc == 1) {
303336702Skevans		fprintf(stderr, "bectl import: missing boot environment name\n");
304336668Skevans		return (usage(false));
305336668Skevans	}
306336668Skevans
307336668Skevans	if (argc > 2) {
308336702Skevans		fprintf(stderr, "bectl import: extra arguments provided\n");
309336668Skevans		return (usage(false));
310336668Skevans	}
311336668Skevans
312336668Skevans	bootenv = argv[1];
313336668Skevans
314336668Skevans	if (isatty(STDIN_FILENO)) {
315336702Skevans		fprintf(stderr, "bectl import: input can not be from terminal\n");
316336668Skevans		return (EX_USAGE);
317336668Skevans	}
318336668Skevans
319336668Skevans	err = be_import(be, bootenv, STDIN_FILENO);
320336668Skevans
321336668Skevans	return (err);
322336668Skevans}
323336668Skevans
324337596Skevans#if SOON
325336668Skevansstatic int
326336694Skevansbectl_cmd_add(int argc, char *argv[])
327336668Skevans{
328336668Skevans
329336668Skevans	if (argc < 2) {
330336702Skevans		fprintf(stderr, "bectl add: must provide at least one path\n");
331336668Skevans		return (usage(false));
332336668Skevans	}
333336668Skevans
334336668Skevans	for (int i = 1; i < argc; ++i) {
335336668Skevans		printf("arg %d: %s\n", i, argv[i]);
336336703Skevans		/* XXX TODO catch err */
337336668Skevans		be_add_child(be, argv[i], true);
338336668Skevans	}
339336668Skevans
340336668Skevans	return (0);
341336668Skevans}
342337596Skevans#endif
343336668Skevans
344336668Skevansstatic int
345336694Skevansbectl_cmd_destroy(int argc, char *argv[])
346336668Skevans{
347346429Skevans	nvlist_t *props;
348346429Skevans	char *origin, *target, targetds[BE_MAXPATHLEN];
349346429Skevans	int err, flags, opt;
350336668Skevans
351346429Skevans	flags = 0;
352346429Skevans	while ((opt = getopt(argc, argv, "Fo")) != -1) {
353336668Skevans		switch (opt) {
354336668Skevans		case 'F':
355346429Skevans			flags |= BE_DESTROY_FORCE;
356336668Skevans			break;
357346429Skevans		case 'o':
358346429Skevans			flags |= BE_DESTROY_ORIGIN;
359346429Skevans			break;
360336668Skevans		default:
361336702Skevans			fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
362336668Skevans			    optopt);
363336668Skevans			return (usage(false));
364336668Skevans		}
365336668Skevans	}
366336668Skevans
367336668Skevans	argc -= optind;
368336668Skevans	argv += optind;
369336668Skevans
370336668Skevans	if (argc != 1) {
371336702Skevans		fprintf(stderr, "bectl destroy: wrong number of arguments\n");
372336668Skevans		return (usage(false));
373336668Skevans	}
374336668Skevans
375336668Skevans	target = argv[0];
376336668Skevans
377346429Skevans	/* We'll emit a notice if there's an origin to be cleaned up */
378346429Skevans	if ((flags & BE_DESTROY_ORIGIN) == 0 && strchr(target, '@') == NULL) {
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		}
387346429Skevans		if (nvlist_lookup_string(props, "origin", &origin) == 0)
388346429Skevans			fprintf(stderr, "bectl destroy: leaving origin '%s' intact\n",
389346429Skevans			    origin);
390346429Skevans		be_prop_list_free(props);
391346429Skevans	}
392336668Skevans
393346429Skevansdestroy:
394346429Skevans	err = be_destroy(be, target, flags);
395346429Skevans
396336668Skevans	return (err);
397336668Skevans}
398336668Skevans
399337358Skevansstatic int
400336694Skevansbectl_cmd_mount(int argc, char *argv[])
401336668Skevans{
402336703Skevans	char result_loc[BE_MAXPATHLEN];
403336703Skevans	char *bootenv, *mountpoint;
404346429Skevans	int err, mntflags;
405336668Skevans
406346429Skevans	/* XXX TODO: Allow shallow */
407346429Skevans	mntflags = BE_MNT_DEEP;
408336668Skevans	if (argc < 2) {
409336702Skevans		fprintf(stderr, "bectl mount: missing argument(s)\n");
410336668Skevans		return (usage(false));
411336668Skevans	}
412336668Skevans
413336668Skevans	if (argc > 3) {
414336702Skevans		fprintf(stderr, "bectl mount: too many arguments\n");
415336668Skevans		return (usage(false));
416336668Skevans	}
417336668Skevans
418336668Skevans	bootenv = argv[1];
419336668Skevans	mountpoint = ((argc == 3) ? argv[2] : NULL);
420336668Skevans
421346429Skevans	err = be_mount(be, bootenv, mountpoint, mntflags, result_loc);
422336668Skevans
423336668Skevans	switch (err) {
424336668Skevans	case BE_ERR_SUCCESS:
425336668Skevans		printf("successfully mounted %s at %s\n", bootenv, result_loc);
426336668Skevans		break;
427336668Skevans	default:
428336668Skevans		fprintf(stderr,
429336668Skevans		    (argc == 3) ? "failed to mount bootenv %s at %s\n" :
430336668Skevans		    "failed to mount bootenv %s at temporary path %s\n",
431336668Skevans		    bootenv, mountpoint);
432336668Skevans	}
433336668Skevans
434336668Skevans	return (err);
435336668Skevans}
436336668Skevans
437336668Skevans
438336668Skevansstatic int
439336694Skevansbectl_cmd_rename(int argc, char *argv[])
440336668Skevans{
441336703Skevans	char *dest, *src;
442336668Skevans	int err;
443336668Skevans
444336668Skevans	if (argc < 3) {
445336702Skevans		fprintf(stderr, "bectl rename: missing argument\n");
446336668Skevans		return (usage(false));
447336668Skevans	}
448336668Skevans
449336668Skevans	if (argc > 3) {
450336702Skevans		fprintf(stderr, "bectl rename: too many arguments\n");
451336668Skevans		return (usage(false));
452336668Skevans	}
453336668Skevans
454336668Skevans	src = argv[1];
455336668Skevans	dest = argv[2];
456336668Skevans
457336668Skevans	err = be_rename(be, src, dest);
458336668Skevans
459336668Skevans	switch (err) {
460336668Skevans	case BE_ERR_SUCCESS:
461336668Skevans		break;
462336668Skevans	default:
463336668Skevans		fprintf(stderr, "failed to rename bootenv %s to %s\n",
464336668Skevans		    src, dest);
465336668Skevans	}
466336668Skevans
467336668Skevans	return (0);
468336668Skevans}
469336668Skevans
470336730Skevansstatic int
471336694Skevansbectl_cmd_unmount(int argc, char *argv[])
472336668Skevans{
473336703Skevans	char *bootenv, *cmd;
474336668Skevans	int err, flags, opt;
475336668Skevans
476336668Skevans	/* Store alias used */
477336668Skevans	cmd = argv[0];
478336668Skevans
479336668Skevans	flags = 0;
480336668Skevans	while ((opt = getopt(argc, argv, "f")) != -1) {
481336668Skevans		switch (opt) {
482336668Skevans		case 'f':
483336668Skevans			flags |= BE_MNT_FORCE;
484336668Skevans			break;
485336668Skevans		default:
486336702Skevans			fprintf(stderr, "bectl %s: unknown option '-%c'\n",
487336668Skevans			    cmd, optopt);
488336668Skevans			return (usage(false));
489336668Skevans		}
490336668Skevans	}
491336668Skevans
492336668Skevans	argc -= optind;
493336668Skevans	argv += optind;
494336668Skevans
495336668Skevans	if (argc != 1) {
496336702Skevans		fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
497336668Skevans		return (usage(false));
498336668Skevans	}
499336668Skevans
500336668Skevans	bootenv = argv[0];
501336668Skevans
502336668Skevans	err = be_unmount(be, bootenv, flags);
503336668Skevans
504336668Skevans	switch (err) {
505336668Skevans	case BE_ERR_SUCCESS:
506336668Skevans		break;
507336668Skevans	default:
508336668Skevans		fprintf(stderr, "failed to unmount bootenv %s\n", bootenv);
509336668Skevans	}
510336668Skevans
511336668Skevans	return (err);
512336668Skevans}
513336668Skevans
514352348Skevansstatic int
515352348Skevansbectl_cmd_check(int argc, char *argv[] __unused)
516352348Skevans{
517336668Skevans
518352348Skevans	/* The command is left as argv[0] */
519352348Skevans	if (argc != 1) {
520352348Skevans		fprintf(stderr, "bectl check: wrong number of arguments\n");
521352348Skevans		return (usage(false));
522352348Skevans	}
523352348Skevans
524352348Skevans	return (0);
525352348Skevans}
526352348Skevans
527336668Skevansint
528336668Skevansmain(int argc, char *argv[])
529336668Skevans{
530352348Skevans	struct command_map_entry *cmd;
531336710Skevans	const char *command;
532346429Skevans	char *root;
533352348Skevans	int rc;
534336668Skevans
535352348Skevans	cmd = NULL;
536346429Skevans	root = NULL;
537346429Skevans	if (argc < 2)
538336668Skevans		return (usage(false));
539346429Skevans
540346429Skevans	if (strcmp(argv[1], "-r") == 0) {
541346429Skevans		if (argc < 4)
542346429Skevans			return (usage(false));
543346429Skevans		root = strdup(argv[2]);
544346429Skevans		command = argv[3];
545346429Skevans		argc -= 3;
546346429Skevans		argv += 3;
547346429Skevans	} else {
548346429Skevans		command = argv[1];
549346429Skevans		argc -= 1;
550346429Skevans		argv += 1;
551336668Skevans	}
552336668Skevans
553336668Skevans	/* Handle command aliases */
554336703Skevans	if (strcmp(command, "umount") == 0)
555336668Skevans		command = "unmount";
556336668Skevans
557336703Skevans	if (strcmp(command, "ujail") == 0)
558336668Skevans		command = "unjail";
559336668Skevans
560336703Skevans	if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
561336668Skevans		return (usage(true));
562336668Skevans
563352348Skevans	if ((cmd = get_cmd_info(command)) == NULL) {
564336668Skevans		fprintf(stderr, "unknown command: %s\n", command);
565336668Skevans		return (usage(false));
566336668Skevans	}
567336668Skevans
568346429Skevans	if ((be = libbe_init(root)) == NULL)
569336668Skevans		return (-1);
570336668Skevans
571352348Skevans	libbe_print_on_error(be, !cmd->silent);
572336668Skevans
573352348Skevans	rc = cmd->fn(argc, argv);
574336668Skevans
575336668Skevans	libbe_close(be);
576336668Skevans	return (rc);
577336668Skevans}
578