]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
destroy: implement in the api
authorSerge Hallyn <serge.hallyn@ubuntu.com>
Fri, 17 May 2013 05:20:10 +0000 (07:20 +0200)
committerSerge Hallyn <serge.hallyn@ubuntu.com>
Sat, 25 May 2013 04:37:04 +0000 (23:37 -0500)
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.

(this also has a few other cleanups, like marking some functions
static)

Changelog:
fold into destroy: fix zfs destroy
destroy: use correct program name in help

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
configure.ac
src/lxc/Makefile.am
src/lxc/arguments.h
src/lxc/bdev.c
src/lxc/bdev.h
src/lxc/cgroup.c
src/lxc/lxc_destroy.c [new file with mode: 0644]
src/lxc/lxccontainer.c
src/lxc/utils.c
src/lxc/utils.h

index 8979f75891e4b22edd1ba61f725e5b70f378a15a..372fc75cb7d6a72b2972071135b843241bbb84e2 100644 (file)
@@ -383,7 +383,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
 
index 578ee060bf8ee755173224004615aa6b07288a5a..6974b272e8c4c30f17b1badb77b1943e86679c33 100644 (file)
@@ -122,8 +122,7 @@ bin_SCRIPTS = \
        lxc-netstat \
        lxc-checkconfig \
        lxc-version \
-       lxc-create \
-       lxc-destroy
+       lxc-create
 
 EXTRA_DIST = \
        lxc-device \
@@ -160,7 +159,8 @@ bin_PROGRAMS = \
        lxc-checkpoint \
        lxc-restart \
        lxc-kill \
-       lxc-config
+       lxc-config \
+       lxc-destroy
 
 pkglibexec_PROGRAMS = \
        lxc-init
@@ -179,6 +179,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
index 145474d4448d581e2cc773532be4d4ad4c561759..37de4b5629cb4ed05c72a3b2171979844c655387 100644 (file)
@@ -69,6 +69,9 @@ struct lxc_arguments {
        int hardstop;
        int shutdown;
 
+       /* for lxc-destroy */
+       int force;
+
        /* close fds from parent? */
        int close_all_fds;
 
index a0ce5f52244135842f9d911e21fcb15a577611d6..6d61d2de459c297db06c81c8dfc30c470058ce6e 100644 (file)
@@ -312,7 +312,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;
@@ -321,7 +321,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;
@@ -403,11 +403,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,
 };
 
 
@@ -422,7 +430,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;
@@ -431,7 +439,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;
@@ -451,12 +459,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;
@@ -465,7 +473,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;
@@ -483,7 +491,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;
@@ -583,11 +591,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,
 };
 
 //
@@ -815,11 +853,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,
 };
 
 //
@@ -871,7 +923,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;
@@ -880,7 +932,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;
@@ -904,6 +956,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)
 
@@ -1048,11 +1102,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,
 };
 
 //
@@ -1069,7 +1160,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;
@@ -1108,7 +1199,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;
@@ -1217,11 +1308,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[] = {
index d69efd8a722f375bdc71785199a115df5281e5c4..e4f1e6080510f090e326bf94baaa0db8a9f4d245 100644 (file)
@@ -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,
index f04d59a38533d83533fd454d6291f31063a78746..c28e24974009530e02bcf5e73d7aed2563fc57fc 100644 (file)
@@ -784,7 +784,7 @@ out:
        return retv;
 }
 
-int recursive_rmdir(char *dirname)
+static int cgroup_rmdir(char *dirname)
 {
        struct dirent dirent, *direntp;
        DIR *dir;
@@ -817,7 +817,7 @@ int recursive_rmdir(char *dirname)
                if (ret)
                        continue;
                if (S_ISDIR(mystat.st_mode))
-                       recursive_rmdir(pathname);
+                       cgroup_rmdir(pathname);
        }
 
        ret = rmdir(dirname);
@@ -825,8 +825,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)
@@ -841,7 +839,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 (file)
index 0000000..686b303
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *
+ * Copyright © 2013 Serge Hallyn <serge.hallyn@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-destroy",
+       .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);
+}
index a1c156c64b2ac405a7a4fae89aabb8aa791f99d0..1d1174259059e7eac2d394f2d6cf8ec1eec61817 100644 (file)
@@ -301,7 +301,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);
@@ -1048,32 +1048,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)
@@ -1203,7 +1222,7 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys,
        if (container_mem_lock(c))
                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);
@@ -1224,7 +1243,7 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c
        if (container_mem_lock(c))
                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);
index cd35e00069b14e91c5ba539b8dc3ec5cbf8ec1f2..6c0f9d0651fc118a9d9c21e2d9bc4a42470bd70e 100644 (file)
 
 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 */
index fbfe5d3a04668d14cdd017d6fd85847f3e38d2d9..7d2bfa8bb7a413e2e20eacebc1f4cba89771c8a9 100644 (file)
@@ -27,6 +27,8 @@
 #include <sys/types.h>
 #include "config.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);