[lxc-devel] [PATCH 3/3] destroy: implement in the api
Serge Hallyn
serge.hallyn at ubuntu.com
Fri May 17 19:19:09 UTC 2013
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.hallyn at 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.hallyn at 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
More information about the lxc-devel
mailing list