MFC bectl(8)/libbe(3): r337663-337664,337667,337697-337699,337800,337805,
337915-337918,337921,337924,337947,337993-337995,338221-338222,338303, 338417,339047,339972,339994,340334,340507-340508,340592-340594, 340635-340636,340722-340723,340974,342466,342849,342903,342911,343335, 343543,343977,343993-343994,344034,344067,344084,345302,345769, 345845-345846,345848,346082 There are simply too many small changes to enumerate; in summary: bectl(8)/libbe(3) has been introduced from current state in -CURRENT and added to the stable/11 rescue build. bectl(8) is a tool for managing ZFS boot environments, largely inspired by beadm. It includes features such as being able to jail a boot environment or easily mount it for modification. Relnotes: probably
This commit is contained in:
parent
60452bbf7a
commit
d1886e7aef
@ -2155,7 +2155,7 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} \
|
||||
${_cddl_lib_libumem} ${_cddl_lib_libnvpair} \
|
||||
${_cddl_lib_libuutil} \
|
||||
${_cddl_lib_libavl} \
|
||||
${_cddl_lib_libzfs_core} \
|
||||
${_cddl_lib_libzfs_core} ${_cddl_lib_libzfs} \
|
||||
${_cddl_lib_libctf} \
|
||||
lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \
|
||||
${_secure_lib_libcrypto} ${_lib_libldns} \
|
||||
@ -2224,7 +2224,15 @@ _cddl_lib_libavl= cddl/lib/libavl
|
||||
_cddl_lib_libuutil= cddl/lib/libuutil
|
||||
.if ${MK_ZFS} != "no"
|
||||
_cddl_lib_libzfs_core= cddl/lib/libzfs_core
|
||||
_cddl_lib_libzfs= cddl/lib/libzfs
|
||||
|
||||
cddl/lib/libzfs_core__L: cddl/lib/libnvpair__L
|
||||
|
||||
cddl/lib/libzfs__L: cddl/lib/libzfs_core__L lib/msun__L lib/libutil__L
|
||||
cddl/lib/libzfs__L: lib/libthr__L lib/libmd__L lib/libz__L cddl/lib/libumem__L
|
||||
cddl/lib/libzfs__L: cddl/lib/libuutil__L cddl/lib/libavl__L lib/libgeom__L
|
||||
|
||||
lib/libbe__L: cddl/lib/libzfs__L
|
||||
.endif
|
||||
_cddl_lib_libctf= cddl/lib/libctf
|
||||
_cddl_lib= cddl/lib
|
||||
|
@ -28,6 +28,7 @@ LINE("lib80211", "802.11 Wireless Network Management Library (lib80211, \\-l8021
|
||||
LINE("libarchive", "Streaming Archive Library (libarchive, \\-larchive)")
|
||||
LINE("libarm", "ARM Architecture Library (libarm, \\-larm)")
|
||||
LINE("libarm32", "ARM32 Architecture Library (libarm32, \\-larm32)")
|
||||
LINE("libbe", "Boot Environment Library (libbe, \\-lbe)")
|
||||
LINE("libbluetooth", "Bluetooth Library (libbluetooth, \\-lbluetooth)")
|
||||
LINE("libbsm", "Basic Security Module Library (libbsm, \\-lbsm)")
|
||||
LINE("libc", "Standard C\\~Library (libc, \\-lc)")
|
||||
|
@ -380,6 +380,8 @@
|
||||
..
|
||||
..
|
||||
sbin
|
||||
bectl
|
||||
..
|
||||
dhclient
|
||||
..
|
||||
devd
|
||||
|
@ -290,6 +290,7 @@ _libproc= libproc
|
||||
_librtld_db= librtld_db
|
||||
.endif
|
||||
SUBDIR.${MK_OFED}+= ofed
|
||||
SUBDIR.${MK_ZFS}+= libbe
|
||||
|
||||
SUBDIR.${MK_OPENMP}+= libomp
|
||||
|
||||
|
31
lib/libbe/Makefile
Normal file
31
lib/libbe/Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PACKAGE= lib${LIB}
|
||||
LIB= be
|
||||
SHLIBDIR?= /lib
|
||||
SHLIB_MAJOR= 1
|
||||
SHLIB_MINOR= 0
|
||||
|
||||
SRCS= be.c be_access.c be_error.c be_info.c
|
||||
INCS= be.h
|
||||
MAN= libbe.3
|
||||
|
||||
WARNS?= 2
|
||||
IGNORE_PRAGMA= yes
|
||||
|
||||
LIBADD+= zfs
|
||||
LIBADD+= nvpair
|
||||
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
|
||||
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
|
||||
|
||||
CFLAGS+= -DNEED_SOLARIS_BOOLEAN
|
||||
|
||||
.include <bsd.lib.mk>
|
1038
lib/libbe/be.c
Normal file
1038
lib/libbe/be.c
Normal file
File diff suppressed because it is too large
Load Diff
133
lib/libbe/be.h
Normal file
133
lib/libbe/be.h
Normal file
@ -0,0 +1,133 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _LIBBE_H
|
||||
#define _LIBBE_H
|
||||
|
||||
#include <libnvpair.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define BE_MAXPATHLEN 512
|
||||
|
||||
typedef struct libbe_handle libbe_handle_t;
|
||||
|
||||
typedef enum be_error {
|
||||
BE_ERR_SUCCESS = 0, /* No error */
|
||||
BE_ERR_INVALIDNAME, /* invalid boot env name */
|
||||
BE_ERR_EXISTS, /* boot env name already taken */
|
||||
BE_ERR_NOENT, /* boot env doesn't exist */
|
||||
BE_ERR_PERMS, /* insufficient permissions */
|
||||
BE_ERR_DESTROYACT, /* cannot destroy active boot env */
|
||||
BE_ERR_DESTROYMNT, /* destroying a mounted be requires force */
|
||||
BE_ERR_BADPATH, /* path not suitable for operation */
|
||||
BE_ERR_PATHBUSY, /* requested path is busy */
|
||||
BE_ERR_PATHLEN, /* provided name exceeds maximum length limit */
|
||||
BE_ERR_BADMOUNT, /* mountpoint is not '/' */
|
||||
BE_ERR_NOORIGIN, /* could not open snapshot's origin */
|
||||
BE_ERR_MOUNTED, /* boot environment is already mounted */
|
||||
BE_ERR_NOMOUNT, /* boot environment is not mounted */
|
||||
BE_ERR_ZFSOPEN, /* calling zfs_open() failed */
|
||||
BE_ERR_ZFSCLONE, /* error when calling zfs_clone to create be */
|
||||
BE_ERR_IO, /* error when doing some I/O operation */
|
||||
BE_ERR_NOPOOL, /* operation not supported on this pool */
|
||||
BE_ERR_NOMEM, /* insufficient memory */
|
||||
BE_ERR_UNKNOWN, /* unknown error */
|
||||
BE_ERR_INVORIGIN, /* invalid origin */
|
||||
} be_error_t;
|
||||
|
||||
|
||||
/* Library handling functions: be.c */
|
||||
libbe_handle_t *libbe_init(const char *root);
|
||||
void libbe_close(libbe_handle_t *);
|
||||
|
||||
/* Bootenv information functions: be_info.c */
|
||||
const char *be_active_name(libbe_handle_t *);
|
||||
const char *be_active_path(libbe_handle_t *);
|
||||
const char *be_nextboot_name(libbe_handle_t *);
|
||||
const char *be_nextboot_path(libbe_handle_t *);
|
||||
const char *be_root_path(libbe_handle_t *);
|
||||
|
||||
int be_get_bootenv_props(libbe_handle_t *, nvlist_t *);
|
||||
int be_get_dataset_props(libbe_handle_t *, const char *, nvlist_t *);
|
||||
int be_get_dataset_snapshots(libbe_handle_t *, const char *, nvlist_t *);
|
||||
int be_prop_list_alloc(nvlist_t **be_list);
|
||||
void be_prop_list_free(nvlist_t *be_list);
|
||||
|
||||
int be_activate(libbe_handle_t *, const char *, bool);
|
||||
|
||||
/* Bootenv creation functions */
|
||||
int be_create(libbe_handle_t *, const char *);
|
||||
int be_create_from_existing(libbe_handle_t *, const char *, const char *);
|
||||
int be_create_from_existing_snap(libbe_handle_t *, const char *, const char *);
|
||||
int be_snapshot(libbe_handle_t *, const char *, const char *, bool, char *);
|
||||
|
||||
/* Bootenv manipulation functions */
|
||||
int be_rename(libbe_handle_t *, const char *, const char *);
|
||||
|
||||
/* Bootenv removal functions */
|
||||
|
||||
typedef enum {
|
||||
BE_DESTROY_FORCE = 1 << 0,
|
||||
BE_DESTROY_ORIGIN = 1 << 1,
|
||||
} be_destroy_opt_t;
|
||||
|
||||
int be_destroy(libbe_handle_t *, const char *, int);
|
||||
|
||||
/* Bootenv mounting functions: be_access.c */
|
||||
|
||||
typedef enum {
|
||||
BE_MNT_FORCE = 1 << 0,
|
||||
BE_MNT_DEEP = 1 << 1,
|
||||
} be_mount_opt_t;
|
||||
|
||||
int be_mount(libbe_handle_t *, char *, char *, int, char *);
|
||||
int be_unmount(libbe_handle_t *, char *, int);
|
||||
int be_mounted_at(libbe_handle_t *, const char *path, nvlist_t *);
|
||||
|
||||
/* Error related functions: be_error.c */
|
||||
int libbe_errno(libbe_handle_t *);
|
||||
const char *libbe_error_description(libbe_handle_t *);
|
||||
void libbe_print_on_error(libbe_handle_t *, bool);
|
||||
|
||||
/* Utility Functions */
|
||||
int be_root_concat(libbe_handle_t *, const char *, char *);
|
||||
int be_validate_name(libbe_handle_t * __unused, const char *);
|
||||
int be_validate_snap(libbe_handle_t *, const char *);
|
||||
int be_exists(libbe_handle_t *, char *);
|
||||
|
||||
int be_export(libbe_handle_t *, const char *, int fd);
|
||||
int be_import(libbe_handle_t *, const char *, int fd);
|
||||
|
||||
#if SOON
|
||||
int be_add_child(libbe_handle_t *, const char *, bool);
|
||||
#endif
|
||||
void be_nicenum(uint64_t num, char *buf, size_t buflen);
|
||||
|
||||
#endif /* _LIBBE_H */
|
316
lib/libbe/be_access.c
Normal file
316
lib/libbe/be_access.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
|
||||
* Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
|
||||
* Copyright (c) 2019 Wes Maag <wes@jwmaag.org>
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "be.h"
|
||||
#include "be_impl.h"
|
||||
|
||||
struct be_mountcheck_info {
|
||||
const char *path;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct be_mount_info {
|
||||
libbe_handle_t *lbh;
|
||||
const char *be;
|
||||
const char *mountpoint;
|
||||
int mntflags;
|
||||
int deepmount;
|
||||
};
|
||||
|
||||
static int
|
||||
be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data)
|
||||
{
|
||||
struct be_mountcheck_info *info;
|
||||
char *mountpoint;
|
||||
|
||||
if (data == NULL)
|
||||
return (1);
|
||||
info = (struct be_mountcheck_info *)data;
|
||||
if (!zfs_is_mounted(zfs_hdl, &mountpoint))
|
||||
return (0);
|
||||
if (strcmp(mountpoint, info->path) == 0) {
|
||||
info->name = strdup(zfs_get_name(zfs_hdl));
|
||||
free(mountpoint);
|
||||
return (1);
|
||||
}
|
||||
free(mountpoint);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from be_mount, uses the given zfs_handle and attempts to
|
||||
* mount it at the passed mountpoint. If the deepmount flag is set, continue
|
||||
* calling the function for each child dataset.
|
||||
*/
|
||||
static int
|
||||
be_mount_iter(zfs_handle_t *zfs_hdl, void *data)
|
||||
{
|
||||
int err;
|
||||
char *mountpoint;
|
||||
char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN];
|
||||
struct be_mount_info *info;
|
||||
|
||||
info = (struct be_mount_info *)data;
|
||||
|
||||
if (zfs_is_mounted(zfs_hdl, &mountpoint)) {
|
||||
free(mountpoint);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF)
|
||||
return (0);
|
||||
|
||||
if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt, BE_MAXPATHLEN,
|
||||
NULL, NULL, 0, 1))
|
||||
return (1);
|
||||
|
||||
if (strcmp("none", zfs_mnt) != 0) {
|
||||
char opt = '\0';
|
||||
|
||||
mountpoint = be_mountpoint_augmented(info->lbh, zfs_mnt);
|
||||
|
||||
snprintf(tmp, BE_MAXPATHLEN, "%s%s", info->mountpoint,
|
||||
mountpoint);
|
||||
|
||||
if ((err = zmount(zfs_get_name(zfs_hdl), tmp, info->mntflags,
|
||||
__DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) {
|
||||
switch (errno) {
|
||||
case ENAMETOOLONG:
|
||||
return (set_error(info->lbh, BE_ERR_PATHLEN));
|
||||
case ELOOP:
|
||||
case ENOENT:
|
||||
case ENOTDIR:
|
||||
return (set_error(info->lbh, BE_ERR_BADPATH));
|
||||
case EPERM:
|
||||
return (set_error(info->lbh, BE_ERR_PERMS));
|
||||
case EBUSY:
|
||||
return (set_error(info->lbh, BE_ERR_PATHBUSY));
|
||||
default:
|
||||
return (set_error(info->lbh, BE_ERR_UNKNOWN));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!info->deepmount)
|
||||
return (0);
|
||||
|
||||
return (zfs_iter_filesystems(zfs_hdl, be_mount_iter, info));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
be_umount_iter(zfs_handle_t *zfs_hdl, void *data)
|
||||
{
|
||||
|
||||
int err;
|
||||
char *mountpoint;
|
||||
struct be_mount_info *info;
|
||||
|
||||
info = (struct be_mount_info *)data;
|
||||
|
||||
if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) {
|
||||
return (err);
|
||||
}
|
||||
|
||||
if (!zfs_is_mounted(zfs_hdl, &mountpoint)) {
|
||||
return (0);
|
||||
}
|
||||
free(mountpoint);
|
||||
|
||||
if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) {
|
||||
switch (errno) {
|
||||
case ENAMETOOLONG:
|
||||
return (set_error(info->lbh, BE_ERR_PATHLEN));
|
||||
case ELOOP:
|
||||
case ENOENT:
|
||||
case ENOTDIR:
|
||||
return (set_error(info->lbh, BE_ERR_BADPATH));
|
||||
case EPERM:
|
||||
return (set_error(info->lbh, BE_ERR_PERMS));
|
||||
case EBUSY:
|
||||
return (set_error(info->lbh, BE_ERR_PATHBUSY));
|
||||
default:
|
||||
return (set_error(info->lbh, BE_ERR_UNKNOWN));
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* usage
|
||||
*/
|
||||
int
|
||||
be_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details)
|
||||
{
|
||||
char be[BE_MAXPATHLEN];
|
||||
zfs_handle_t *root_hdl;
|
||||
struct be_mountcheck_info info;
|
||||
prop_data_t propinfo;
|
||||
|
||||
bzero(&be, BE_MAXPATHLEN);
|
||||
if ((root_hdl = zfs_open(lbh->lzh, lbh->root,
|
||||
ZFS_TYPE_FILESYSTEM)) == NULL)
|
||||
return (BE_ERR_ZFSOPEN);
|
||||
|
||||
info.path = path;
|
||||
info.name = NULL;
|
||||
zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info);
|
||||
zfs_close(root_hdl);
|
||||
|
||||
if (info.name != NULL) {
|
||||
if (details != NULL) {
|
||||
if ((root_hdl = zfs_open(lbh->lzh, lbh->root,
|
||||
ZFS_TYPE_FILESYSTEM)) == NULL) {
|
||||
free(info.name);
|
||||
return (BE_ERR_ZFSOPEN);
|
||||
}
|
||||
|
||||
propinfo.lbh = lbh;
|
||||
propinfo.list = details;
|
||||
propinfo.single_object = false;
|
||||
prop_list_builder_cb(root_hdl, &propinfo);
|
||||
zfs_close(root_hdl);
|
||||
}
|
||||
free(info.name);
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* usage
|
||||
*/
|
||||
int
|
||||
be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags,
|
||||
char *result_loc)
|
||||
{
|
||||
char be[BE_MAXPATHLEN];
|
||||
char mnt_temp[BE_MAXPATHLEN];
|
||||
int mntflags, mntdeep;
|
||||
int err;
|
||||
struct be_mount_info info;
|
||||
zfs_handle_t *zhdl;
|
||||
|
||||
if ((err = be_root_concat(lbh, bootenv, be)) != 0)
|
||||
return (set_error(lbh, err));
|
||||
|
||||
if ((err = be_exists(lbh, bootenv)) != 0)
|
||||
return (set_error(lbh, err));
|
||||
|
||||
if (is_mounted(lbh->lzh, be, NULL))
|
||||
return (set_error(lbh, BE_ERR_MOUNTED));
|
||||
|
||||
mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0;
|
||||
mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0;
|
||||
|
||||
/* Create mountpoint if it is not specified */
|
||||
if (mountpoint == NULL) {
|
||||
strlcpy(mnt_temp, "/tmp/be_mount.XXXX", sizeof(mnt_temp));
|
||||
if (mkdtemp(mnt_temp) == NULL)
|
||||
return (set_error(lbh, BE_ERR_IO));
|
||||
}
|
||||
|
||||
if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
|
||||
return (set_error(lbh, BE_ERR_ZFSOPEN));
|
||||
|
||||
info.lbh = lbh;
|
||||
info.be = be;
|
||||
info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint;
|
||||
info.mntflags = mntflags;
|
||||
info.deepmount = mntdeep;
|
||||
|
||||
if((err = be_mount_iter(zhdl, &info) != 0)) {
|
||||
zfs_close(zhdl);
|
||||
return (err);
|
||||
}
|
||||
zfs_close(zhdl);
|
||||
|
||||
if (result_loc != NULL)
|
||||
strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint,
|
||||
BE_MAXPATHLEN);
|
||||
|
||||
return (BE_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* usage
|
||||
*/
|
||||
int
|
||||
be_unmount(libbe_handle_t *lbh, char *bootenv, int flags)
|
||||
{
|
||||
int err;
|
||||
char be[BE_MAXPATHLEN];
|
||||
zfs_handle_t *root_hdl;
|
||||
struct be_mount_info info;
|
||||
|
||||
if ((err = be_root_concat(lbh, bootenv, be)) != 0)
|
||||
return (set_error(lbh, err));
|
||||
|
||||
if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
|
||||
return (set_error(lbh, BE_ERR_ZFSOPEN));
|
||||
|
||||
info.lbh = lbh;
|
||||
info.be = be;
|
||||
info.mountpoint = NULL;
|
||||
info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0;
|
||||
|
||||
if ((err = be_umount_iter(root_hdl, &info)) != 0) {
|
||||
zfs_close(root_hdl);
|
||||
return (err);
|
||||
}
|
||||
|
||||
zfs_close(root_hdl);
|
||||
return (BE_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will blow away the input buffer as needed if we're discovered
|
||||
* to be looking at a root-mount. If the mountpoint is naturally beyond the
|
||||
* root, however, the buffer may be left intact and a pointer to the section
|
||||
* past altroot will be returned instead for the caller's perusal.
|
||||
*/
|
||||
char *
|
||||
be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint)
|
||||
{
|
||||
|
||||
if (lbh->altroot_len == 0)
|
||||
return (mountpoint);
|
||||
if (mountpoint == NULL || *mountpoint == '\0')
|
||||
return (mountpoint);
|
||||
|
||||
if (mountpoint[lbh->altroot_len] == '\0') {
|
||||
*(mountpoint + 1) = '\0';
|
||||
return (mountpoint);
|
||||
} else
|
||||
return (mountpoint + lbh->altroot_len);
|
||||
}
|
136
lib/libbe/be_error.c
Normal file
136
lib/libbe/be_error.c
Normal file
@ -0,0 +1,136 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "be.h"
|
||||
#include "be_impl.h"
|
||||
|
||||
/*
|
||||
* Usage
|
||||
*/
|
||||
int
|
||||
libbe_errno(libbe_handle_t *lbh)
|
||||
{
|
||||
|
||||
return (lbh->error);
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
libbe_error_description(libbe_handle_t *lbh)
|
||||
{
|
||||
|
||||
switch (lbh->error) {
|
||||
case BE_ERR_INVALIDNAME:
|
||||
return ("invalid boot environment name");
|
||||
|
||||
case BE_ERR_EXISTS:
|
||||
return ("boot environment name already taken");
|
||||
|
||||
case BE_ERR_NOENT:
|
||||
return ("specified boot environment does not exist");
|
||||
|
||||
case BE_ERR_PERMS:
|
||||
return ("insufficient permissions");
|
||||
|
||||
case BE_ERR_DESTROYACT:
|
||||
return ("cannot destroy active boot environment");
|
||||
|
||||
case BE_ERR_DESTROYMNT:
|
||||
return ("cannot destroy mounted boot env unless forced");
|
||||
|
||||
case BE_ERR_BADPATH:
|
||||
return ("path not suitable for operation");
|
||||
|
||||
case BE_ERR_PATHBUSY:
|
||||
return ("specified path is busy");
|
||||
|
||||
case BE_ERR_PATHLEN:
|
||||
return ("provided path name exceeds maximum length limit");
|
||||
|
||||
case BE_ERR_BADMOUNT:
|
||||
return ("mountpoint is not \"/\"");
|
||||
|
||||
case BE_ERR_NOORIGIN:
|
||||
return ("could not open snapshot's origin");
|
||||
|
||||
case BE_ERR_MOUNTED:
|
||||
return ("boot environment is already mounted");
|
||||
|
||||
case BE_ERR_NOMOUNT:
|
||||
return ("boot environment is not mounted");
|
||||
|
||||
case BE_ERR_ZFSOPEN:
|
||||
return ("calling zfs_open() failed");
|
||||
|
||||
case BE_ERR_ZFSCLONE:
|
||||
return ("error when calling zfs_clone() to create boot env");
|
||||
|
||||
case BE_ERR_IO:
|
||||
return ("input/output error");
|
||||
|
||||
case BE_ERR_NOPOOL:
|
||||
return ("operation not supported on this pool");
|
||||
|
||||
case BE_ERR_NOMEM:
|
||||
return ("insufficient memory");
|
||||
|
||||
case BE_ERR_UNKNOWN:
|
||||
return ("unknown error");
|
||||
|
||||
case BE_ERR_INVORIGIN:
|
||||
return ("invalid origin");
|
||||
|
||||
default:
|
||||
assert(lbh->error == BE_ERR_SUCCESS);
|
||||
return ("no error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
libbe_print_on_error(libbe_handle_t *lbh, bool val)
|
||||
{
|
||||
|
||||
lbh->print_on_err = val;
|
||||
libzfs_print_on_error(lbh->lzh, val);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
set_error(libbe_handle_t *lbh, be_error_t err)
|
||||
{
|
||||
|
||||
lbh->error = err;
|
||||
if (lbh->print_on_err && (err != BE_ERR_SUCCESS))
|
||||
fprintf(stderr, "%s\n", libbe_error_description(lbh));
|
||||
|
||||
return (err);
|
||||
}
|
76
lib/libbe/be_impl.h
Normal file
76
lib/libbe/be_impl.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _LIBBE_IMPL_H
|
||||
#define _LIBBE_IMPL_H
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "be.h"
|
||||
|
||||
struct libbe_handle {
|
||||
char root[BE_MAXPATHLEN];
|
||||
char rootfs[BE_MAXPATHLEN];
|
||||
char bootfs[BE_MAXPATHLEN];
|
||||
size_t altroot_len;
|
||||
zpool_handle_t *active_phandle;
|
||||
libzfs_handle_t *lzh;
|
||||
be_error_t error;
|
||||
bool print_on_err;
|
||||
};
|
||||
|
||||
struct libbe_deep_clone {
|
||||
libbe_handle_t *lbh;
|
||||
const char *bename;
|
||||
const char *snapname;
|
||||
const char *be_root;
|
||||
};
|
||||
|
||||
struct libbe_dccb {
|
||||
libbe_handle_t *lbh;
|
||||
zfs_handle_t *zhp;
|
||||
nvlist_t *props;
|
||||
};
|
||||
|
||||
typedef struct prop_data {
|
||||
nvlist_t *list;
|
||||
libbe_handle_t *lbh;
|
||||
bool single_object; /* list will contain props directly */
|
||||
} prop_data_t;
|
||||
|
||||
int prop_list_builder_cb(zfs_handle_t *, void *);
|
||||
int be_proplist_update(prop_data_t *);
|
||||
|
||||
char *be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint);
|
||||
|
||||
/* Clobbers any previous errors */
|
||||
int set_error(libbe_handle_t *, be_error_t);
|
||||
|
||||
#endif /* _LIBBE_IMPL_H */
|
305
lib/libbe/be_info.c
Normal file
305
lib/libbe/be_info.c
Normal file
@ -0,0 +1,305 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
|
||||
* Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "be.h"
|
||||
#include "be_impl.h"
|
||||
|
||||
static int snapshot_proplist_update(zfs_handle_t *hdl, prop_data_t *data);
|
||||
|
||||
/*
|
||||
* Returns the name of the active boot environment
|
||||
*/
|
||||
const char *
|
||||
be_active_name(libbe_handle_t *lbh)
|
||||
{
|
||||
|
||||
if (*lbh->rootfs != '\0')
|
||||
return (strrchr(lbh->rootfs, '/') + sizeof(char));
|
||||
else
|
||||
return (lbh->rootfs);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns full path of the active boot environment
|
||||
*/
|
||||
const char *
|
||||
be_active_path(libbe_handle_t *lbh)
|
||||
{
|
||||
|
||||
return (lbh->rootfs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the name of the next active boot environment
|
||||
*/
|
||||
const char *
|
||||
be_nextboot_name(libbe_handle_t *lbh)
|
||||
{
|
||||
|
||||
if (*lbh->bootfs != '\0')
|
||||
return (strrchr(lbh->bootfs, '/') + sizeof(char));
|
||||
else
|
||||
return (lbh->bootfs);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns full path of the active boot environment
|
||||
*/
|
||||
const char *
|
||||
be_nextboot_path(libbe_handle_t *lbh)
|
||||
{
|
||||
|
||||
return (lbh->bootfs);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns the path of the boot environment root dataset
|
||||
*/
|
||||
const char *
|
||||
be_root_path(libbe_handle_t *lbh)
|
||||
{
|
||||
|
||||
return (lbh->root);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Populates dsnvl with one nvlist per bootenv dataset describing the properties
|
||||
* of that dataset that we've declared ourselves to care about.
|
||||
*/
|
||||
int
|
||||
be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl)
|
||||
{
|
||||
prop_data_t data;
|
||||
|
||||
data.lbh = lbh;
|
||||
data.list = dsnvl;
|
||||
data.single_object = false;
|
||||
return (be_proplist_update(&data));
|
||||
}
|
||||
|
||||
int
|
||||
be_get_dataset_props(libbe_handle_t *lbh, const char *name, nvlist_t *props)
|
||||
{
|
||||
zfs_handle_t *snap_hdl;
|
||||
prop_data_t data;
|
||||
int ret;
|
||||
|
||||
data.lbh = lbh;
|
||||
data.list = props;
|
||||
data.single_object = true;
|
||||
if ((snap_hdl = zfs_open(lbh->lzh, name,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL)
|
||||
return (BE_ERR_ZFSOPEN);
|
||||
|
||||
ret = prop_list_builder_cb(snap_hdl, &data);
|
||||
zfs_close(snap_hdl);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
be_get_dataset_snapshots(libbe_handle_t *lbh, const char *name, nvlist_t *props)
|
||||
{
|
||||
zfs_handle_t *ds_hdl;
|
||||
prop_data_t data;
|
||||
int ret;
|
||||
|
||||
data.lbh = lbh;
|
||||
data.list = props;
|
||||
data.single_object = false;
|
||||
if ((ds_hdl = zfs_open(lbh->lzh, name,
|
||||
ZFS_TYPE_FILESYSTEM)) == NULL)
|
||||
return (BE_ERR_ZFSOPEN);
|
||||
|
||||
ret = snapshot_proplist_update(ds_hdl, &data);
|
||||
zfs_close(ds_hdl);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal callback function used by zfs_iter_filesystems. For each dataset in
|
||||
* the bootenv root, populate an nvlist_t of its relevant properties.
|
||||
*/
|
||||
int
|
||||
prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p)
|
||||
{
|
||||
char buf[512], *mountpoint;
|
||||
prop_data_t *data;
|
||||
libbe_handle_t *lbh;
|
||||
nvlist_t *props;
|
||||
const char *dataset, *name;
|
||||
boolean_t mounted;
|
||||
|
||||
/*
|
||||
* XXX TODO:
|
||||
* some system for defining constants for the nvlist keys
|
||||
* error checking
|
||||
*/
|
||||
data = (prop_data_t *)data_p;
|
||||
lbh = data->lbh;
|
||||
|
||||
if (data->single_object)
|
||||
props = data->list;
|
||||
else
|
||||
nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
|
||||
|
||||
dataset = zfs_get_name(zfs_hdl);
|
||||
nvlist_add_string(props, "dataset", dataset);
|
||||
|
||||
name = strrchr(dataset, '/') + 1;
|
||||
nvlist_add_string(props, "name", name);
|
||||
|
||||
mounted = zfs_is_mounted(zfs_hdl, &mountpoint);
|
||||
|
||||
if (mounted)
|
||||
nvlist_add_string(props, "mounted", mountpoint);
|
||||
|
||||
if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, 512,
|
||||
NULL, NULL, 0, 1) == 0)
|
||||
nvlist_add_string(props, "mountpoint", buf);
|
||||
|
||||
if (zfs_prop_get(zfs_hdl, ZFS_PROP_ORIGIN, buf, 512,
|
||||
NULL, NULL, 0, 1) == 0)
|
||||
nvlist_add_string(props, "origin", buf);
|
||||
|
||||
if (zfs_prop_get(zfs_hdl, ZFS_PROP_CREATION, buf, 512,
|
||||
NULL, NULL, 0, 1) == 0)
|
||||
nvlist_add_string(props, "creation", buf);
|
||||
|
||||
nvlist_add_boolean_value(props, "active",
|
||||
(strcmp(be_active_path(lbh), dataset) == 0));
|
||||
|
||||
if (zfs_prop_get(zfs_hdl, ZFS_PROP_USED, buf, 512,
|
||||
NULL, NULL, 0, 1) == 0)
|
||||
nvlist_add_string(props, "used", buf);
|
||||
|
||||
if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDDS, buf, 512,
|
||||
NULL, NULL, 0, 1) == 0)
|
||||
nvlist_add_string(props, "usedds", buf);
|
||||
|
||||
if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDSNAP, buf, 512,
|
||||
NULL, NULL, 0, 1) == 0)
|
||||
nvlist_add_string(props, "usedsnap", buf);
|
||||
|
||||
if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDREFRESERV, buf, 512,
|
||||
NULL, NULL, 0, 1) == 0)
|
||||
nvlist_add_string(props, "usedrefreserv", buf);
|
||||
|
||||
if (zfs_prop_get(zfs_hdl, ZFS_PROP_REFERENCED, buf, 512,
|
||||
NULL, NULL, 0, 1) == 0)
|
||||
nvlist_add_string(props, "referenced", buf);
|
||||
|
||||
nvlist_add_boolean_value(props, "nextboot",
|
||||
(strcmp(be_nextboot_path(lbh), dataset) == 0));
|
||||
|
||||
if (!data->single_object)
|
||||
nvlist_add_nvlist(data->list, name, props);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Updates the properties of each bootenv in the libbe handle
|
||||
* XXX TODO: ensure that this is always consistent (run after adds, deletes,
|
||||
* renames,etc
|
||||
*/
|
||||
int
|
||||
be_proplist_update(prop_data_t *data)
|
||||
{
|
||||
zfs_handle_t *root_hdl;
|
||||
|
||||
if ((root_hdl = zfs_open(data->lbh->lzh, data->lbh->root,
|
||||
ZFS_TYPE_FILESYSTEM)) == NULL)
|
||||
return (BE_ERR_ZFSOPEN);
|
||||
|
||||
/* XXX TODO: some error checking here */
|
||||
zfs_iter_filesystems(root_hdl, prop_list_builder_cb, data);
|
||||
|
||||
zfs_close(root_hdl);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
snapshot_proplist_update(zfs_handle_t *hdl, prop_data_t *data)
|
||||
{
|
||||
|
||||
return (zfs_iter_snapshots_sorted(hdl, prop_list_builder_cb, data));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
be_prop_list_alloc(nvlist_t **be_list)
|
||||
{
|
||||
|
||||
return (nvlist_alloc(be_list, NV_UNIQUE_NAME, KM_SLEEP));
|
||||
}
|
||||
|
||||
/*
|
||||
* frees property list and its children
|
||||
*/
|
||||
void
|
||||
be_prop_list_free(nvlist_t *be_list)
|
||||
{
|
||||
nvlist_t *prop_list;
|
||||
nvpair_t *be_pair;
|
||||
|
||||
be_pair = nvlist_next_nvpair(be_list, NULL);
|
||||
if (nvpair_value_nvlist(be_pair, &prop_list) == 0)
|
||||
nvlist_free(prop_list);
|
||||
|
||||
while ((be_pair = nvlist_next_nvpair(be_list, be_pair)) != NULL) {
|
||||
if (nvpair_value_nvlist(be_pair, &prop_list) == 0)
|
||||
nvlist_free(prop_list);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Usage
|
||||
*/
|
||||
int
|
||||
be_exists(libbe_handle_t *lbh, char *be)
|
||||
{
|
||||
char buf[BE_MAXPATHLEN];
|
||||
|
||||
be_root_concat(lbh, be, buf);
|
||||
|
||||
if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_DATASET))
|
||||
return (BE_ERR_NOENT);
|
||||
|
||||
return (BE_ERR_SUCCESS);
|
||||
}
|
504
lib/libbe/libbe.3
Normal file
504
lib/libbe/libbe.3
Normal file
@ -0,0 +1,504 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
.\"
|
||||
.\" Copyright (c) 2017 Kyle Kneitinger
|
||||
.\" All rights reserved.
|
||||
.\" Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
|
||||
.\"
|
||||
.\" 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$
|
||||
.\"
|
||||
.Dd February 12, 2019
|
||||
.Dt LIBBE 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm libbe
|
||||
.Nd library for creating, destroying and modifying ZFS boot environments
|
||||
.Sh LIBRARY
|
||||
.Lb libbe
|
||||
.Sh SYNOPSIS
|
||||
.In be.h
|
||||
.Ft "libbe_handle_t *hdl" Ns
|
||||
.Fn libbe_init "const char *be_root"
|
||||
.Pp
|
||||
.Ft void
|
||||
.Fn libbe_close "libbe_handle_t *hdl"
|
||||
.Pp
|
||||
.Ft const char * Ns
|
||||
.Fn be_active_name "libbe_handle_t *hdl"
|
||||
.Pp
|
||||
.Ft const char * Ns
|
||||
.Fn be_active_path "libbe_handle_t *hdl"
|
||||
.Pp
|
||||
.Ft const char * Ns
|
||||
.Fn be_nextboot_name "libbe_handle_t *hdl"
|
||||
.Pp
|
||||
.Ft const char * Ns
|
||||
.Fn be_nextboot_path "libbe_handle_t *hdl"
|
||||
.Pp
|
||||
.Ft const char * Ns
|
||||
.Fn be_root_path "libbe_handle_t *hdl"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_create "libbe_handle_t *hdl" "const char *be_name"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_create_from_existing "libbe_handle_t *hdl" "const char *be_name" "const char *be_origin"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_create_from_existing_snap "libbe_handle_t *hdl" "const char *be_name" "const char *snap"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_rename "libbe_handle_t *hdl" "const char *be_old" "const char *be_new"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_activate "libbe_handle_t *hdl" "const char *be_name" "bool temporary"
|
||||
.Ft int
|
||||
.Fn be_destroy "libbe_handle_t *hdl" "const char *be_name" "int options"
|
||||
.Pp
|
||||
.Ft void
|
||||
.Fn be_nicenum "uint64_t num" "char *buf" "size_t bufsz"
|
||||
.Pp
|
||||
.\" TODO: Write up of mount options
|
||||
.\" typedef enum {
|
||||
.\" BE_MNT_FORCE = 1 << 0,
|
||||
.\" BE_MNT_DEEP = 1 << 1,
|
||||
.\" } be_mount_opt_t
|
||||
.Ft int
|
||||
.Fn be_mount "libbe_handle_t *hdl" "char *be_name" "char *mntpoint" "int flags" "char *result"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_mounted_at "libbe_handle_t *hdl" "const char *path" "nvlist_t *details"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_unmount "libbe_handle_t *hdl" "char *be_name" "int flags"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn libbe_errno "libbe_handle_t *hdl"
|
||||
.Pp
|
||||
.Ft const char * Ns
|
||||
.Fn libbe_error_description "libbe_handle_t *hdl"
|
||||
.Pp
|
||||
.Ft void
|
||||
.Fn libbe_print_on_error "libbe_handle_t *hdl" "bool doprint"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_root_concat "libbe_handle_t *hdl" "const char *be_name" "char *result"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_validate_name "libbe_handle_t *hdl" "const char *be_name"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_validate_snap "libbe_handle_t *hdl" "const char *snap"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_exists "libbe_handle_t *hdl" "char *be_name"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_export "libbe_handle_t *hdl" "const char *be_name" "int fd"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_import "libbe_handle_t *hdl" "const char *be_name" "int fd"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_prop_list_alloc "nvlist_t **prop_list"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_get_bootenv_props "libbe_handle_t *hdl" "nvlist_t *be_list"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_get_dataset_props "libbe_handle_t *hdl" "const char *ds_name" "nvlist_t *props"
|
||||
.Pp
|
||||
.Ft int
|
||||
.Fn be_get_dataset_snapshots "libbe_handle_t *hdl" "const char *ds_name" "nvlist_t *snap_list"
|
||||
.Pp
|
||||
.Ft void
|
||||
.Fn be_prop_list_free "nvlist_t *prop_list"
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
interfaces with libzfs to provide a set of functions for various operations
|
||||
regarding ZFS boot environments including "deep" boot environments in which
|
||||
a boot environments has child datasets.
|
||||
.Pp
|
||||
A context structure is passed to each function, allowing for a small amount
|
||||
of state to be retained, such as errors from previous operations.
|
||||
.Nm
|
||||
may be configured to print the corresponding error message to
|
||||
.Dv stderr
|
||||
when an error is encountered with
|
||||
.Fn libbe_print_on_error .
|
||||
.Pp
|
||||
All functions returning an
|
||||
.Vt int
|
||||
return 0 on success, or a
|
||||
.Nm
|
||||
errno otherwise as described in
|
||||
.Sx DIAGNOSTICS .
|
||||
.Pp
|
||||
The
|
||||
.Fn libbe_init
|
||||
function takes an optional BE root and initializes
|
||||
.Nm ,
|
||||
returning a
|
||||
.Vt "libbe_handle_t *"
|
||||
on success, or
|
||||
.Dv NULL
|
||||
on error.
|
||||
If a BE root is supplied,
|
||||
.Nm
|
||||
will only operate out of that pool and BE root.
|
||||
An error may occur if:
|
||||
.Bl -column
|
||||
.It /boot and / are not on the same filesystem and device,
|
||||
.It libzfs fails to initialize,
|
||||
.It The system has not been properly booted with a ZFS boot
|
||||
environment,
|
||||
.It Nm
|
||||
fails to open the zpool the active boot environment resides on, or
|
||||
.It Nm
|
||||
fails to locate the boot environment that is currently mounted.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fn libbe_close
|
||||
function frees all resources previously acquired in
|
||||
.Fn libbe_init ,
|
||||
invalidating the handle in the process.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_active_name
|
||||
function returns the name of the currently booted boot environment.
|
||||
This boot environment may not belong to the same BE root as the root libbe
|
||||
is operating on!
|
||||
.Pp
|
||||
The
|
||||
.Fn be_active_path
|
||||
function returns the full path of the currently booted boot environment.
|
||||
This boot environment may not belong to the same BE root as the root libbe
|
||||
is operating on!
|
||||
.Pp
|
||||
The
|
||||
.Fn be_nextboot_name
|
||||
function returns the name of the boot environment that will be active on reboot.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_nextboot_path
|
||||
function returns the full path of the boot environment that will be
|
||||
active on reboot.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_root_path
|
||||
function returns the boot environment root path.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_create
|
||||
function creates a boot environment with the given name.
|
||||
It will be created from a snapshot of the currently booted boot environment.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_create_from_existing
|
||||
function creates a boot environment with the given name from the name of an
|
||||
existing boot environment.
|
||||
A snapshot will be made of the base boot environment, and the new boot
|
||||
environment will be created from that.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_create_from_existing_snap
|
||||
function creates a boot environment with the given name from an existing
|
||||
snapshot.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_rename
|
||||
function renames a boot environment without unmounting it, as if renamed with
|
||||
the
|
||||
.Fl u
|
||||
argument were passed to
|
||||
.Nm zfs
|
||||
.Cm rename
|
||||
.Pp
|
||||
The
|
||||
.Fn be_activate
|
||||
function makes a boot environment active on the next boot.
|
||||
If the
|
||||
.Fa temporary
|
||||
flag is set, then it will be active for the next boot only, as done by
|
||||
.Xr zfsbootcfg 8 .
|
||||
Next boot functionality is currently only available when booting in x86 BIOS
|
||||
mode.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_destroy
|
||||
function will recursively destroy the given boot environment.
|
||||
It will not destroy a mounted boot environment unless the
|
||||
.Dv BE_DESTROY_FORCE
|
||||
option is set in
|
||||
.Fa options .
|
||||
If the
|
||||
.Dv BE_DESTROY_ORIGIN
|
||||
option is set in
|
||||
.Fa options ,
|
||||
the
|
||||
.Fn be_destroy
|
||||
function will destroy the origin snapshot to this boot environment as well.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_nicenum
|
||||
function will format
|
||||
.Fa name
|
||||
in a traditional ZFS humanized format, similar to
|
||||
.Xr humanize_number 3 .
|
||||
This function effectively proxies
|
||||
.Fn zfs_nicenum
|
||||
from libzfs.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_mount
|
||||
function will mount the given boot environment.
|
||||
If
|
||||
.Fa mountpoint
|
||||
is
|
||||
.Dv NULL ,
|
||||
a mount point will be generated in
|
||||
.Pa /tmp
|
||||
using
|
||||
.Xr mkdtemp 3 .
|
||||
If
|
||||
.Fa result
|
||||
is not
|
||||
.Dv NULL ,
|
||||
it should be large enough to accommodate
|
||||
.Dv BE_MAXPATHLEN
|
||||
including the null terminator.
|
||||
the final mount point will be copied into it.
|
||||
Setting the
|
||||
.Dv BE_MNT_FORCE
|
||||
flag will pass
|
||||
.Dv MNT_FORCE
|
||||
to the underlying
|
||||
.Xr mount 2
|
||||
call.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_mounted_at
|
||||
function will check if there is a boot environment mounted at the given
|
||||
.Fa path .
|
||||
If
|
||||
.Fa details
|
||||
is not
|
||||
.Dv NULL ,
|
||||
it will be populated with a list of the mounted dataset's properties.
|
||||
This list of properties matches the properties collected by
|
||||
.Fn be_get_bootenv_props .
|
||||
.Pp
|
||||
The
|
||||
.Fn be_unmount
|
||||
function will unmount the given boot environment.
|
||||
Setting the
|
||||
.Dv BE_MNT_FORCE
|
||||
flag will pass
|
||||
.Dv MNT_FORCE
|
||||
to the underlying
|
||||
.Xr mount 2
|
||||
call.
|
||||
.Pp
|
||||
The
|
||||
.Fn libbe_errno
|
||||
function returns the
|
||||
.Nm
|
||||
errno.
|
||||
.Pp
|
||||
The
|
||||
.Fn libbe_error_description
|
||||
function returns a string description of the currently set
|
||||
.Nm
|
||||
errno.
|
||||
.Pp
|
||||
The
|
||||
.Fn libbe_print_on_error
|
||||
function will change whether or not
|
||||
.Nm
|
||||
prints the description of any encountered error to
|
||||
.Dv stderr ,
|
||||
based on
|
||||
.Fa doprint .
|
||||
.Pp
|
||||
The
|
||||
.Fn be_root_concat
|
||||
function will concatenate the boot environment root and the given boot
|
||||
environment name into
|
||||
.Fa result .
|
||||
.Pp
|
||||
The
|
||||
.Fn be_validate_name
|
||||
function will validate the given boot environment name for both length
|
||||
restrictions as well as valid character restrictions.
|
||||
This function does not set the internal library error state.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_validate_snap
|
||||
function will validate the given snapshot name.
|
||||
The snapshot must have a valid name, exist, and have a mountpoint of
|
||||
.Pa / .
|
||||
This function does not set the internal library error state.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_exists
|
||||
function will check whether the given boot environment exists and has a
|
||||
mountpoint of
|
||||
.Pa / .
|
||||
This function does not set the internal library error state, but will return
|
||||
the appropriate error.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_export
|
||||
function will export the given boot environment to the file specified by
|
||||
.Fa fd .
|
||||
A snapshot will be created of the boot environment prior to export.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_import
|
||||
function will import the boot environment in the file specified by
|
||||
.Fa fd ,
|
||||
and give it the name
|
||||
.Fa be_name .
|
||||
.Pp
|
||||
The
|
||||
.Fn be_prop_list_alloc
|
||||
function allocates a property list suitable for passing to
|
||||
.Fn be_get_bootenv_props ,
|
||||
.Fn be_get_dataset_props ,
|
||||
or
|
||||
.Fn be_get_dataset_snapshots .
|
||||
It should be freed later by
|
||||
.Fa be_prop_list_free .
|
||||
.Pp
|
||||
The
|
||||
.Fn be_get_bootenv_props
|
||||
function will populate
|
||||
.Fa be_list
|
||||
with
|
||||
.Vt nvpair_t
|
||||
of boot environment names paired with an
|
||||
.Vt nvlist_t
|
||||
of their properties.
|
||||
The following properties are currently collected as appropriate:
|
||||
.Bl -column "Returned name"
|
||||
.It Sy Returned name Ta Sy Description
|
||||
.It dataset Ta -
|
||||
.It name Ta Boot environment name
|
||||
.It mounted Ta Current mount point
|
||||
.It mountpoint Ta Do mountpoint Dc property
|
||||
.It origin Ta Do origin Dc property
|
||||
.It creation Ta Do creation Dc property
|
||||
.It active Ta Currently booted environment
|
||||
.It used Ta Literal Do used Dc property
|
||||
.It usedds Ta Literal Do usedds Dc property
|
||||
.It usedsnap Ta Literal Do usedrefreserv Dc property
|
||||
.It referenced Ta Literal Do referenced Dc property
|
||||
.It nextboot Ta Active on next boot
|
||||
.El
|
||||
.Pp
|
||||
Only the
|
||||
.Dq dataset ,
|
||||
.Dq name ,
|
||||
.Dq active ,
|
||||
and
|
||||
.Dq nextboot
|
||||
returned values will always be present.
|
||||
All other properties may be omitted if not available.
|
||||
.Pp
|
||||
The
|
||||
.Fn be_get_dataset_props
|
||||
function will get properties of the specified dataset.
|
||||
.Fa props
|
||||
is populated directly with a list of the properties as returned by
|
||||
.Fn be_get_bootenv_props .
|
||||
.Pp
|
||||
The
|
||||
.Fn be_get_dataset_snapshots
|
||||
function will retrieve all snapshots of the given dataset.
|
||||
.Fa snap_list
|
||||
will be populated with a list of
|
||||
.Vt nvpair_t
|
||||
exactly as specified by
|
||||
.Fn be_get_bootenv_props .
|
||||
.Pp
|
||||
The
|
||||
.Fn be_prop_list_free
|
||||
function will free the property list.
|
||||
.Sh DIAGNOSTICS
|
||||
Upon error, one of the following values will be returned:
|
||||
.Bl -dash -offset indent -compact
|
||||
.It
|
||||
BE_ERR_SUCCESS
|
||||
.It
|
||||
BE_ERR_INVALIDNAME
|
||||
.It
|
||||
BE_ERR_EXISTS
|
||||
.It
|
||||
BE_ERR_NOENT
|
||||
.It
|
||||
BE_ERR_PERMS
|
||||
.It
|
||||
BE_ERR_DESTROYACT
|
||||
.It
|
||||
BE_ERR_DESTROYMNT
|
||||
.It
|
||||
BE_ERR_BADPATH
|
||||
.It
|
||||
BE_ERR_PATHBUSY
|
||||
.It
|
||||
BE_ERR_PATHLEN
|
||||
.It
|
||||
BE_ERR_BADMOUNT
|
||||
.It
|
||||
BE_ERR_NOORIGIN
|
||||
.It
|
||||
BE_ERR_MOUNTED
|
||||
.It
|
||||
BE_ERR_NOMOUNT
|
||||
.It
|
||||
BE_ERR_ZFSOPEN
|
||||
.It
|
||||
BE_ERR_ZFSCLONE
|
||||
.It
|
||||
BE_ERR_IO
|
||||
.It
|
||||
BE_ERR_NOPOOL
|
||||
.It
|
||||
BE_ERR_NOMEM
|
||||
.It
|
||||
BE_ERR_UNKNOWN
|
||||
.It
|
||||
BE_ERR_INVORIGIN
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr bectl 8
|
||||
.Sh HISTORY
|
||||
.Nm
|
||||
and its corresponding command,
|
||||
.Xr bectl 8 ,
|
||||
were written as a 2017 Google Summer of Code project with Allan Jude serving
|
||||
as a mentor.
|
||||
Later work was done by
|
||||
.An Kyle Evans Aq Mt kevans@FreeBSD.org .
|
@ -123,6 +123,7 @@ CRUNCH_PROGS_sbin+= routed rtquery
|
||||
.endif
|
||||
|
||||
.if ${MK_ZFS} != "no"
|
||||
CRUNCH_PROGS_sbin+= bectl
|
||||
CRUNCH_PROGS_sbin+= zfs
|
||||
CRUNCH_PROGS_sbin+= zpool
|
||||
CRUNCH_PROGS_usr.sbin+= zdb
|
||||
@ -134,6 +135,7 @@ CRUNCH_PROGS_usr.sbin+= zdb
|
||||
CRUNCH_LIBS+= -l80211 -lalias -lcam -lncursesw -ldevstat -lipsec -llzma
|
||||
.if ${MK_ZFS} != "no"
|
||||
CRUNCH_LIBS+= -lavl -lzpool -lzfs_core -lzfs -lnvpair -lpthread -luutil -lumem
|
||||
CRUNCH_LIBS+= -lbe
|
||||
.else
|
||||
# liblzma needs pthread
|
||||
CRUNCH_LIBS+= -lpthread
|
||||
|
@ -87,6 +87,7 @@ SUBDIR.${MK_PF}+= pfctl
|
||||
SUBDIR.${MK_PF}+= pflogd
|
||||
SUBDIR.${MK_QUOTAS}+= quotacheck
|
||||
SUBDIR.${MK_ROUTED}+= routed
|
||||
SUBDIR.${MK_ZFS}+= bectl
|
||||
SUBDIR.${MK_ZFS}+= zfsbootcfg
|
||||
|
||||
SUBDIR.${MK_TESTS}+= tests
|
||||
|
24
sbin/bectl/Makefile
Normal file
24
sbin/bectl/Makefile
Normal file
@ -0,0 +1,24 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.include <src.opts.mk>
|
||||
|
||||
PROG= bectl
|
||||
MAN= bectl.8
|
||||
|
||||
SRCS= bectl.c bectl_jail.c bectl_list.c
|
||||
|
||||
LIBADD+= be
|
||||
LIBADD+= jail
|
||||
LIBADD+= nvpair
|
||||
LIBADD+= util
|
||||
|
||||
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
|
||||
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
|
||||
|
||||
CFLAGS+= -DNEED_SOLARIS_BOOLEAN
|
||||
|
||||
HAS_TESTS= yes
|
||||
SUBDIR.${MK_TESTS}+= tests
|
||||
|
||||
.include <bsd.prog.mk>
|
310
sbin/bectl/bectl.8
Normal file
310
sbin/bectl/bectl.8
Normal file
@ -0,0 +1,310 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
.\"
|
||||
.\" Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
|
||||
.\" 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.
|
||||
.\"
|
||||
.\"
|
||||
.\" @(#)be.1
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd April 3, 2019
|
||||
.Dt BECTL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm bectl
|
||||
.Nd Utility to manage boot environments on ZFS
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Cm activate
|
||||
.Op Fl t
|
||||
.Ar beName
|
||||
.Nm
|
||||
.Cm create
|
||||
.Op Fl r
|
||||
.Op Fl e Brq Ar nonActiveBe | Ar beName Ns Cm @ Ns Ar snapshot
|
||||
.Ar newBeName
|
||||
.Nm
|
||||
.Cm destroy
|
||||
.Op Fl \&Fo
|
||||
.Ar beName Ns Op Cm @ Ns Ar snapshot
|
||||
.Nm
|
||||
.Cm export
|
||||
.Ar sourceBe
|
||||
.Nm
|
||||
.Cm import
|
||||
.Ar targetBe
|
||||
.Nm
|
||||
.Cm jail
|
||||
.Op Fl bU
|
||||
.Oo Bro Fl o Ar key Ns Cm = Ns Ar value | Fl u Ar key Brc Oc Ns ...
|
||||
.Ar beName
|
||||
.Op Ar utility Op Ar argument ...
|
||||
.Nm
|
||||
.Cm list
|
||||
.Op Fl aDHs
|
||||
.Nm
|
||||
.Cm mount
|
||||
.Ar beName
|
||||
.Op Ar mountpoint
|
||||
.Nm
|
||||
.Cm rename
|
||||
.Ar origBeName
|
||||
.Ar newBeName
|
||||
.Nm
|
||||
.Brq Cm ujail | unjail
|
||||
.Brq Ar jailId | jailName
|
||||
.Ar beName
|
||||
.Nm
|
||||
.Brq Cm umount | unmount
|
||||
.Op Fl f
|
||||
.Ar beName
|
||||
.Pp
|
||||
.Nm
|
||||
.Op Fl h\&?
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
command is used to setup and interact with ZFS boot environments, which are
|
||||
bootable clones of datasets.
|
||||
.Pp
|
||||
Boot environments
|
||||
allow the system to be upgraded, while preserving the old system environment in
|
||||
a separate ZFS dataset.
|
||||
.Pp
|
||||
The following commands are supported by
|
||||
.Nm :
|
||||
.Bl -tag -width activate
|
||||
.It Xo
|
||||
.Cm activate
|
||||
.Op Fl t
|
||||
.Ar beName
|
||||
.Xc
|
||||
Activate the given
|
||||
.Ar beName
|
||||
as the default boot filesystem.
|
||||
If the
|
||||
.Op Fl t
|
||||
flag is given, this takes effect only for the next boot.
|
||||
.It Xo
|
||||
.Cm create
|
||||
.Op Fl r
|
||||
.Op Fl e Brq Ar nonActiveBe | Ar beName Ns Cm @ Ns Ar snapshot
|
||||
.Ar newBeName
|
||||
.Xc
|
||||
Create a new boot environment named
|
||||
.Ar newBeName .
|
||||
.Pp
|
||||
If the
|
||||
.Fl r
|
||||
flag is given, a recursive boot environment will be made.
|
||||
.Pp
|
||||
If the
|
||||
.Fl e
|
||||
flag is specified, the new environment will be cloned from the given
|
||||
.Ar nonActiveBe
|
||||
or
|
||||
.Ar beName Ns Cm @ Ns Ar snapshot .
|
||||
Otherwise, the new environment will be created from the currently booted environment.
|
||||
.Pp
|
||||
If
|
||||
.Nm
|
||||
is creating from another boot environment, a snapshot of that boot environment will be created to clone from.
|
||||
.It Xo
|
||||
.Cm destroy
|
||||
.Op Fl \&Fo
|
||||
.Ar beName Ns Op Cm @ Ns Ar snapshot
|
||||
.Xc
|
||||
Destroy the given
|
||||
.Ar beName
|
||||
boot environment or
|
||||
.Ar beName Ns Cm @ Ns Ar snapshot
|
||||
snapshot without confirmation, unlike in
|
||||
.Xr beadm 1 .
|
||||
Specifying
|
||||
.Fl F
|
||||
will automatically unmount without confirmation.
|
||||
.Pp
|
||||
By default,
|
||||
.Nm
|
||||
will warn that it is not destroying the origin of
|
||||
.Ar beName .
|
||||
The
|
||||
.Fl o
|
||||
flag may be specified to destroy the origin as well.
|
||||
.It Cm export Ar sourceBe
|
||||
Export
|
||||
.Ar sourceBe
|
||||
to
|
||||
.Xr stdout 4 .
|
||||
.Xr stdout 4
|
||||
must be piped or redirected to a file.
|
||||
.It Cm import Ar targetBe
|
||||
Import
|
||||
.Ar targetBe
|
||||
from
|
||||
.Xr stdin 4 .
|
||||
.It Xo
|
||||
.Cm jail
|
||||
.Op Fl bU
|
||||
.Oo Bro Fl o Ar key Ns Cm = Ns Ar value | Fl u Ar key Brc Oc Ns ...
|
||||
.Ar beName
|
||||
.Op Ar utility Op Ar argument ...
|
||||
.Xc
|
||||
Create a jail of the given boot environment.
|
||||
Multiple
|
||||
.Fl o
|
||||
and
|
||||
.Fl u
|
||||
arguments may be specified.
|
||||
.Fl o
|
||||
will set a jail parameter, and
|
||||
.Fl u
|
||||
will unset a jail parameter.
|
||||
.Pp
|
||||
By default, jails are created in interactive mode and
|
||||
.Pa /bin/sh
|
||||
is
|
||||
executed within the jail.
|
||||
If
|
||||
.Ar utility
|
||||
is specified, it will be executed instead of
|
||||
.Pa /bin/sh .
|
||||
The jail will be destroyed and the boot environment unmounted when the command
|
||||
finishes executing, unless the
|
||||
.Fl U
|
||||
argument is specified.
|
||||
.Pp
|
||||
The
|
||||
.Fl b
|
||||
argument enables batch mode, thereby disabling interactive mode.
|
||||
The
|
||||
.Fl U
|
||||
argument will be ignored in batch mode.
|
||||
.Pp
|
||||
The
|
||||
.Va name ,
|
||||
.Va host.hostname ,
|
||||
and
|
||||
.Va path
|
||||
must be set, the default values are specified below.
|
||||
.Pp
|
||||
All
|
||||
.Ar key Ns Cm = Ns Ar value
|
||||
pairs are interpreted as jail parameters as described in
|
||||
.Xr jail 8 .
|
||||
The following default parameters are provided:
|
||||
.Bl -column "allow.mount.devfs" ""
|
||||
.It Va allow.mount Ta Cm true
|
||||
.It Va allow.mount.devfs Ta Cm true
|
||||
.It Va enforce_statfs Ta Cm 1
|
||||
.It Va name Ta Set to jail ID.
|
||||
.It Va host.hostname Ta Va bootenv
|
||||
.It Va path Ta Set to a path in Pa /tmp
|
||||
generated by
|
||||
.Xr libbe 3 .
|
||||
.El
|
||||
.Pp
|
||||
All default parameters may be overwritten.
|
||||
.It Cm list Op Fl aDHs
|
||||
Display all boot environments.
|
||||
The
|
||||
.Em Active
|
||||
field indicates whether the boot environment is active now
|
||||
.Pq Em \&N ;
|
||||
active on reboot
|
||||
.Pq Em \&R ;
|
||||
or both
|
||||
.Pq Em \&NR .
|
||||
.Pp
|
||||
If
|
||||
.Fl a
|
||||
is used, display all datasets.
|
||||
If
|
||||
.Fl D
|
||||
is used, display the full space usage for each boot environment, assuming all
|
||||
other boot environments were destroyed.
|
||||
The
|
||||
.Fl H
|
||||
option is used for scripting.
|
||||
It does not print headers and separate fields by a single tab instead of
|
||||
arbitrary white space.
|
||||
If
|
||||
.Fl s
|
||||
is used, display all snapshots as well.
|
||||
.It Cm mount Ar beName Op Ar mountpoint
|
||||
Temporarily mount the boot environment.
|
||||
Mount at the specified
|
||||
.Ar mountpoint
|
||||
if provided.
|
||||
.It Cm rename Ar origBeName newBeName
|
||||
Rename the given
|
||||
.Ar origBeName
|
||||
to the given
|
||||
.Ar newBeName .
|
||||
The boot environment will not be unmounted in order for this rename to occur.
|
||||
.It Cm ujail Bro Ar jailId | jailName Brc Ar beName
|
||||
.It Cm unjail Bro Ar jailId | jailName Brc Ar beName
|
||||
Destroy the jail created from the given boot environment.
|
||||
.It Xo
|
||||
.Cm umount
|
||||
.Op Fl f
|
||||
.Ar beName
|
||||
.Xc
|
||||
.It Xo
|
||||
.Cm unmount
|
||||
.Op Fl f
|
||||
.Ar beName
|
||||
.Xc
|
||||
Unmount the given boot environment, if it is mounted.
|
||||
Specifying
|
||||
.Fl f
|
||||
will force the unmount if busy.
|
||||
.El
|
||||
.Pp
|
||||
.Nm
|
||||
prints usage information if
|
||||
.Fl h
|
||||
or
|
||||
.Fl \&?
|
||||
is specified.
|
||||
.Sh EXAMPLES
|
||||
.Bl -bullet
|
||||
.It
|
||||
To fill in with jail upgrade example when behavior is firm.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr beinstall.sh 1 ,
|
||||
.Xr libbe 3 ,
|
||||
.Xr jail 8 ,
|
||||
.Xr zfs 8 ,
|
||||
.Xr zpool 8
|
||||
.Sh HISTORY
|
||||
.Nm
|
||||
is based on
|
||||
.Xr beadm 1
|
||||
and was implemented as a project for the 2017 Summer of Code, along with
|
||||
.Xr libbe 3 .
|
||||
.Sh AUTHORS
|
||||
.Nm
|
||||
was written by
|
||||
.An Kyle Kneitinger (kneitinger) Aq Mt kyle@kneit.in .
|
||||
.Pp
|
||||
.Xr beadm 1
|
||||
was written and is maintained by
|
||||
.An Slawomir Wojciech Wojtczak (vermaden) Aq Mt vermaden@interia.pl .
|
||||
.Pp
|
||||
.An Bryan Drewery (bdrewery) Aq Mt bryan@shatow.net
|
||||
wrote the original
|
||||
.Xr beadm 1
|
||||
manual page that this one is derived from.
|
561
sbin/bectl/bectl.c
Normal file
561
sbin/bectl/bectl.c
Normal file
@ -0,0 +1,561 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <errno.h>
|
||||
#include <libutil.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <be.h>
|
||||
|
||||
#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;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
37
sbin/bectl/bectl.h
Normal file
37
sbin/bectl/bectl.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
|
||||
*
|
||||
* 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 ``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 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$
|
||||
*/
|
||||
|
||||
int usage(bool explicit);
|
||||
|
||||
int bectl_cmd_jail(int argc, char *argv[]);
|
||||
int bectl_cmd_unjail(int argc, char *argv[]);
|
||||
|
||||
int bectl_cmd_list(int argc, char *argv[]);
|
||||
|
||||
extern libbe_handle_t *be;
|
486
sbin/bectl/bectl_jail.c
Normal file
486
sbin/bectl/bectl_jail.c
Normal file
@ -0,0 +1,486 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
|
||||
*
|
||||
* 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 ``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 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/jail.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <err.h>
|
||||
#include <jail.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <be.h>
|
||||
#include "bectl.h"
|
||||
|
||||
#define MNTTYPE_ZFS 222
|
||||
|
||||
static void jailparam_add(const char *name, const char *val);
|
||||
static int jailparam_del(const char *name);
|
||||
static bool jailparam_addarg(char *arg);
|
||||
static int jailparam_delarg(char *arg);
|
||||
|
||||
static int bectl_search_jail_paths(const char *mnt);
|
||||
static int bectl_locate_jail(const char *ident);
|
||||
static int bectl_jail_cleanup(char *mountpoint, int jid);
|
||||
|
||||
static char mnt_loc[BE_MAXPATHLEN];
|
||||
static nvlist_t *jailparams;
|
||||
|
||||
static const char *disabled_params[] = {
|
||||
"command", "exec.start", "nopersist", "persist", NULL
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
jailparam_add(const char *name, const char *val)
|
||||
{
|
||||
|
||||
nvlist_add_string(jailparams, name, val);
|
||||
}
|
||||
|
||||
static int
|
||||
jailparam_del(const char *name)
|
||||
{
|
||||
|
||||
nvlist_remove_all(jailparams, name);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static bool
|
||||
jailparam_addarg(char *arg)
|
||||
{
|
||||
char *name, *val;
|
||||
size_t i, len;
|
||||
|
||||
if (arg == NULL)
|
||||
return (false);
|
||||
name = arg;
|
||||
if ((val = strchr(arg, '=')) == NULL) {
|
||||
fprintf(stderr, "bectl jail: malformed jail option '%s'\n",
|
||||
arg);
|
||||
return (false);
|
||||
}
|
||||
|
||||
*val++ = '\0';
|
||||
if (strcmp(name, "path") == 0) {
|
||||
if (strlen(val) >= BE_MAXPATHLEN) {
|
||||
fprintf(stderr,
|
||||
"bectl jail: skipping too long path assignment '%s' (max length = %d)\n",
|
||||
val, BE_MAXPATHLEN);
|
||||
return (false);
|
||||
}
|
||||
strlcpy(mnt_loc, val, sizeof(mnt_loc));
|
||||
}
|
||||
|
||||
for (i = 0; disabled_params[i] != NULL; i++) {
|
||||
len = strlen(disabled_params[i]);
|
||||
if (strncmp(disabled_params[i], name, len) == 0) {
|
||||
fprintf(stderr, "invalid jail parameter: %s\n", name);
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
jailparam_add(name, val);
|
||||
return (true);
|
||||
}
|
||||
|
||||
static int
|
||||
jailparam_delarg(char *arg)
|
||||
{
|
||||
char *name, *val;
|
||||
|
||||
if (arg == NULL)
|
||||
return (EINVAL);
|
||||
name = arg;
|
||||
if ((val = strchr(name, '=')) != NULL)
|
||||
*val++ = '\0';
|
||||
|
||||
if (strcmp(name, "path") == 0)
|
||||
*mnt_loc = '\0';
|
||||
return (jailparam_del(name));
|
||||
}
|
||||
|
||||
static int
|
||||
build_jailcmd(char ***argvp, bool interactive, int argc, char *argv[])
|
||||
{
|
||||
char *cmd, **jargv, *name, *val;
|
||||
nvpair_t *nvp;
|
||||
size_t i, iarg, nargv;
|
||||
|
||||
cmd = NULL;
|
||||
nvp = NULL;
|
||||
iarg = i = 0;
|
||||
if (nvlist_size(jailparams, &nargv, NV_ENCODE_NATIVE) != 0)
|
||||
return (1);
|
||||
|
||||
/*
|
||||
* Number of args + "/usr/sbin/jail", "-c", and ending NULL.
|
||||
* If interactive also include command.
|
||||
*/
|
||||
nargv += 3;
|
||||
if (interactive) {
|
||||
if (argc == 0)
|
||||
nargv++;
|
||||
else
|
||||
nargv += argc;
|
||||
}
|
||||
|
||||
jargv = *argvp = calloc(nargv, sizeof(jargv));
|
||||
if (jargv == NULL)
|
||||
err(2, "calloc");
|
||||
|
||||
jargv[iarg++] = strdup("/usr/sbin/jail");
|
||||
jargv[iarg++] = strdup("-c");
|
||||
while ((nvp = nvlist_next_nvpair(jailparams, nvp)) != NULL) {
|
||||
name = nvpair_name(nvp);
|
||||
if (nvpair_value_string(nvp, &val) != 0)
|
||||
continue;
|
||||
|
||||
if (asprintf(&jargv[iarg++], "%s=%s", name, val) < 0)
|
||||
goto error;
|
||||
}
|
||||
if (interactive) {
|
||||
if (argc < 1)
|
||||
cmd = strdup("/bin/sh");
|
||||
else {
|
||||
cmd = argv[0];
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
if (asprintf(&jargv[iarg++], "command=%s", cmd) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (argc < 1) {
|
||||
free(cmd);
|
||||
cmd = NULL;
|
||||
}
|
||||
|
||||
for (; argc > 0; argc--) {
|
||||
if (asprintf(&jargv[iarg++], "%s", argv[0]) < 0)
|
||||
goto error;
|
||||
argv++;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
error:
|
||||
if (interactive && argc < 1)
|
||||
free(cmd);
|
||||
for (; i < iarg - 1; i++) {
|
||||
free(jargv[i]);
|
||||
}
|
||||
free(jargv);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Remove jail and cleanup any non zfs mounts. */
|
||||
static int
|
||||
bectl_jail_cleanup(char *mountpoint, int jid)
|
||||
{
|
||||
struct statfs *mntbuf;
|
||||
size_t i, searchlen, mntsize;
|
||||
|
||||
if (jid >= 0 && jail_remove(jid) != 0) {
|
||||
fprintf(stderr, "unable to remove jail");
|
||||
return (1);
|
||||
}
|
||||
|
||||
searchlen = strnlen(mountpoint, MAXPATHLEN);
|
||||
mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
|
||||
for (i = 0; i < mntsize; i++) {
|
||||
if (strncmp(mountpoint, mntbuf[i].f_mntonname, searchlen) == 0 &&
|
||||
mntbuf[i].f_type != MNTTYPE_ZFS) {
|
||||
|
||||
if (unmount(mntbuf[i].f_mntonname, 0) != 0) {
|
||||
fprintf(stderr, "bectl jail: unable to unmount filesystem %s",
|
||||
mntbuf[i].f_mntonname);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
bectl_cmd_jail(int argc, char *argv[])
|
||||
{
|
||||
char *bootenv, **jargv, *mountpoint;
|
||||
int i, jid, mntflags, opt, ret;
|
||||
bool default_hostname, interactive, unjail;
|
||||
pid_t pid;
|
||||
|
||||
|
||||
/* XXX TODO: Allow shallow */
|
||||
mntflags = BE_MNT_DEEP;
|
||||
default_hostname = interactive = unjail = true;
|
||||
|
||||
if ((nvlist_alloc(&jailparams, NV_UNIQUE_NAME, 0)) != 0) {
|
||||
fprintf(stderr, "nvlist_alloc() failed\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
jailparam_add("persist", "true");
|
||||
jailparam_add("allow.mount", "true");
|
||||
jailparam_add("allow.mount.devfs", "true");
|
||||
jailparam_add("enforce_statfs", "1");
|
||||
|
||||
while ((opt = getopt(argc, argv, "bo:Uu:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'b':
|
||||
interactive = false;
|
||||
break;
|
||||
case 'o':
|
||||
if (jailparam_addarg(optarg)) {
|
||||
/*
|
||||
* optarg has been modified to null terminate
|
||||
* at the assignment operator.
|
||||
*/
|
||||
if (strcmp(optarg, "host.hostname") == 0)
|
||||
default_hostname = false;
|
||||
} else {
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
case 'U':
|
||||
unjail = false;
|
||||
break;
|
||||
case 'u':
|
||||
if ((ret = jailparam_delarg(optarg)) == 0) {
|
||||
if (strcmp(optarg, "host.hostname") == 0)
|
||||
default_hostname = true;
|
||||
} else if (ret != ENOENT) {
|
||||
fprintf(stderr,
|
||||
"bectl jail: error unsetting \"%s\"\n",
|
||||
optarg);
|
||||
return (ret);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "bectl jail: unknown option '-%c'\n",
|
||||
optopt);
|
||||
return (usage(false));
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc < 1) {
|
||||
fprintf(stderr, "bectl jail: missing boot environment name\n");
|
||||
return (usage(false));
|
||||
}
|
||||
|
||||
bootenv = argv[0];
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
/*
|
||||
* XXX TODO: if its already mounted, perhaps there should be a flag to
|
||||
* indicate its okay to proceed??
|
||||
*/
|
||||
if (*mnt_loc == '\0')
|
||||
mountpoint = NULL;
|
||||
else
|
||||
mountpoint = mnt_loc;
|
||||
if (be_mount(be, bootenv, mountpoint, mntflags, mnt_loc) != BE_ERR_SUCCESS) {
|
||||
fprintf(stderr, "could not mount bootenv\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (default_hostname)
|
||||
jailparam_add("host.hostname", bootenv);
|
||||
|
||||
/*
|
||||
* This is our indicator that path was not set by the user, so we'll use
|
||||
* the path that libbe generated for us.
|
||||
*/
|
||||
if (mountpoint == NULL) {
|
||||
jailparam_add("path", mnt_loc);
|
||||
mountpoint = mnt_loc;
|
||||
}
|
||||
|
||||
if ((build_jailcmd(&jargv, interactive, argc, argv)) != 0) {
|
||||
fprintf(stderr, "unable to build argument list for jail command\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
|
||||
switch (pid) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
return (1);
|
||||
case 0:
|
||||
execv("/usr/sbin/jail", jargv);
|
||||
fprintf(stderr, "bectl jail: failed to execute\n");
|
||||
default:
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
for (i = 0; jargv[i] != NULL; i++) {
|
||||
free(jargv[i]);
|
||||
}
|
||||
free(jargv);
|
||||
|
||||
if (!interactive)
|
||||
return (0);
|
||||
|
||||
if (unjail) {
|
||||
/*
|
||||
* We're not checking the jail id result here because in the
|
||||
* case of invalid param, or last command in jail was an error
|
||||
* the jail will not exist upon exit. bectl_jail_cleanup will
|
||||
* only jail_remove if the jid is >= 0.
|
||||
*/
|
||||
jid = bectl_locate_jail(bootenv);
|
||||
bectl_jail_cleanup(mountpoint, jid);
|
||||
be_unmount(be, bootenv, 0);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bectl_search_jail_paths(const char *mnt)
|
||||
{
|
||||
int jid;
|
||||
char lastjid[16];
|
||||
char jailpath[MAXPATHLEN];
|
||||
|
||||
/* jail_getv expects name/value strings */
|
||||
snprintf(lastjid, sizeof(lastjid), "%d", 0);
|
||||
|
||||
while ((jid = jail_getv(0, "lastjid", lastjid, "path", &jailpath,
|
||||
NULL)) != -1) {
|
||||
|
||||
/* the jail we've been looking for */
|
||||
if (strcmp(jailpath, mnt) == 0)
|
||||
return (jid);
|
||||
|
||||
/* update lastjid and keep on looking */
|
||||
snprintf(lastjid, sizeof(lastjid), "%d", jid);
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Locate a jail based on an arbitrary identifier. This may be either a name,
|
||||
* a jid, or a BE name. Returns the jid or -1 on failure.
|
||||
*/
|
||||
static int
|
||||
bectl_locate_jail(const char *ident)
|
||||
{
|
||||
nvlist_t *belist, *props;
|
||||
char *mnt;
|
||||
int jid;
|
||||
|
||||
/* Try the easy-match first */
|
||||
jid = jail_getid(ident);
|
||||
if (jid != -1)
|
||||
return (jid);
|
||||
|
||||
/* Attempt to try it as a BE name, first */
|
||||
if (be_prop_list_alloc(&belist) != 0)
|
||||
return (-1);
|
||||
|
||||
if (be_get_bootenv_props(be, belist) != 0)
|
||||
return (-1);
|
||||
|
||||
if (nvlist_lookup_nvlist(belist, ident, &props) == 0) {
|
||||
|
||||
/* path where a boot environment is mounted */
|
||||
if (nvlist_lookup_string(props, "mounted", &mnt) == 0) {
|
||||
|
||||
/* looking for a jail that matches our bootenv path */
|
||||
jid = bectl_search_jail_paths(mnt);
|
||||
be_prop_list_free(belist);
|
||||
return (jid);
|
||||
}
|
||||
|
||||
be_prop_list_free(belist);
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
bectl_cmd_unjail(int argc, char *argv[])
|
||||
{
|
||||
char path[MAXPATHLEN];
|
||||
char *cmd, *name, *target;
|
||||
int jid;
|
||||
|
||||
/* Store alias used */
|
||||
cmd = argv[0];
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
|
||||
return (usage(false));
|
||||
}
|
||||
|
||||
target = argv[1];
|
||||
|
||||
/* Locate the jail */
|
||||
if ((jid = bectl_locate_jail(target)) == -1) {
|
||||
fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd,
|
||||
target);
|
||||
return (1);
|
||||
}
|
||||
|
||||
bzero(&path, MAXPATHLEN);
|
||||
name = jail_getname(jid);
|
||||
if (jail_getv(0, "name", name, "path", path, NULL) != jid) {
|
||||
free(name);
|
||||
fprintf(stderr,
|
||||
"bectl %s: failed to get path for jail requested by '%s'\n",
|
||||
cmd, target);
|
||||
return (1);
|
||||
}
|
||||
|
||||
free(name);
|
||||
|
||||
if (be_mounted_at(be, path, NULL) != 0) {
|
||||
fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n",
|
||||
cmd, target);
|
||||
return (1);
|
||||
}
|
||||
|
||||
bectl_jail_cleanup(path, jid);
|
||||
be_unmount(be, target, 0);
|
||||
|
||||
return (0);
|
||||
}
|
419
sbin/bectl/bectl_list.c
Normal file
419
sbin/bectl/bectl_list.c
Normal file
@ -0,0 +1,419 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
|
||||
*
|
||||
* 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 ``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 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <be.h>
|
||||
|
||||
#include "bectl.h"
|
||||
|
||||
struct printc {
|
||||
int active_colsz_def;
|
||||
int be_colsz;
|
||||
int current_indent;
|
||||
int mount_colsz;
|
||||
int space_colsz;
|
||||
bool script_fmt;
|
||||
bool show_all_datasets;
|
||||
bool show_snaps;
|
||||
bool show_space;
|
||||
};
|
||||
|
||||
static const char *get_origin_props(nvlist_t *dsprops, nvlist_t **originprops);
|
||||
static void print_padding(const char *fval, int colsz, struct printc *pc);
|
||||
static int print_snapshots(const char *dsname, struct printc *pc);
|
||||
static void print_info(const char *name, nvlist_t *dsprops, struct printc *pc);
|
||||
static void print_headers(nvlist_t *props, struct printc *pc);
|
||||
static unsigned long long dataset_space(const char *oname);
|
||||
|
||||
#define HEADER_BE "BE"
|
||||
#define HEADER_BEPLUS "BE/Dataset/Snapshot"
|
||||
#define HEADER_ACTIVE "Active"
|
||||
#define HEADER_MOUNT "Mountpoint"
|
||||
#define HEADER_SPACE "Space"
|
||||
#define HEADER_CREATED "Created"
|
||||
|
||||
/* Spaces */
|
||||
#define INDENT_INCREMENT 2
|
||||
|
||||
/*
|
||||
* Given a set of dataset properties (for a BE dataset), populate originprops
|
||||
* with the origin's properties.
|
||||
*/
|
||||
static const char *
|
||||
get_origin_props(nvlist_t *dsprops, nvlist_t **originprops)
|
||||
{
|
||||
char *propstr;
|
||||
|
||||
if (nvlist_lookup_string(dsprops, "origin", &propstr) == 0) {
|
||||
if (be_prop_list_alloc(originprops) != 0) {
|
||||
fprintf(stderr,
|
||||
"bectl list: failed to allocate origin prop nvlist\n");
|
||||
return (NULL);
|
||||
}
|
||||
if (be_get_dataset_props(be, propstr, *originprops) != 0) {
|
||||
/* XXX TODO: Real errors */
|
||||
fprintf(stderr,
|
||||
"bectl list: failed to fetch origin properties\n");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (propstr);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
print_padding(const char *fval, int colsz, struct printc *pc)
|
||||
{
|
||||
|
||||
/* -H flag handling; all delimiters/padding are a single tab */
|
||||
if (pc->script_fmt) {
|
||||
printf("\t");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fval != NULL)
|
||||
colsz -= strlen(fval);
|
||||
printf("%*s ", colsz, "");
|
||||
}
|
||||
|
||||
static unsigned long long
|
||||
dataset_space(const char *oname)
|
||||
{
|
||||
unsigned long long space;
|
||||
char *dsname, *propstr, *sep;
|
||||
nvlist_t *dsprops;
|
||||
|
||||
space = 0;
|
||||
dsname = strdup(oname);
|
||||
if (dsname == NULL)
|
||||
return (0);
|
||||
|
||||
/* Truncate snapshot to dataset name, as needed */
|
||||
if ((sep = strchr(dsname, '@')) != NULL)
|
||||
*sep = '\0';
|
||||
|
||||
if (be_prop_list_alloc(&dsprops) != 0) {
|
||||
free(dsname);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (be_get_dataset_props(be, dsname, dsprops) != 0) {
|
||||
nvlist_free(dsprops);
|
||||
free(dsname);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (nvlist_lookup_string(dsprops, "used", &propstr) == 0)
|
||||
space = strtoull(propstr, NULL, 10);
|
||||
|
||||
nvlist_free(dsprops);
|
||||
free(dsname);
|
||||
return (space);
|
||||
}
|
||||
|
||||
static int
|
||||
print_snapshots(const char *dsname, struct printc *pc)
|
||||
{
|
||||
nvpair_t *cur;
|
||||
nvlist_t *props, *sprops;
|
||||
|
||||
if (be_prop_list_alloc(&props) != 0) {
|
||||
fprintf(stderr, "bectl list: failed to allocate snapshot nvlist\n");
|
||||
return (1);
|
||||
}
|
||||
if (be_get_dataset_snapshots(be, dsname, props) != 0) {
|
||||
fprintf(stderr, "bectl list: failed to fetch boot ds snapshots\n");
|
||||
return (1);
|
||||
}
|
||||
for (cur = nvlist_next_nvpair(props, NULL); cur != NULL;
|
||||
cur = nvlist_next_nvpair(props, cur)) {
|
||||
nvpair_value_nvlist(cur, &sprops);
|
||||
print_info(nvpair_name(cur), sprops, pc);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
print_info(const char *name, nvlist_t *dsprops, struct printc *pc)
|
||||
{
|
||||
#define BUFSZ 64
|
||||
char buf[BUFSZ];
|
||||
unsigned long long ctimenum, space;
|
||||
nvlist_t *originprops;
|
||||
const char *oname;
|
||||
char *dsname, *propstr;
|
||||
int active_colsz;
|
||||
boolean_t active_now, active_reboot;
|
||||
|
||||
dsname = NULL;
|
||||
originprops = NULL;
|
||||
printf("%*s%s", pc->current_indent, "", name);
|
||||
nvlist_lookup_string(dsprops, "dataset", &dsname);
|
||||
|
||||
/* Recurse at the base level if we're breaking info down */
|
||||
if (pc->current_indent == 0 && (pc->show_all_datasets ||
|
||||
pc->show_snaps)) {
|
||||
printf("\n");
|
||||
if (dsname == NULL)
|
||||
/* XXX TODO: Error? */
|
||||
return;
|
||||
/*
|
||||
* Whether we're dealing with -a or -s, we'll always print the
|
||||
* dataset name/information followed by its origin. For -s, we
|
||||
* additionally iterate through all snapshots of this boot
|
||||
* environment and also print their information.
|
||||
*/
|
||||
pc->current_indent += INDENT_INCREMENT;
|
||||
print_info(dsname, dsprops, pc);
|
||||
pc->current_indent += INDENT_INCREMENT;
|
||||
if ((oname = get_origin_props(dsprops, &originprops)) != NULL) {
|
||||
print_info(oname, originprops, pc);
|
||||
nvlist_free(originprops);
|
||||
}
|
||||
|
||||
/* Back up a level; snapshots at the same level as dataset */
|
||||
pc->current_indent -= INDENT_INCREMENT;
|
||||
if (pc->show_snaps)
|
||||
print_snapshots(dsname, pc);
|
||||
pc->current_indent = 0;
|
||||
return;
|
||||
} else
|
||||
print_padding(name, pc->be_colsz - pc->current_indent, pc);
|
||||
|
||||
active_colsz = pc->active_colsz_def;
|
||||
if (nvlist_lookup_boolean_value(dsprops, "active",
|
||||
&active_now) == 0 && active_now) {
|
||||
printf("N");
|
||||
active_colsz--;
|
||||
}
|
||||
if (nvlist_lookup_boolean_value(dsprops, "nextboot",
|
||||
&active_reboot) == 0 && active_reboot) {
|
||||
printf("R");
|
||||
active_colsz--;
|
||||
}
|
||||
if (active_colsz == pc->active_colsz_def) {
|
||||
printf("-");
|
||||
active_colsz--;
|
||||
}
|
||||
print_padding(NULL, active_colsz, pc);
|
||||
if (nvlist_lookup_string(dsprops, "mounted", &propstr) == 0) {
|
||||
printf("%s", propstr);
|
||||
print_padding(propstr, pc->mount_colsz, pc);
|
||||
} else {
|
||||
printf("%s", "-");
|
||||
print_padding("-", pc->mount_colsz, pc);
|
||||
}
|
||||
|
||||
oname = get_origin_props(dsprops, &originprops);
|
||||
if (nvlist_lookup_string(dsprops, "used", &propstr) == 0) {
|
||||
/*
|
||||
* The space used column is some composition of:
|
||||
* - The "used" property of the dataset
|
||||
* - The "used" property of the origin snapshot (not -a or -s)
|
||||
* - The "used" property of the origin dataset (-D flag only)
|
||||
*
|
||||
* The -D flag is ignored if -a or -s are specified.
|
||||
*/
|
||||
space = strtoull(propstr, NULL, 10);
|
||||
|
||||
if (!pc->show_all_datasets && !pc->show_snaps &&
|
||||
originprops != NULL &&
|
||||
nvlist_lookup_string(originprops, "used", &propstr) == 0)
|
||||
space += strtoull(propstr, NULL, 10);
|
||||
|
||||
if (pc->show_space && oname != NULL)
|
||||
space += dataset_space(oname);
|
||||
|
||||
/* Alas, there's more to it,. */
|
||||
be_nicenum(space, buf, 6);
|
||||
printf("%s", buf);
|
||||
print_padding(buf, pc->space_colsz, pc);
|
||||
} else {
|
||||
printf("-");
|
||||
print_padding("-", pc->space_colsz, pc);
|
||||
}
|
||||
|
||||
if (nvlist_lookup_string(dsprops, "creation", &propstr) == 0) {
|
||||
ctimenum = strtoull(propstr, NULL, 10);
|
||||
strftime(buf, BUFSZ, "%Y-%m-%d %H:%M",
|
||||
localtime((time_t *)&ctimenum));
|
||||
printf("%s", buf);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
if (originprops != NULL)
|
||||
be_prop_list_free(originprops);
|
||||
#undef BUFSZ
|
||||
}
|
||||
|
||||
static void
|
||||
print_headers(nvlist_t *props, struct printc *pc)
|
||||
{
|
||||
const char *chosen_be_header;
|
||||
nvpair_t *cur;
|
||||
nvlist_t *dsprops;
|
||||
char *propstr;
|
||||
size_t be_maxcol;
|
||||
|
||||
if (pc->show_all_datasets || pc->show_snaps)
|
||||
chosen_be_header = HEADER_BEPLUS;
|
||||
else
|
||||
chosen_be_header = HEADER_BE;
|
||||
be_maxcol = strlen(chosen_be_header);
|
||||
for (cur = nvlist_next_nvpair(props, NULL); cur != NULL;
|
||||
cur = nvlist_next_nvpair(props, cur)) {
|
||||
be_maxcol = MAX(be_maxcol, strlen(nvpair_name(cur)));
|
||||
if (!pc->show_all_datasets && !pc->show_snaps)
|
||||
continue;
|
||||
nvpair_value_nvlist(cur, &dsprops);
|
||||
if (nvlist_lookup_string(dsprops, "dataset", &propstr) != 0)
|
||||
continue;
|
||||
be_maxcol = MAX(be_maxcol, strlen(propstr) + INDENT_INCREMENT);
|
||||
if (nvlist_lookup_string(dsprops, "origin", &propstr) != 0)
|
||||
continue;
|
||||
be_maxcol = MAX(be_maxcol,
|
||||
strlen(propstr) + INDENT_INCREMENT * 2);
|
||||
}
|
||||
|
||||
pc->be_colsz = be_maxcol;
|
||||
pc->active_colsz_def = strlen(HEADER_ACTIVE);
|
||||
pc->mount_colsz = strlen(HEADER_MOUNT);
|
||||
pc->space_colsz = strlen(HEADER_SPACE);
|
||||
printf("%*s %s %s %s %s\n", -pc->be_colsz, chosen_be_header,
|
||||
HEADER_ACTIVE, HEADER_MOUNT, HEADER_SPACE, HEADER_CREATED);
|
||||
|
||||
/*
|
||||
* All other invocations in which we aren't using the default header
|
||||
* will produce quite a bit of input. Throw an extra blank line after
|
||||
* the header to make it look nicer.
|
||||
*/
|
||||
if (strcmp(chosen_be_header, HEADER_BE) != 0)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int
|
||||
bectl_cmd_list(int argc, char *argv[])
|
||||
{
|
||||
struct printc pc;
|
||||
nvpair_t *cur;
|
||||
nvlist_t *dsprops, *props;
|
||||
int opt, printed;
|
||||
boolean_t active_now, active_reboot;
|
||||
|
||||
props = NULL;
|
||||
printed = 0;
|
||||
bzero(&pc, sizeof(pc));
|
||||
while ((opt = getopt(argc, argv, "aDHs")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
pc.show_all_datasets = true;
|
||||
break;
|
||||
case 'D':
|
||||
pc.show_space = true;
|
||||
break;
|
||||
case 'H':
|
||||
pc.script_fmt = true;
|
||||
break;
|
||||
case 's':
|
||||
pc.show_snaps = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "bectl list: unknown option '-%c'\n",
|
||||
optopt);
|
||||
return (usage(false));
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
|
||||
if (argc != 0) {
|
||||
fprintf(stderr, "bectl list: extra argument provided\n");
|
||||
return (usage(false));
|
||||
}
|
||||
|
||||
if (be_prop_list_alloc(&props) != 0) {
|
||||
fprintf(stderr, "bectl list: failed to allocate prop nvlist\n");
|
||||
return (1);
|
||||
}
|
||||
if (be_get_bootenv_props(be, props) != 0) {
|
||||
/* XXX TODO: Real errors */
|
||||
fprintf(stderr, "bectl list: failed to fetch boot environments\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Force -D off if either -a or -s are specified */
|
||||
if (pc.show_all_datasets || pc.show_snaps)
|
||||
pc.show_space = false;
|
||||
if (!pc.script_fmt)
|
||||
print_headers(props, &pc);
|
||||
/* Do a first pass to print active and next active first */
|
||||
for (cur = nvlist_next_nvpair(props, NULL); cur != NULL;
|
||||
cur = nvlist_next_nvpair(props, cur)) {
|
||||
nvpair_value_nvlist(cur, &dsprops);
|
||||
active_now = active_reboot = false;
|
||||
|
||||
nvlist_lookup_boolean_value(dsprops, "active", &active_now);
|
||||
nvlist_lookup_boolean_value(dsprops, "nextboot",
|
||||
&active_reboot);
|
||||
if (!active_now && !active_reboot)
|
||||
continue;
|
||||
if (printed > 0 && (pc.show_all_datasets || pc.show_snaps))
|
||||
printf("\n");
|
||||
print_info(nvpair_name(cur), dsprops, &pc);
|
||||
printed++;
|
||||
}
|
||||
|
||||
/* Now pull everything else */
|
||||
for (cur = nvlist_next_nvpair(props, NULL); cur != NULL;
|
||||
cur = nvlist_next_nvpair(props, cur)) {
|
||||
nvpair_value_nvlist(cur, &dsprops);
|
||||
active_now = active_reboot = false;
|
||||
|
||||
nvlist_lookup_boolean_value(dsprops, "active", &active_now);
|
||||
nvlist_lookup_boolean_value(dsprops, "nextboot",
|
||||
&active_reboot);
|
||||
if (active_now || active_reboot)
|
||||
continue;
|
||||
if (printed > 0 && (pc.show_all_datasets || pc.show_snaps))
|
||||
printf("\n");
|
||||
print_info(nvpair_name(cur), dsprops, &pc);
|
||||
printed++;
|
||||
}
|
||||
be_prop_list_free(props);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
7
sbin/bectl/tests/Makefile
Normal file
7
sbin/bectl/tests/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PACKAGE= tests
|
||||
|
||||
ATF_TESTS_SH+= bectl_test
|
||||
|
||||
.include <bsd.test.mk>
|
368
sbin/bectl/tests/bectl_test.sh
Executable file
368
sbin/bectl/tests/bectl_test.sh
Executable file
@ -0,0 +1,368 @@
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
#
|
||||
# Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
|
||||
#
|
||||
# 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}
|
||||
# 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
|
||||
}
|
||||
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 a second boot environment
|
||||
atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default target
|
||||
|
||||
# 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()
|
||||
{
|
||||
for bootenv in "default" "target"; do
|
||||
# mountpoint of the boot environment
|
||||
mountpoint="$(bectl -r bectl_test/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 $(get_zpool_name)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@ -21,6 +21,7 @@ LIBASN1?= ${DESTDIR}${LIBDIR_BASE}/libasn1.a
|
||||
LIBATM?= ${DESTDIR}${LIBDIR_BASE}/libatm.a
|
||||
LIBAUDITD?= ${DESTDIR}${LIBDIR_BASE}/libauditd.a
|
||||
LIBAVL?= ${DESTDIR}${LIBDIR_BASE}/libavl.a
|
||||
LIBBE?= ${DESTDIR}${LIBDIR_BASE}/libbe.a
|
||||
LIBBEGEMOT?= ${DESTDIR}${LIBDIR_BASE}/libbegemot.a
|
||||
LIBBLACKLIST?= ${DESTDIR}${LIBDIR_BASE}/libblacklist.a
|
||||
LIBBLUETOOTH?= ${DESTDIR}${LIBDIR_BASE}/libbluetooth.a
|
||||
|
@ -59,6 +59,7 @@ _LIBRARIES= \
|
||||
asn1 \
|
||||
auditd \
|
||||
avl \
|
||||
be \
|
||||
begemot \
|
||||
bluetooth \
|
||||
bsdxml \
|
||||
@ -326,6 +327,7 @@ _DP_zfs= md pthread umem util uutil m nvpair avl bsdxml geom nvpair z \
|
||||
zfs_core
|
||||
_DP_zfs_core= nvpair
|
||||
_DP_zpool= md pthread z nvpair avl umem
|
||||
_DP_be= zfs nvpair
|
||||
|
||||
# OFED support
|
||||
.if ${MK_OFED} != "no"
|
||||
@ -466,6 +468,8 @@ LIBBSNMPTOOLS?= ${LIBBSNMPTOOLSDIR}/libbsnmptools.a
|
||||
LIBAMUDIR= ${OBJTOP}/usr.sbin/amd/libamu
|
||||
LIBAMU?= ${LIBAMUDIR}/libamu.a
|
||||
|
||||
LIBBE?= ${LIBBEDIR}/libbe.a
|
||||
|
||||
# Define a directory for each library. This is useful for adding -L in when
|
||||
# not using a --sysroot or for meta mode bootstrapping when there is no
|
||||
# Makefile.depend. These are sorted by directory.
|
||||
|
@ -1330,7 +1330,7 @@ OLD_FILES+=usr/bin/ztest
|
||||
OLD_FILES+=usr/lib/libbe.a
|
||||
OLD_FILES+=usr/lib/libbe_p.a
|
||||
OLD_FILES+=usr/lib/libbe.so
|
||||
OLD_LIBS+=usr/lib/libbe.so.1
|
||||
OLD_LIBS+=lib/libbe.so.1
|
||||
OLD_FILES+=usr/lib/libzfs.a
|
||||
OLD_LIBS+=usr/lib/libzfs.so
|
||||
OLD_FILES+=usr/lib/libzfs_core.a
|
||||
|
Loading…
x
Reference in New Issue
Block a user