This requires implementing bdev->ops->destroy() for each of the backing store types. Then implementing lxcapi_clone(), writing lxc_destroy.c using the api, and removing the lxc-destroy.in script.
Signed-off-by: Serge Hallyn <serge.hal...@ubuntu.com> --- configure.ac | 1 - src/lxc/Makefile.am | 7 +-- src/lxc/arguments.h | 3 ++ src/lxc/bdev.c | 129 ++++++++++++++++++++++++++++++++++++++++++++----- src/lxc/bdev.h | 1 + src/lxc/cgroup.c | 8 ++- src/lxc/lxc_destroy.c | 96 ++++++++++++++++++++++++++++++++++++ src/lxc/lxccontainer.c | 55 ++++++++++++++------- src/lxc/utils.c | 75 ++++++++++++++++++++++++++++ src/lxc/utils.h | 2 + 10 files changed, 338 insertions(+), 39 deletions(-) create mode 100644 src/lxc/lxc_destroy.c diff --git a/configure.ac b/configure.ac index 83d997b..2ea3550 100644 --- a/configure.ac +++ b/configure.ac @@ -384,7 +384,6 @@ AC_CONFIG_FILES([ src/lxc/lxc-version src/lxc/lxc-create src/lxc/lxc-start-ephemeral - src/lxc/lxc-destroy src/lxc/legacy/lxc-ls src/lxc/lxc.functions diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index ce79904..2d417b5 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -123,8 +123,7 @@ bin_SCRIPTS = \ lxc-netstat \ lxc-checkconfig \ lxc-version \ - lxc-create \ - lxc-destroy + lxc-create EXTRA_DIST = \ lxc-device \ @@ -162,7 +161,8 @@ bin_PROGRAMS = \ lxc-checkpoint \ lxc-restart \ lxc-kill \ - lxc-config + lxc-config \ + lxc-destroy pkglibexec_PROGRAMS = \ lxc-init @@ -181,6 +181,7 @@ lxc_cgroup_SOURCES = lxc_cgroup.c lxc_checkpoint_SOURCES = lxc_checkpoint.c lxc_config_SOURCES = lxc_config.c lxc_console_SOURCES = lxc_console.c +lxc_destroy_SOURCES = lxc_destroy.c lxc_execute_SOURCES = lxc_execute.c lxc_freeze_SOURCES = lxc_freeze.c lxc_info_SOURCES = lxc_info.c diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h index a0f0fc9..cca1fe8 100644 --- a/src/lxc/arguments.h +++ b/src/lxc/arguments.h @@ -67,6 +67,9 @@ struct lxc_arguments { int wait; int reboot; + /* for lxc-destroy */ + int force; + /* close fds from parent? */ int close_all_fds; diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index fcde16b..a4764a5 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -325,7 +325,7 @@ static int dir_detect(const char *path) // // XXXXXXX plain directory bind mount ops // -int dir_mount(struct bdev *bdev) +static int dir_mount(struct bdev *bdev) { if (strcmp(bdev->type, "dir")) return -22; @@ -334,7 +334,7 @@ int dir_mount(struct bdev *bdev) return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); } -int dir_umount(struct bdev *bdev) +static int dir_umount(struct bdev *bdev) { if (strcmp(bdev->type, "dir")) return -22; @@ -416,11 +416,19 @@ static int dir_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna return 0; } +static int dir_destroy(struct bdev *orig) +{ + if (!lxc_rmdir_onedev(orig->src)) + return -1; + return 0; +} + struct bdev_ops dir_ops = { .detect = &dir_detect, .mount = &dir_mount, .umount = &dir_umount, .clone_paths = &dir_clonepaths, + .destroy = &dir_destroy, }; @@ -435,7 +443,7 @@ struct bdev_ops dir_ops = { // sake of flexibility let's always bind-mount. // -static int zfs_list_entry(const char *path, char *output) +static int zfs_list_entry(const char *path, char *output, size_t inlen) { FILE *f; int found=0; @@ -444,7 +452,7 @@ static int zfs_list_entry(const char *path, char *output) SYSERROR("popen failed"); return 0; } - while (fgets(output, LXC_LOG_BUFFER_SIZE, f)) { + while (fgets(output, inlen, f)) { if (strstr(output, path)) { found = 1; break; @@ -464,12 +472,12 @@ static int zfs_detect(const char *path) ERROR("out of memory"); return 0; } - found = zfs_list_entry(path, output); + found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE); free(output); return found; } -int zfs_mount(struct bdev *bdev) +static int zfs_mount(struct bdev *bdev) { if (strcmp(bdev->type, "zfs")) return -22; @@ -478,7 +486,7 @@ int zfs_mount(struct bdev *bdev) return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); } -int zfs_umount(struct bdev *bdev) +static int zfs_umount(struct bdev *bdev) { if (strcmp(bdev->type, "zfs")) return -22; @@ -496,7 +504,7 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, int ret; pid_t pid; - if (zfs_list_entry(opath, output)) { + if (zfs_list_entry(opath, output, MAXPATHLEN)) { // zfsroot is output up to ' ' if ((p = index(output, ' ')) == NULL) return -1; @@ -596,11 +604,41 @@ static int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna return zfs_clone(orig->src, new->src, oldname, cname, lxcpath, snap); } +/* + * TODO: detect whether this was a clone, and if so then also delete the + * snapshot it was based on, so that we don't hold the original + * container busy. + */ +static int zfs_destroy(struct bdev *orig) +{ + pid_t pid; + char output[MAXPATHLEN], *p; + + if ((pid = fork()) < 0) + return -1; + if (pid) + return wait_for_pid(pid); + + if (!zfs_list_entry(orig->src, output, MAXPATHLEN)) { + ERROR("Error: zfs entry for %s not found", orig->src); + return -1; + } + + // zfs mount is output up to ' ' + if ((p = index(output, ' ')) == NULL) + return -1; + *p = '\0'; + + execlp("zfs", "zfs", "destroy", output, NULL); + exit(1); +} + struct bdev_ops zfs_ops = { .detect = &zfs_detect, .mount = &zfs_mount, .umount = &zfs_umount, .clone_paths = &zfs_clonepaths, + .destroy = &zfs_destroy, }; // @@ -828,11 +866,25 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna return 0; } +static int lvm_destroy(struct bdev *orig) +{ + pid_t pid; + + if ((pid = fork()) < 0) + return -1; + if (!pid) { + execlp("lvremove", "lvremove", "-f", orig->src, NULL); + exit(1); + } + return wait_for_pid(pid); +} + struct bdev_ops lvm_ops = { .detect = &lvm_detect, .mount = &lvm_mount, .umount = &lvm_umount, .clone_paths = &lvm_clonepaths, + .destroy = &lvm_destroy, }; // @@ -884,7 +936,7 @@ static int btrfs_detect(const char *path) return 0; } -int btrfs_mount(struct bdev *bdev) +static int btrfs_mount(struct bdev *bdev) { if (strcmp(bdev->type, "btrfs")) return -22; @@ -893,7 +945,7 @@ int btrfs_mount(struct bdev *bdev) return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); } -int btrfs_umount(struct bdev *bdev) +static int btrfs_umount(struct bdev *bdev) { if (strcmp(bdev->type, "btrfs")) return -22; @@ -917,6 +969,8 @@ struct btrfs_ioctl_vol_args { struct btrfs_ioctl_vol_args_v2) #define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ struct btrfs_ioctl_vol_args) +#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ + struct btrfs_ioctl_vol_args) #define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0) @@ -1061,11 +1115,48 @@ static int btrfs_clonepaths(struct bdev *orig, struct bdev *new, const char *old return btrfs_subvolume_create(new->dest); } +static int btrfs_destroy(struct bdev *orig) +{ + int ret, fd = -1; + struct btrfs_ioctl_vol_args args; + char *path = orig->src; + char *p, *newfull = strdup(path); + + if (!newfull) { + ERROR("Error: out of memory"); + return -1; + } + + p = rindex(newfull, '/'); + if (!p) { + ERROR("bad path: %s", path); + return -1; + } + *p = '\0'; + + if ((fd = open(newfull, O_RDONLY)) < 0) { + ERROR("Error opening %s", newfull); + free(newfull); + return -1; + } + + memset(&args, 0, sizeof(args)); + strncpy(args.name, p+1, BTRFS_SUBVOL_NAME_MAX); + args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0; + ret = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); + INFO("btrfs: snapshot create ioctl returned %d", ret); + + free(newfull); + close(fd); + return ret; +} + struct bdev_ops btrfs_ops = { .detect = &btrfs_detect, .mount = &btrfs_mount, .umount = &btrfs_umount, .clone_paths = &btrfs_clonepaths, + .destroy = &btrfs_destroy, }; // @@ -1082,7 +1173,7 @@ static int overlayfs_detect(const char *path) // // XXXXXXX plain directory bind mount ops // -int overlayfs_mount(struct bdev *bdev) +static int overlayfs_mount(struct bdev *bdev) { char *options, *dup, *lower, *upper; int len; @@ -1121,7 +1212,7 @@ int overlayfs_mount(struct bdev *bdev) return ret; } -int overlayfs_umount(struct bdev *bdev) +static int overlayfs_umount(struct bdev *bdev) { if (strcmp(bdev->type, "overlayfs")) return -22; @@ -1230,11 +1321,25 @@ static int overlayfs_clonepaths(struct bdev *orig, struct bdev *new, const char return 0; } +int overlayfs_destroy(struct bdev *orig) +{ + char *upper; + + if (strncmp(orig->src, "overlayfs:", 10) != 0) + return -22; + upper = index(orig->src + 10, ':'); + if (!upper) + return -22; + upper++; + return lxc_rmdir_onedev(upper); +} + struct bdev_ops overlayfs_ops = { .detect = &overlayfs_detect, .mount = &overlayfs_mount, .umount = &overlayfs_umount, .clone_paths = &overlayfs_clonepaths, + .destroy = &overlayfs_destroy, }; struct bdev_type bdevs[] = { diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h index cc03592..9c2f9f9 100644 --- a/src/lxc/bdev.h +++ b/src/lxc/bdev.h @@ -16,6 +16,7 @@ struct bdev_ops { // mount requires src and dest to be set. int (*mount)(struct bdev *bdev); int (*umount)(struct bdev *bdev); + int (*destroy)(struct bdev *bdev); /* given original mount, rename the paths for cloned container */ int (*clone_paths)(struct bdev *orig, struct bdev *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index a8ae8c1..0fac0d8 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -816,7 +816,7 @@ out: return retv; } -int recursive_rmdir(char *dirname) +static int cgroup_rmdir(char *dirname) { struct dirent dirent, *direntp; DIR *dir; @@ -849,7 +849,7 @@ int recursive_rmdir(char *dirname) if (ret) continue; if (S_ISDIR(mystat.st_mode)) - recursive_rmdir(pathname); + cgroup_rmdir(pathname); } ret = rmdir(dirname); @@ -857,8 +857,6 @@ int recursive_rmdir(char *dirname) if (closedir(dir)) ERROR("failed to close directory"); return ret; - - } static int lxc_one_cgroup_destroy(struct mntent *mntent, const char *cgpath) @@ -873,7 +871,7 @@ static int lxc_one_cgroup_destroy(struct mntent *mntent, const char *cgpath) return -1; } DEBUG("destroying %s\n", cgname); - if (recursive_rmdir(cgname)) { + if (cgroup_rmdir(cgname)) { SYSERROR("failed to remove cgroup '%s'", cgname); return -1; } diff --git a/src/lxc/lxc_destroy.c b/src/lxc/lxc_destroy.c new file mode 100644 index 0000000..87cadae --- /dev/null +++ b/src/lxc/lxc_destroy.c @@ -0,0 +1,96 @@ +/* + * + * Copyright © 2013 Serge Hallyn <serge.hal...@ubuntu.com>. + * Copyright © 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "../lxc/lxccontainer.h" + +#include <stdio.h> +#include <libgen.h> +#include <unistd.h> +#include <sys/types.h> + +#include <lxc/lxc.h> +#include <lxc/log.h> + +#include "arguments.h" +#include "utils.h" + +lxc_log_define(lxc_destroy, lxc); + +static int my_parser(struct lxc_arguments* args, int c, char* arg) +{ + switch (c) { + case 'f': args->force = 1; break; + } + return 0; +} + +static const struct option my_longopts[] = { + {"force", no_argument, 0, 'f'}, + LXC_COMMON_OPTIONS +}; + +static struct lxc_arguments my_args = { + .progname = "lxc-shutdown", + .help = "\ +--name=NAME [-f] [-P lxcpath]\n\ +\n\ +lxc-stop stops a container with the identifier NAME\n\ +\n\ +Options :\n\ + -n, --name=NAME NAME for name of the container\n\ + -f, --force wait for the container to shut down\n", + .options = my_longopts, + .parser = my_parser, + .checker = NULL, +}; + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + + /* this is a short term test. We'll probably want to check for + * write access to lxcpath instead */ + if (geteuid()) { + fprintf(stderr, "%s must be run as root\n", argv[0]); + exit(1); + } + + if (lxc_arguments_parse(&my_args, argc, argv)) + exit(1); + + if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, + my_args.progname, my_args.quiet, my_args.lxcpath[0])) + exit(1); + + c = lxc_container_new(my_args.name, my_args.lxcpath[0]); + if (!c) { + fprintf(stderr, "System error loading container\n"); + exit(1); + } + + if (c->is_running(c)) { + if (!my_args.force) { + fprintf(stderr, "%s is running\n", my_args.name); + exit(1); + } + c->stop(c); + } + + exit(c->destroy(c) ? 0 : 1); +} diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 2126c89..9f96109 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -222,7 +222,7 @@ static const char *lxcapi_state(struct lxc_container *c) return ret; } -static bool is_stopped_nolock(struct lxc_container *c) +static bool is_stopped_locked(struct lxc_container *c) { lxc_state_t s; s = lxc_getstate(c->name, c->config_path); @@ -850,32 +850,51 @@ static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file) return true; } +static const char *lxcapi_get_config_path(struct lxc_container *c); +// do we want the api to support --force, or leave that to the caller? static bool lxcapi_destroy(struct lxc_container *c) { - pid_t pid; - - if (!c) - return false; + struct bdev *r; + bool ret = false; /* container is already destroyed if we don't have a config and rootfs.path is not accessible */ - if (!lxcapi_is_defined(c) && (!c->lxc_conf || !c->lxc_conf->rootfs.path || access(c->lxc_conf->rootfs.path, F_OK) != 0)) + if (!c || !lxcapi_is_defined(c) || !c->lxc_conf || !c->lxc_conf->rootfs.path) return false; - pid = fork(); - if (pid < 0) + if (lxclock(c->privlock, 0)) + return false; + if (lxclock(c->slock, 0)) { + lxcunlock(c->privlock); return false; - if (pid == 0) { // child - execlp("lxc-destroy", "lxc-destroy", "-n", c->name, "-P", c->config_path, NULL); - perror("execl"); - exit(1); } - if (wait_for_pid(pid) < 0) { - ERROR("Error destroying container %s", c->name); - return false; + if (!is_stopped_locked(c)) { + // we should queue some sort of error - in c->error_string? + ERROR("container %s is not stopped", c->name); + goto out; } - return true; + r = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); + if (r) { + if (r->ops->destroy(r) < 0) { + ERROR("Error destroying rootfs for %s", c->name); + goto out; + } + } + + const char *p1 = lxcapi_get_config_path(c); + char *path = alloca(strlen(p1) + strlen(c->name) + 2); + sprintf(path, "%s/%s", p1, c->name); + if (lxc_rmdir_onedev(path) < 0) { + ERROR("Error destroying container directory for %s", c->name); + goto out; + } + ret = true; + +out: + lxcunlock(c->privlock); + lxcunlock(c->slock); + return ret; } static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v) @@ -1005,7 +1024,7 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, if (lxclock(c->privlock, 0)) return false; - if (is_stopped_nolock(c)) + if (is_stopped_locked(c)) goto err; ret = lxc_cgroup_set(c->name, subsys, value, c->config_path); @@ -1026,7 +1045,7 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c if (lxclock(c->privlock, 0)) return -1; - if (is_stopped_nolock(c)) + if (is_stopped_locked(c)) goto out; ret = lxc_cgroup_get(c->name, subsys, retv, inlen, c->config_path); diff --git a/src/lxc/utils.c b/src/lxc/utils.c index be1ce88..670607b 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -41,6 +41,81 @@ lxc_log_define(lxc_utils, lxc); +static int _recursive_rmdir_onedev(char *dirname, dev_t pdev) +{ + struct dirent dirent, *direntp; + DIR *dir; + int ret, failed=0; + char pathname[MAXPATHLEN]; + + dir = opendir(dirname); + if (!dir) { + ERROR("%s: failed to open %s", __func__, dirname); + return 0; + } + + while (!readdir_r(dir, &dirent, &direntp)) { + struct stat mystat; + int rc; + + if (!direntp) + break; + + if (!strcmp(direntp->d_name, ".") || + !strcmp(direntp->d_name, "..")) + continue; + + rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name); + if (rc < 0 || rc >= MAXPATHLEN) { + ERROR("pathname too long"); + failed=1; + continue; + } + ret = lstat(pathname, &mystat); + if (ret) { + ERROR("%s: failed to stat %s", __func__, pathname); + failed=1; + continue; + } + if (mystat.st_dev != pdev) + continue; + if (S_ISDIR(mystat.st_mode)) { + if (!_recursive_rmdir_onedev(pathname, pdev)) + failed=1; + } else { + if (unlink(pathname) < 0) { + ERROR("%s: failed to delete %s", __func__, pathname); + failed=1; + } + } + } + + if (rmdir(dirname) < 0) { + ERROR("%s: failed to delete %s", __func__, dirname); + failed=1; + } + + if (closedir(dir)) { + ERROR("%s: failed to close directory %s", __func__, dirname); + failed=1; + } + + return !failed; +} + +/* returns 1 on success, 0 if there were any failures */ +extern int lxc_rmdir_onedev(char *path) +{ + struct stat mystat; + + if (lstat(path, &mystat) < 0) { + ERROR("%s: failed to stat %s", __func__, path); + return 0; + } + + return _recursive_rmdir_onedev(path, mystat.st_dev); +} + static int mount_fs(const char *source, const char *target, const char *type) { /* the umount may fail */ diff --git a/src/lxc/utils.h b/src/lxc/utils.h index 09af34a..67fa36b 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -23,6 +23,8 @@ #ifndef _utils_h #define _utils_h +/* returns 1 on success, 0 if there were any failures */ +extern int lxc_rmdir_onedev(char *path); extern int lxc_setup_fs(void); extern int get_u16(unsigned short *val, const char *arg, int base); extern int mkdir_p(const char *dir, mode_t mode); -- 1.8.1.2 ------------------------------------------------------------------------------ AlienVault Unified Security Management (USM) platform delivers complete security visibility with the essential security capabilities. Easily and efficiently configure, manage, and operate all of your security controls from a single console and one unified framework. Download a free trial. http://p.sf.net/sfu/alienvault_d2d _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel