Index: head/sbin/bectl/bectl.c =================================================================== --- head/sbin/bectl/bectl.c (revision 349454) +++ head/sbin/bectl/bectl.c (revision 349455) @@ -1,561 +1,562 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Kyle J. Kneitinger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bectl.h" static int bectl_cmd_activate(int argc, char *argv[]); static int bectl_cmd_create(int argc, char *argv[]); static int bectl_cmd_destroy(int argc, char *argv[]); static int bectl_cmd_export(int argc, char *argv[]); static int bectl_cmd_import(int argc, char *argv[]); #if SOON static int bectl_cmd_add(int argc, char *argv[]); #endif static int bectl_cmd_mount(int argc, char *argv[]); static int bectl_cmd_rename(int argc, char *argv[]); static int bectl_cmd_unmount(int argc, char *argv[]); libbe_handle_t *be; int usage(bool explicit) { FILE *fp; fp = explicit ? stdout : stderr; fprintf(fp, "%s", "usage:\tbectl {-h | -? | subcommand [args...]}\n" #if SOON "\tbectl add (path)*\n" #endif "\tbectl activate [-t] beName\n" "\tbectl create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n" "\tbectl create [-r] beName@snapshot\n" "\tbectl destroy [-F] {beName | beName@snapshot}\n" "\tbectl export sourceBe\n" "\tbectl import targetBe\n" "\tbectl jail {-b | -U} [{-o key=value | -u key}]... " "{jailID | jailName}\n" "\t bootenv [utility [argument ...]]\n" "\tbectl list [-DHas]\n" "\tbectl mount beName [mountpoint]\n" "\tbectl rename origBeName newBeName\n" "\tbectl {ujail | unjail} {jailID | jailName} bootenv\n" "\tbectl {umount | unmount} [-f] beName\n"); return (explicit ? 0 : EX_USAGE); } /* * Represents a relationship between the command name and the parser action * that handles it. */ struct command_map_entry { const char *command; int (*fn)(int argc, char *argv[]); }; static struct command_map_entry command_map[] = { { "activate", bectl_cmd_activate }, { "create", bectl_cmd_create }, { "destroy", bectl_cmd_destroy }, { "export", bectl_cmd_export }, { "import", bectl_cmd_import }, #if SOON { "add", bectl_cmd_add }, #endif { "jail", bectl_cmd_jail }, { "list", bectl_cmd_list }, { "mount", bectl_cmd_mount }, { "rename", bectl_cmd_rename }, { "unjail", bectl_cmd_unjail }, { "unmount", bectl_cmd_unmount }, }; static int get_cmd_index(const char *cmd, int *idx) { int map_size; map_size = nitems(command_map); for (int i = 0; i < map_size; ++i) { if (strcmp(cmd, command_map[i].command) == 0) { *idx = i; return (0); } } return (1); } static int bectl_cmd_activate(int argc, char *argv[]) { int err, opt; bool temp; temp = false; while ((opt = getopt(argc, argv, "t")) != -1) { switch (opt) { case 't': temp = true; break; default: fprintf(stderr, "bectl activate: unknown option '-%c'\n", optopt); return (usage(false)); } } argc -= optind; argv += optind; if (argc != 1) { fprintf(stderr, "bectl activate: wrong number of arguments\n"); return (usage(false)); } /* activate logic goes here */ if ((err = be_activate(be, argv[0], temp)) != 0) /* XXX TODO: more specific error msg based on err */ printf("did not successfully activate boot environment %s\n", argv[0]); else printf("successfully activated boot environment %s\n", argv[0]); if (temp) printf("for next boot\n"); return (err); } /* * TODO: when only one arg is given, and it contains an "@" the this should * create that snapshot */ static int bectl_cmd_create(int argc, char *argv[]) { - char *atpos, *bootenv, *snapname, *source; + char snapshot[BE_MAXPATHLEN]; + char *atpos, *bootenv, *snapname; int err, opt; bool recursive; snapname = NULL; recursive = false; while ((opt = getopt(argc, argv, "e:r")) != -1) { switch (opt) { case 'e': snapname = optarg; break; case 'r': recursive = true; break; default: fprintf(stderr, "bectl create: unknown option '-%c'\n", optopt); return (usage(false)); } } argc -= optind; argv += optind; if (argc != 1) { fprintf(stderr, "bectl create: wrong number of arguments\n"); return (usage(false)); } bootenv = *argv; + + err = BE_ERR_SUCCESS; if ((atpos = strchr(bootenv, '@')) != NULL) { /* * This is the "create a snapshot variant". No new boot * environment is to be created here. */ *atpos++ = '\0'; err = be_snapshot(be, bootenv, atpos, recursive, NULL); - } else if (snapname != NULL) { - if (strchr(snapname, '@') != NULL) - err = be_create_from_existing_snap(be, bootenv, - snapname); - else - err = be_create_from_existing(be, bootenv, snapname); } else { - if ((snapname = strchr(bootenv, '@')) != NULL) { - *(snapname++) = '\0'; - if ((err = be_snapshot(be, be_active_path(be), - snapname, true, NULL)) != BE_ERR_SUCCESS) - fprintf(stderr, "failed to create snapshot\n"); - asprintf(&source, "%s@%s", be_active_path(be), snapname); - err = be_create_from_existing_snap(be, bootenv, - source); - return (err); - } else - err = be_create(be, bootenv); + if (snapname == NULL) + /* Create from currently booted BE */ + err = be_snapshot(be, be_active_path(be), NULL, + recursive, snapshot); + else if (strchr(snapname, '@') != NULL) + /* Create from given snapshot */ + strlcpy(snapshot, snapname, sizeof(snapshot)); + else + /* Create from given BE */ + err = be_snapshot(be, snapname, NULL, recursive, + snapshot); + + if (err == BE_ERR_SUCCESS) + err = be_create_depth(be, bootenv, snapshot, + recursive == true ? -1 : 0); } switch (err) { case BE_ERR_SUCCESS: break; default: if (atpos != NULL) fprintf(stderr, "failed to create a snapshot '%s' of '%s'\n", atpos, bootenv); else if (snapname == NULL) fprintf(stderr, "failed to create bootenv %s\n", bootenv); else fprintf(stderr, "failed to create bootenv %s from snapshot %s\n", bootenv, snapname); } return (err); } static int bectl_cmd_export(int argc, char *argv[]) { char *bootenv; if (argc == 1) { fprintf(stderr, "bectl export: missing boot environment name\n"); return (usage(false)); } if (argc > 2) { fprintf(stderr, "bectl export: extra arguments provided\n"); return (usage(false)); } bootenv = argv[1]; if (isatty(STDOUT_FILENO)) { fprintf(stderr, "bectl export: must redirect output\n"); return (EX_USAGE); } be_export(be, bootenv, STDOUT_FILENO); return (0); } static int bectl_cmd_import(int argc, char *argv[]) { char *bootenv; int err; if (argc == 1) { fprintf(stderr, "bectl import: missing boot environment name\n"); return (usage(false)); } if (argc > 2) { fprintf(stderr, "bectl import: extra arguments provided\n"); return (usage(false)); } bootenv = argv[1]; if (isatty(STDIN_FILENO)) { fprintf(stderr, "bectl import: input can not be from terminal\n"); return (EX_USAGE); } err = be_import(be, bootenv, STDIN_FILENO); return (err); } #if SOON static int bectl_cmd_add(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "bectl add: must provide at least one path\n"); return (usage(false)); } for (int i = 1; i < argc; ++i) { printf("arg %d: %s\n", i, argv[i]); /* XXX TODO catch err */ be_add_child(be, argv[i], true); } return (0); } #endif static int bectl_cmd_destroy(int argc, char *argv[]) { nvlist_t *props; char *origin, *target, targetds[BE_MAXPATHLEN]; int err, flags, opt; flags = 0; while ((opt = getopt(argc, argv, "Fo")) != -1) { switch (opt) { case 'F': flags |= BE_DESTROY_FORCE; break; case 'o': flags |= BE_DESTROY_ORIGIN; break; default: fprintf(stderr, "bectl destroy: unknown option '-%c'\n", optopt); return (usage(false)); } } argc -= optind; argv += optind; if (argc != 1) { fprintf(stderr, "bectl destroy: wrong number of arguments\n"); return (usage(false)); } target = argv[0]; /* We'll emit a notice if there's an origin to be cleaned up */ if ((flags & BE_DESTROY_ORIGIN) == 0 && strchr(target, '@') == NULL) { if (be_root_concat(be, target, targetds) != 0) goto destroy; if (be_prop_list_alloc(&props) != 0) goto destroy; if (be_get_dataset_props(be, targetds, props) != 0) { be_prop_list_free(props); goto destroy; } if (nvlist_lookup_string(props, "origin", &origin) == 0) fprintf(stderr, "bectl destroy: leaving origin '%s' intact\n", origin); be_prop_list_free(props); } destroy: err = be_destroy(be, target, flags); return (err); } static int bectl_cmd_mount(int argc, char *argv[]) { char result_loc[BE_MAXPATHLEN]; char *bootenv, *mountpoint; int err, mntflags; /* XXX TODO: Allow shallow */ mntflags = BE_MNT_DEEP; if (argc < 2) { fprintf(stderr, "bectl mount: missing argument(s)\n"); return (usage(false)); } if (argc > 3) { fprintf(stderr, "bectl mount: too many arguments\n"); return (usage(false)); } bootenv = argv[1]; mountpoint = ((argc == 3) ? argv[2] : NULL); err = be_mount(be, bootenv, mountpoint, mntflags, result_loc); switch (err) { case BE_ERR_SUCCESS: printf("successfully mounted %s at %s\n", bootenv, result_loc); break; default: fprintf(stderr, (argc == 3) ? "failed to mount bootenv %s at %s\n" : "failed to mount bootenv %s at temporary path %s\n", bootenv, mountpoint); } return (err); } static int bectl_cmd_rename(int argc, char *argv[]) { char *dest, *src; int err; if (argc < 3) { fprintf(stderr, "bectl rename: missing argument\n"); return (usage(false)); } if (argc > 3) { fprintf(stderr, "bectl rename: too many arguments\n"); return (usage(false)); } src = argv[1]; dest = argv[2]; err = be_rename(be, src, dest); switch (err) { case BE_ERR_SUCCESS: break; default: fprintf(stderr, "failed to rename bootenv %s to %s\n", src, dest); } return (0); } static int bectl_cmd_unmount(int argc, char *argv[]) { char *bootenv, *cmd; int err, flags, opt; /* Store alias used */ cmd = argv[0]; flags = 0; while ((opt = getopt(argc, argv, "f")) != -1) { switch (opt) { case 'f': flags |= BE_MNT_FORCE; break; default: fprintf(stderr, "bectl %s: unknown option '-%c'\n", cmd, optopt); return (usage(false)); } } argc -= optind; argv += optind; if (argc != 1) { fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd); return (usage(false)); } bootenv = argv[0]; err = be_unmount(be, bootenv, flags); switch (err) { case BE_ERR_SUCCESS: break; default: fprintf(stderr, "failed to unmount bootenv %s\n", bootenv); } return (err); } int main(int argc, char *argv[]) { const char *command; char *root; int command_index, rc; root = NULL; if (argc < 2) return (usage(false)); if (strcmp(argv[1], "-r") == 0) { if (argc < 4) return (usage(false)); root = strdup(argv[2]); command = argv[3]; argc -= 3; argv += 3; } else { command = argv[1]; argc -= 1; argv += 1; } /* Handle command aliases */ if (strcmp(command, "umount") == 0) command = "unmount"; if (strcmp(command, "ujail") == 0) command = "unjail"; if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0)) return (usage(true)); if (get_cmd_index(command, &command_index)) { fprintf(stderr, "unknown command: %s\n", command); return (usage(false)); } if ((be = libbe_init(root)) == NULL) return (-1); libbe_print_on_error(be, true); rc = command_map[command_index].fn(argc, argv); libbe_close(be); return (rc); } Index: head/sbin/bectl/tests/bectl_test.sh =================================================================== --- head/sbin/bectl/tests/bectl_test.sh (revision 349454) +++ head/sbin/bectl/tests/bectl_test.sh (revision 349455) @@ -1,376 +1,400 @@ # # SPDX-License-Identifier: BSD-2-Clause-FreeBSD # # Copyright (c) 2018 Kyle Evans # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ ZPOOL_NAME_FILE=zpool_name get_zpool_name() { cat $ZPOOL_NAME_FILE } make_zpool_name() { mktemp -u bectl_test_XXXXXX > $ZPOOL_NAME_FILE get_zpool_name } # Establishes a bectl_create zpool that can be used for some light testing; contains # a 'default' BE and not much else. bectl_create_setup() { zpool=$1 disk=$2 mnt=$3 # Sanity check to make sure `make_zpool_name` succeeded atf_check test -n "$zpool" kldload -n -q zfs || atf_skip "ZFS module not loaded on the current system" atf_check mkdir -p ${mnt} atf_check truncate -s 1G ${disk} atf_check zpool create -o altroot=${mnt} ${zpool} ${disk} atf_check zfs create -o mountpoint=none ${zpool}/ROOT atf_check zfs create -o mountpoint=/ -o canmount=noauto \ ${zpool}/ROOT/default } bectl_create_deep_setup() { zpool=$1 disk=$2 mnt=$3 # Sanity check to make sure `make_zpool_name` succeeded atf_check test -n "$zpool" bectl_create_setup ${zpool} ${disk} ${mnt} atf_check mkdir -p ${root} atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root} atf_check mkdir -p ${root}/usr atf_check zfs create -o mountpoint=/usr -o canmount=noauto \ ${zpool}/ROOT/default/usr atf_check -o ignore bectl -r ${zpool}/ROOT umount default } bectl_cleanup() { zpool=$1 if [ -z "$zpool" ]; then echo "Skipping cleanup; zpool not set up" elif zpool get health ${zpool} >/dev/null 2>&1; then zpool destroy -f ${zpool} fi } atf_test_case bectl_create cleanup bectl_create_head() { atf_set "descr" "Check the various forms of bectl create" atf_set "require.user" root } bectl_create_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt bectl_create_setup ${zpool} ${disk} ${mount} + + # Create a child dataset that will be used to test creation + # of recursive and non-recursive boot environments. + atf_check zfs create -o mountpoint=/usr -o canmount=noauto \ + ${zpool}/ROOT/default/usr + # Test standard creation, creation of a snapshot, and creation from a # snapshot. atf_check bectl -r ${zpool}/ROOT create -e default default2 atf_check bectl -r ${zpool}/ROOT create default2@test_snap atf_check bectl -r ${zpool}/ROOT create -e default2@test_snap default3 + + # Test standard creation, creation of a snapshot, and creation from a + # snapshot for recursive boot environments. + atf_check bectl -r ${zpool}/ROOT create -r -e default recursive + atf_check bectl -r ${zpool}/ROOT create -r recursive@test_snap + atf_check bectl -r ${zpool}/ROOT create -r -e recursive@test_snap recursive-snap + + # Test that non-recursive boot environments have no child datasets. + atf_check -e not-empty -s not-exit:0 \ + zfs list "${zpool}/ROOT/default2/usr" + atf_check -e not-empty -s not-exit:0 \ + zfs list "${zpool}/ROOT/default3/usr" + + # Test that recursive boot environments have child datasets. + atf_check -o not-empty \ + zfs list "${zpool}/ROOT/recursive/usr" + atf_check -o not-empty \ + zfs list "${zpool}/ROOT/recursive-snap/usr" } bectl_create_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_destroy cleanup bectl_destroy_head() { atf_set "descr" "Check bectl destroy" atf_set "require.user" root } bectl_destroy_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt root=${mount}/root bectl_create_setup ${zpool} ${disk} ${mount} atf_check bectl -r ${zpool}/ROOT create -e default default2 atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2 atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2 atf_check -e not-empty -s not-exit:0 zfs get mountpoint ${zpool}/ROOT/default2 # Test origin snapshot deletion when the snapshot to be destroyed # belongs to a mounted dataset, see PR 236043. atf_check mkdir -p ${root} atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root} atf_check bectl -r ${zpool}/ROOT create -e default default3 atf_check bectl -r ${zpool}/ROOT destroy -o default3 atf_check bectl -r ${zpool}/ROOT unmount default } bectl_destroy_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_export_import cleanup bectl_export_import_head() { atf_set "descr" "Check bectl export and import" atf_set "require.user" root } bectl_export_import_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt bectl_create_setup ${zpool} ${disk} ${mount} atf_check -o save:exported bectl -r ${zpool}/ROOT export default atf_check -x "bectl -r ${zpool}/ROOT import default2 < exported" atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2 atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2 atf_check -e not-empty -s not-exit:0 zfs get mountpoint \ ${zpool}/ROOT/default2 } bectl_export_import_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_list cleanup bectl_list_head() { atf_set "descr" "Check bectl list" atf_set "require.user" root } bectl_list_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt bectl_create_setup ${zpool} ${disk} ${mount} # Test the list functionality, including that BEs come and go away # as they're created and destroyed. Creation and destruction tests # use the 'zfs' utility to verify that they're actually created, so # these are just light tests that 'list' is picking them up. atf_check -o save:list.out bectl -r ${zpool}/ROOT list atf_check -o not-empty grep 'default' list.out atf_check bectl -r ${zpool}/ROOT create -e default default2 atf_check -o save:list.out bectl -r ${zpool}/ROOT list atf_check -o not-empty grep 'default2' list.out atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2 atf_check -o save:list.out bectl -r ${zpool}/ROOT list atf_check -s not-exit:0 grep 'default2' list.out # XXX TODO: Formatting checks } bectl_list_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_mount cleanup bectl_mount_head() { atf_set "descr" "Check bectl mount/unmount" atf_set "require.user" root } bectl_mount_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt root=${mount}/root bectl_create_deep_setup ${zpool} ${disk} ${mount} atf_check mkdir -p ${root} # Test unmount first... atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root} atf_check -o not-empty -x "mount | grep '^${zpool}/ROOT/default'" atf_check bectl -r ${zpool}/ROOT unmount default atf_check -s not-exit:0 -x "mount | grep '^${zpool}/ROOT/default'" # Then umount! atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root} atf_check -o not-empty -x "mount | grep '^${zpool}/ROOT/default'" atf_check bectl -r ${zpool}/ROOT umount default atf_check -s not-exit:0 -x "mount | grep '^${zpool}/ROOT/default'" } bectl_mount_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_rename cleanup bectl_rename_head() { atf_set "descr" "Check bectl rename" atf_set "require.user" root } bectl_rename_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt bectl_create_setup ${zpool} ${disk} ${mount} atf_check bectl -r ${zpool}/ROOT rename default default2 atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2 atf_check -e not-empty -s not-exit:0 zfs get mountpoint \ ${zpool}/ROOT/default } bectl_rename_cleanup() { bectl_cleanup $(get_zpool_name) } atf_test_case bectl_jail cleanup bectl_jail_head() { atf_set "descr" "Check bectl rename" atf_set "require.user" root } bectl_jail_body() { cwd=$(realpath .) zpool=$(make_zpool_name) disk=${cwd}/disk.img mount=${cwd}/mnt root=${mount}/root if [ ! -f /rescue/rescue ]; then atf_skip "This test requires a rescue binary" fi bectl_create_deep_setup ${zpool} ${disk} ${mount} # Prepare our minimal BE... plop a rescue binary into it atf_check mkdir -p ${root} atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root} atf_check mkdir -p ${root}/rescue atf_check cp /rescue/rescue ${root}/rescue/rescue atf_check bectl -r ${zpool}/ROOT umount default # Prepare some more boot environments atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default target atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default 1234 # Attempt to unjail a BE with numeric name; jail_getid at one point # did not validate that the input was a valid jid before returning the # jid. atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b 1234 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail 1234 # When a jail name is not explicit, it should match the jail id. atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o jid=233637 default atf_check -o inline:"233637\n" -s exit:0 -x "jls -j 233637 name" atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default # Basic command-mode tests, with and without jail cleanup atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \ jail default /rescue/rescue ls -1 atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \ jail -Uo path=${root} default /rescue/rescue ls -1 atf_check [ -f ${root}/rescue/rescue ] atf_check bectl -r ${zpool}/ROOT ujail default # Batch mode tests atf_check bectl -r ${zpool}/ROOT jail -bo path=${root} default atf_check -o not-empty -x "jls | grep -F \"${root}\"" atf_check bectl -r ${zpool}/ROOT ujail default atf_check -s not-exit:0 -x "jls | grep -F \"${root}\"" # 'unjail' naming atf_check bectl -r ${zpool}/ROOT jail -b default atf_check bectl -r ${zpool}/ROOT unjail default atf_check -s not-exit:0 -x "jls | grep -F \"${root}\"" # 'unjail' by BE name. Force bectl to lookup jail id by the BE name. atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b default atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o name=bectl_test target atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail target atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default # cannot unjail an unjailed BE (by either command name) atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT ujail default atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT unjail default # set+unset atf_check bectl -r ${zpool}/ROOT jail -b -o path=${root} -u path default # Ensure that it didn't mount at ${root} atf_check -s not-exit:0 -x "mount | grep -F '${root}'" atf_check bectl -r ${zpool}/ROOT ujail default } # If a test has failed, it's possible that the boot environment hasn't # been 'unjail'ed. We want to remove the jail before 'bectl_cleanup' # attempts to destroy the zpool. bectl_jail_cleanup() { zpool=$(get_zpool_name) for bootenv in "default" "target" "1234"; do # mountpoint of the boot environment mountpoint="$(bectl -r ${zpool}/ROOT list -H | grep ${bootenv} | awk '{print $3}')" # see if any jail paths match the boot environment mountpoint jailid="$(jls | grep ${mountpoint} | awk '{print $1}')" if [ -z "$jailid" ]; then continue; fi jail -r ${jailid} done; bectl_cleanup ${zpool} } atf_init_test_cases() { atf_add_test_case bectl_create atf_add_test_case bectl_destroy atf_add_test_case bectl_export_import atf_add_test_case bectl_list atf_add_test_case bectl_mount atf_add_test_case bectl_rename atf_add_test_case bectl_jail }