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