Trying to cage the beast that is lxccontainer.c. Signed-off-by: Tycho Andersen <tycho.ander...@canonical.com> --- src/lxc/Makefile.am | 4 +- src/lxc/criu.c | 477 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lxc/criu.h | 70 ++++++++ src/lxc/lxccontainer.c | 454 +--------------------------------------------- 4 files changed, 551 insertions(+), 454 deletions(-) create mode 100644 src/lxc/criu.c create mode 100644 src/lxc/criu.h
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index d8e460b..2731843 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -21,7 +21,8 @@ noinst_HEADERS = \ namespace.h \ start.h \ state.h \ - utils.h + utils.h \ + criu.h if IS_BIONIC noinst_HEADERS += \ @@ -75,6 +76,7 @@ liblxc_so_SOURCES = \ state.c state.h \ log.c log.h \ attach.c attach.h \ + criu.c criu.h \ \ network.c network.h \ nl.c nl.h \ diff --git a/src/lxc/criu.c b/src/lxc/criu.c new file mode 100644 index 0000000..043db36 --- /dev/null +++ b/src/lxc/criu.c @@ -0,0 +1,477 @@ +/* + * lxc: linux Container library + * + * Copyright © 2014-2015 Canonical Ltd. + * + * Authors: + * Tycho Andersen <tycho.ander...@canonical.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#define _GNU_SOURCE +#include <assert.h> +#include <linux/limits.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "config.h" + +#include "bdev.h" +#include "cgroup.h" +#include "conf.h" +#include "criu.h" +#include "log.h" +#include "lxc.h" +#include "lxclock.h" +#include "network.h" +#include "utils.h" + +lxc_log_define(lxc_criu, lxc); + +void exec_criu(struct criu_opts *opts) +{ + char **argv, log[PATH_MAX]; + int static_args = 18, argc = 0, i, ret; + int netnr = 0; + struct lxc_list *it; + + char buf[4096]; + FILE *mnts = NULL; + + /* The command line always looks like: + * criu $(action) --tcp-established --file-locks --link-remap --force-irmap \ + * --manage-cgroups action-script foo.sh -D $(directory) \ + * -o $(directory)/$(action).log --ext-mount-map auto + * --enable-external-sharing --enable-external-masters + * +1 for final NULL */ + + if (strcmp(opts->action, "dump") == 0) { + /* -t pid */ + static_args += 2; + + /* --leave-running */ + if (!opts->stop) + static_args++; + } else if (strcmp(opts->action, "restore") == 0) { + /* --root $(lxc_mount_point) --restore-detached + * --restore-sibling --pidfile $foo --cgroup-root $foo */ + static_args += 8; + } else { + return; + } + + if (opts->verbose) + static_args++; + + ret = snprintf(log, PATH_MAX, "%s/%s.log", opts->directory, opts->action); + if (ret < 0 || ret >= PATH_MAX) { + ERROR("logfile name too long\n"); + return; + } + + argv = malloc(static_args * sizeof(*argv)); + if (!argv) + return; + + memset(argv, 0, static_args * sizeof(*argv)); + +#define DECLARE_ARG(arg) \ + do { \ + if (arg == NULL) { \ + ERROR("Got NULL argument for criu"); \ + goto err; \ + } \ + argv[argc++] = strdup(arg); \ + if (!argv[argc-1]) \ + goto err; \ + } while (0) + + argv[argc++] = on_path("criu", NULL); + if (!argv[argc-1]) { + ERROR("Couldn't find criu binary\n"); + goto err; + } + + DECLARE_ARG(opts->action); + DECLARE_ARG("--tcp-established"); + DECLARE_ARG("--file-locks"); + DECLARE_ARG("--link-remap"); + DECLARE_ARG("--force-irmap"); + DECLARE_ARG("--manage-cgroups"); + DECLARE_ARG("--ext-mount-map"); + DECLARE_ARG("auto"); + DECLARE_ARG("--enable-external-sharing"); + DECLARE_ARG("--enable-external-masters"); + DECLARE_ARG("-D"); + DECLARE_ARG(opts->directory); + DECLARE_ARG("-o"); + DECLARE_ARG(log); + + if (opts->verbose) + DECLARE_ARG("-vvvvvv"); + + if (strcmp(opts->action, "dump") == 0) { + char pid[32]; + + if (sprintf(pid, "%d", opts->c->init_pid(opts->c)) < 0) + goto err; + + DECLARE_ARG("-t"); + DECLARE_ARG(pid); + if (!opts->stop) + DECLARE_ARG("--leave-running"); + } else if (strcmp(opts->action, "restore") == 0) { + void *m; + int additional; + + DECLARE_ARG("--root"); + DECLARE_ARG(opts->c->lxc_conf->rootfs.mount); + DECLARE_ARG("--restore-detached"); + DECLARE_ARG("--restore-sibling"); + DECLARE_ARG("--pidfile"); + DECLARE_ARG(opts->pidfile); + DECLARE_ARG("--cgroup-root"); + DECLARE_ARG(opts->cgroup_path); + + additional = lxc_list_len(&opts->c->lxc_conf->network) * 2; + + m = realloc(argv, (argc + additional + 1) * sizeof(*argv)); \ + if (!m) \ + goto err; \ + argv = m; + + lxc_list_for_each(it, &opts->c->lxc_conf->network) { + char eth[128], *veth; + struct lxc_netdev *n = it->elem; + + if (n->name) { + if (strlen(n->name) >= sizeof(eth)) + goto err; + strncpy(eth, n->name, sizeof(eth)); + } else + sprintf(eth, "eth%d", netnr); + + veth = n->priv.veth_attr.pair; + + ret = snprintf(buf, sizeof(buf), "%s=%s@%s", eth, veth, n->link); + if (ret < 0 || ret >= sizeof(buf)) + goto err; + + DECLARE_ARG("--veth-pair"); + DECLARE_ARG(buf); + } + + } + + argv[argc] = NULL; + +#undef DECLARE_ARG + execv(argv[0], argv); +err: + if (mnts) + fclose(mnts); + for (i = 0; argv[i]; i++) + free(argv[i]); + free(argv); +} + +/* Check and make sure the container has a configuration that we know CRIU can + * dump. */ +bool criu_ok(struct lxc_container *c) +{ + struct lxc_list *it; + bool found_deny_rule = false; + + if (geteuid()) { + ERROR("Must be root to checkpoint\n"); + return false; + } + + /* We only know how to restore containers with veth networks. */ + lxc_list_for_each(it, &c->lxc_conf->network) { + struct lxc_netdev *n = it->elem; + if (n->type != LXC_NET_VETH && n->type != LXC_NET_NONE) { + ERROR("Found network that is not VETH or NONE\n"); + return false; + } + } + + // These requirements come from http://criu.org/LXC + if (c->lxc_conf->console.path && + strcmp(c->lxc_conf->console.path, "none") != 0) { + ERROR("lxc.console must be none\n"); + return false; + } + + if (c->lxc_conf->tty != 0) { + ERROR("lxc.tty must be 0\n"); + return false; + } + + lxc_list_for_each(it, &c->lxc_conf->cgroup) { + struct lxc_cgroup *cg = it->elem; + if (strcmp(cg->subsystem, "devices.deny") == 0 && + strcmp(cg->value, "c 5:1 rwm") == 0) { + + found_deny_rule = true; + break; + } + } + + if (!found_deny_rule) { + ERROR("couldn't find devices.deny = c 5:1 rwm"); + return false; + } + + return true; +} + +bool dump_net_info(struct lxc_container *c, char *directory) +{ + int netnr; + struct lxc_list *it; + + netnr = 0; + lxc_list_for_each(it, &c->lxc_conf->network) { + char *veth = NULL, *bridge = NULL, veth_path[PATH_MAX], eth[128]; + struct lxc_netdev *n = it->elem; + bool has_error = true; + int pret; + + pret = snprintf(veth_path, PATH_MAX, "lxc.network.%d.veth.pair", netnr); + if (pret < 0 || pret >= PATH_MAX) + goto out; + + veth = c->get_running_config_item(c, veth_path); + if (!veth) { + /* criu_ok() checks that all interfaces are + * LXC_NET{VETH,NONE}, and VETHs should have this + * config */ + assert(n->type == LXC_NET_NONE); + break; + } + + bridge = c->get_running_config_item(c, veth_path); + if (!bridge) + goto out; + + pret = snprintf(veth_path, PATH_MAX, "%s/veth%d", directory, netnr); + if (pret < 0 || pret >= PATH_MAX || print_to_file(veth_path, veth) < 0) + goto out; + + if (n->name) { + if (strlen(n->name) >= 128) + goto out; + strncpy(eth, n->name, 128); + } else + sprintf(eth, "eth%d", netnr); + + has_error = false; +out: + free(veth); + free(bridge); + if (has_error) + return false; + } + + return true; +} + +static bool restore_net_info(struct lxc_container *c) +{ + struct lxc_list *it; + bool has_error = true; + + if (container_mem_lock(c)) + return false; + + lxc_list_for_each(it, &c->lxc_conf->network) { + struct lxc_netdev *netdev = it->elem; + char template[IFNAMSIZ]; + snprintf(template, sizeof(template), "vethXXXXXX"); + + if (!netdev->priv.veth_attr.pair) + netdev->priv.veth_attr.pair = lxc_mkifname(template); + + if (!netdev->priv.veth_attr.pair) + goto out_unlock; + } + + has_error = false; + +out_unlock: + container_mem_unlock(c); + return !has_error; +} + +void do_restore(struct lxc_container *c, int pipe, char *directory, bool verbose) +{ + pid_t pid; + char pidfile[L_tmpnam]; + struct lxc_handler *handler; + int status; + + if (!tmpnam(pidfile)) + goto out; + + handler = lxc_init(c->name, c->lxc_conf, c->config_path); + if (!handler) + goto out; + + if (!cgroup_init(handler)) { + ERROR("failed initing cgroups"); + goto out_fini_handler; + } + + if (!cgroup_create(handler)) { + ERROR("failed creating groups"); + goto out_fini_handler; + } + + if (!restore_net_info(c)) { + ERROR("failed restoring network info"); + goto out_fini_handler; + } + + resolve_clone_flags(handler); + + pid = fork(); + if (pid < 0) + goto out_fini_handler; + + if (pid == 0) { + struct criu_opts os; + struct lxc_rootfs *rootfs; + + close(pipe); + pipe = -1; + + if (unshare(CLONE_NEWNS)) + goto out_fini_handler; + + /* CRIU needs the lxc root bind mounted so that it is the root of some + * mount. */ + rootfs = &c->lxc_conf->rootfs; + + if (rootfs_is_blockdev(c->lxc_conf)) { + if (do_rootfs_setup(c->lxc_conf, c->name, c->config_path) < 0) + goto out_fini_handler; + } else { + if (mkdir(rootfs->mount, 0755) < 0 && errno != EEXIST) + goto out_fini_handler; + + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { + SYSERROR("remount / to private failed"); + goto out_fini_handler; + } + + if (mount(rootfs->path, rootfs->mount, NULL, MS_BIND, NULL) < 0) { + rmdir(rootfs->mount); + goto out_fini_handler; + } + } + + os.action = "restore"; + os.directory = directory; + os.c = c; + os.pidfile = pidfile; + os.verbose = verbose; + os.cgroup_path = cgroup_canonical_path(handler); + + /* exec_criu() returning is an error */ + exec_criu(&os); + umount(rootfs->mount); + rmdir(rootfs->mount); + goto out_fini_handler; + } else { + int ret; + char title[2048]; + + pid_t w = waitpid(pid, &status, 0); + if (w == -1) { + SYSERROR("waitpid"); + goto out_fini_handler; + } + + ret = write(pipe, &status, sizeof(status)); + close(pipe); + pipe = -1; + + if (sizeof(status) != ret) { + SYSERROR("failed to write all of status"); + goto out_fini_handler; + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) { + goto out_fini_handler; + } else { + int ret; + FILE *f = fopen(pidfile, "r"); + if (!f) { + SYSERROR("couldn't read restore's init pidfile %s\n", pidfile); + goto out_fini_handler; + } + + ret = fscanf(f, "%d", (int*) &handler->pid); + fclose(f); + if (ret != 1) { + ERROR("reading restore pid failed"); + goto out_fini_handler; + } + + if (lxc_set_state(c->name, handler, RUNNING)) + goto out_fini_handler; + } + } else { + ERROR("CRIU was killed with signal %d\n", WTERMSIG(status)); + goto out_fini_handler; + } + + /* + * See comment in lxcapi_start; we don't care if these + * fail because it's just a beauty thing. We just + * assign the return here to silence potential. + */ + ret = snprintf(title, sizeof(title), "[lxc monitor] %s %s", c->config_path, c->name); + ret = setproctitle(title); + + ret = lxc_poll(c->name, handler); + if (ret) + lxc_abort(c->name, handler); + lxc_fini(c->name, handler); + exit(ret); + } + +out_fini_handler: + lxc_fini(c->name, handler); + +out: + if (pipe >= 0) { + status = 1; + if (write(pipe, &status, sizeof(status)) != sizeof(status)) { + SYSERROR("writing status failed"); + } + close(pipe); + } + + exit(1); +} diff --git a/src/lxc/criu.h b/src/lxc/criu.h new file mode 100644 index 0000000..1f65e47 --- /dev/null +++ b/src/lxc/criu.h @@ -0,0 +1,70 @@ +/* + * lxc: linux Container library + * + * Copyright © 2014-2015 Canonical Ltd. + * + * Authors: + * Tycho Andersen <tycho.ander...@canonical.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __LXC_CRIU_H +#define __LXC_CRIU_H + +#include <stdbool.h> + +#include <lxc/lxccontainer.h> + +// We require either the criu major/minor version, or the criu GITID if criu +// was built from git. +#define CRIU_VERSION "1.6" + +#define CRIU_GITID_VERSION "1.5" +#define CRIU_GITID_PATCHLEVEL 133 + +struct criu_opts { + /* The type of criu invocation, one of "dump" or "restore" */ + char *action; + + /* The directory to pass to criu */ + char *directory; + + /* The container to dump */ + struct lxc_container *c; + + /* Enable criu verbose mode? */ + bool verbose; + + /* dump: stop the container or not after dumping? */ + bool stop; + + /* restore: the file to write the init process' pid into */ + char *pidfile; + const char *cgroup_path; +}; + +void exec_criu(struct criu_opts *opts); + +/* Check and make sure the container has a configuration that we know CRIU can + * dump. */ +bool criu_ok(struct lxc_container *c); + +bool dump_net_info(struct lxc_container *c, char *directory); + +// do_restore never returns, the calling process is used as the +// monitor process. do_restore calls exit() if it fails. +void do_restore(struct lxc_container *c, int pipe, char *directory, bool verbose); + +#endif diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index d6826ab..0ca5b9f 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -50,6 +50,7 @@ #include "console.h" #include "cgroup.h" #include "commands.h" +#include "criu.h" #include "log.h" #include "bdev.h" #include "utils.h" @@ -3496,276 +3497,6 @@ static bool lxcapi_detach_interface(struct lxc_container *c, const char *ifname, return true; } -struct criu_opts { - /* The type of criu invocation, one of "dump" or "restore" */ - char *action; - - /* The directory to pass to criu */ - char *directory; - - /* The container to dump */ - struct lxc_container *c; - - /* Enable criu verbose mode? */ - bool verbose; - - /* dump: stop the container or not after dumping? */ - bool stop; - - /* restore: the file to write the init process' pid into */ - char *pidfile; - const char *cgroup_path; -}; - -static void exec_criu(struct criu_opts *opts) -{ - char **argv, log[PATH_MAX]; - int static_args = 18, argc = 0, i, ret; - int netnr = 0; - struct lxc_list *it; - - char buf[4096]; - FILE *mnts = NULL; - - /* The command line always looks like: - * criu $(action) --tcp-established --file-locks --link-remap --force-irmap \ - * --manage-cgroups action-script foo.sh -D $(directory) \ - * -o $(directory)/$(action).log --ext-mount-map auto - * --enable-external-sharing --enable-external-masters - * +1 for final NULL */ - - if (strcmp(opts->action, "dump") == 0) { - /* -t pid */ - static_args += 2; - - /* --leave-running */ - if (!opts->stop) - static_args++; - } else if (strcmp(opts->action, "restore") == 0) { - /* --root $(lxc_mount_point) --restore-detached - * --restore-sibling --pidfile $foo --cgroup-root $foo */ - static_args += 8; - } else { - return; - } - - if (opts->verbose) - static_args++; - - ret = snprintf(log, PATH_MAX, "%s/%s.log", opts->directory, opts->action); - if (ret < 0 || ret >= PATH_MAX) { - ERROR("logfile name too long\n"); - return; - } - - argv = malloc(static_args * sizeof(*argv)); - if (!argv) - return; - - memset(argv, 0, static_args * sizeof(*argv)); - -#define DECLARE_ARG(arg) \ - do { \ - if (arg == NULL) { \ - ERROR("Got NULL argument for criu"); \ - goto err; \ - } \ - argv[argc++] = strdup(arg); \ - if (!argv[argc-1]) \ - goto err; \ - } while (0) - - argv[argc++] = on_path("criu", NULL); - if (!argv[argc-1]) { - ERROR("Couldn't find criu binary\n"); - goto err; - } - - DECLARE_ARG(opts->action); - DECLARE_ARG("--tcp-established"); - DECLARE_ARG("--file-locks"); - DECLARE_ARG("--link-remap"); - DECLARE_ARG("--force-irmap"); - DECLARE_ARG("--manage-cgroups"); - DECLARE_ARG("--ext-mount-map"); - DECLARE_ARG("auto"); - DECLARE_ARG("--enable-external-sharing"); - DECLARE_ARG("--enable-external-masters"); - DECLARE_ARG("-D"); - DECLARE_ARG(opts->directory); - DECLARE_ARG("-o"); - DECLARE_ARG(log); - - if (opts->verbose) - DECLARE_ARG("-vvvvvv"); - - if (strcmp(opts->action, "dump") == 0) { - char pid[32]; - - if (sprintf(pid, "%d", lxcapi_init_pid(opts->c)) < 0) - goto err; - - DECLARE_ARG("-t"); - DECLARE_ARG(pid); - if (!opts->stop) - DECLARE_ARG("--leave-running"); - } else if (strcmp(opts->action, "restore") == 0) { - void *m; - int additional; - - DECLARE_ARG("--root"); - DECLARE_ARG(opts->c->lxc_conf->rootfs.mount); - DECLARE_ARG("--restore-detached"); - DECLARE_ARG("--restore-sibling"); - DECLARE_ARG("--pidfile"); - DECLARE_ARG(opts->pidfile); - DECLARE_ARG("--cgroup-root"); - DECLARE_ARG(opts->cgroup_path); - - additional = lxc_list_len(&opts->c->lxc_conf->network) * 2; - - m = realloc(argv, (argc + additional + 1) * sizeof(*argv)); \ - if (!m) \ - goto err; \ - argv = m; - - lxc_list_for_each(it, &opts->c->lxc_conf->network) { - char eth[128], *veth; - struct lxc_netdev *n = it->elem; - - if (n->name) { - if (strlen(n->name) >= sizeof(eth)) - goto err; - strncpy(eth, n->name, sizeof(eth)); - } else - sprintf(eth, "eth%d", netnr); - - veth = n->priv.veth_attr.pair; - - ret = snprintf(buf, sizeof(buf), "%s=%s@%s", eth, veth, n->link); - if (ret < 0 || ret >= sizeof(buf)) - goto err; - - DECLARE_ARG("--veth-pair"); - DECLARE_ARG(buf); - } - - } - - argv[argc] = NULL; - -#undef DECLARE_ARG - execv(argv[0], argv); -err: - if (mnts) - fclose(mnts); - for (i = 0; argv[i]; i++) - free(argv[i]); - free(argv); -} - -/* Check and make sure the container has a configuration that we know CRIU can - * dump. */ -static bool criu_ok(struct lxc_container *c) -{ - struct lxc_list *it; - bool found_deny_rule = false; - - if (geteuid()) { - ERROR("Must be root to checkpoint\n"); - return false; - } - - /* We only know how to restore containers with veth networks. */ - lxc_list_for_each(it, &c->lxc_conf->network) { - struct lxc_netdev *n = it->elem; - if (n->type != LXC_NET_VETH && n->type != LXC_NET_NONE) { - ERROR("Found network that is not VETH or NONE\n"); - return false; - } - } - - // These requirements come from http://criu.org/LXC - if (c->lxc_conf->console.path && - strcmp(c->lxc_conf->console.path, "none") != 0) { - ERROR("lxc.console must be none\n"); - return false; - } - - if (c->lxc_conf->tty != 0) { - ERROR("lxc.tty must be 0\n"); - return false; - } - - lxc_list_for_each(it, &c->lxc_conf->cgroup) { - struct lxc_cgroup *cg = it->elem; - if (strcmp(cg->subsystem, "devices.deny") == 0 && - strcmp(cg->value, "c 5:1 rwm") == 0) { - - found_deny_rule = true; - break; - } - } - - if (!found_deny_rule) { - ERROR("couldn't find devices.deny = c 5:1 rwm"); - return false; - } - - return true; -} - -static bool dump_net_info(struct lxc_container *c, char *directory) -{ - int netnr; - struct lxc_list *it; - - netnr = 0; - lxc_list_for_each(it, &c->lxc_conf->network) { - char *veth = NULL, *bridge = NULL, veth_path[PATH_MAX], eth[128]; - struct lxc_netdev *n = it->elem; - bool has_error = true; - int pret; - - pret = snprintf(veth_path, PATH_MAX, "lxc.network.%d.veth.pair", netnr); - if (pret < 0 || pret >= PATH_MAX) - goto out; - - veth = lxcapi_get_running_config_item(c, veth_path); - if (!veth) { - /* criu_ok() checks that all interfaces are - * LXC_NET{VETH,NONE}, and VETHs should have this - * config */ - assert(n->type == LXC_NET_NONE); - break; - } - - bridge = lxcapi_get_running_config_item(c, veth_path); - if (!bridge) - goto out; - - pret = snprintf(veth_path, PATH_MAX, "%s/veth%d", directory, netnr); - if (pret < 0 || pret >= PATH_MAX || print_to_file(veth_path, veth) < 0) - goto out; - - if (n->name) { - if (strlen(n->name) >= 128) - goto out; - strncpy(eth, n->name, 128); - } else - sprintf(eth, "eth%d", netnr); - - has_error = false; -out: - free(veth); - free(bridge); - if (has_error) - return false; - } - - return true; -} - static bool lxcapi_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose) { pid_t pid; @@ -3811,189 +3542,6 @@ static bool lxcapi_checkpoint(struct lxc_container *c, char *directory, bool sto } } -static bool restore_net_info(struct lxc_container *c) -{ - struct lxc_list *it; - bool has_error = true; - - if (container_mem_lock(c)) - return false; - - lxc_list_for_each(it, &c->lxc_conf->network) { - struct lxc_netdev *netdev = it->elem; - char template[IFNAMSIZ]; - snprintf(template, sizeof(template), "vethXXXXXX"); - - if (!netdev->priv.veth_attr.pair) - netdev->priv.veth_attr.pair = lxc_mkifname(template); - - if (!netdev->priv.veth_attr.pair) - goto out_unlock; - } - - has_error = false; - -out_unlock: - container_mem_unlock(c); - return !has_error; -} - -// do_restore never returns, the calling process is used as the -// monitor process. do_restore calls exit() if it fails. -static void do_restore(struct lxc_container *c, int pipe, char *directory, bool verbose) -{ - pid_t pid; - char pidfile[L_tmpnam]; - struct lxc_handler *handler; - int status; - - if (!tmpnam(pidfile)) - goto out; - - handler = lxc_init(c->name, c->lxc_conf, c->config_path); - if (!handler) - goto out; - - if (!cgroup_init(handler)) { - ERROR("failed initing cgroups"); - goto out_fini_handler; - } - - if (!cgroup_create(handler)) { - ERROR("failed creating groups"); - goto out_fini_handler; - } - - if (!restore_net_info(c)) { - ERROR("failed restoring network info"); - goto out_fini_handler; - } - - resolve_clone_flags(handler); - - pid = fork(); - if (pid < 0) - goto out_fini_handler; - - if (pid == 0) { - struct criu_opts os; - struct lxc_rootfs *rootfs; - - close(pipe); - pipe = -1; - - if (unshare(CLONE_NEWNS)) - goto out_fini_handler; - - /* CRIU needs the lxc root bind mounted so that it is the root of some - * mount. */ - rootfs = &c->lxc_conf->rootfs; - - if (rootfs_is_blockdev(c->lxc_conf)) { - if (do_rootfs_setup(c->lxc_conf, c->name, c->config_path) < 0) - goto out_fini_handler; - } else { - if (mkdir(rootfs->mount, 0755) < 0 && errno != EEXIST) - goto out_fini_handler; - - if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { - SYSERROR("remount / to private failed"); - goto out_fini_handler; - } - - if (mount(rootfs->path, rootfs->mount, NULL, MS_BIND, NULL) < 0) { - rmdir(rootfs->mount); - goto out_fini_handler; - } - } - - os.action = "restore"; - os.directory = directory; - os.c = c; - os.pidfile = pidfile; - os.verbose = verbose; - os.cgroup_path = cgroup_canonical_path(handler); - - /* exec_criu() returning is an error */ - exec_criu(&os); - umount(rootfs->mount); - rmdir(rootfs->mount); - goto out_fini_handler; - } else { - int ret; - char title[2048]; - - pid_t w = waitpid(pid, &status, 0); - if (w == -1) { - SYSERROR("waitpid"); - goto out_fini_handler; - } - - ret = write(pipe, &status, sizeof(status)); - close(pipe); - pipe = -1; - - if (sizeof(status) != ret) { - SYSERROR("failed to write all of status"); - goto out_fini_handler; - } - - if (WIFEXITED(status)) { - if (WEXITSTATUS(status)) { - goto out_fini_handler; - } else { - int ret; - FILE *f = fopen(pidfile, "r"); - if (!f) { - SYSERROR("couldn't read restore's init pidfile %s\n", pidfile); - goto out_fini_handler; - } - - ret = fscanf(f, "%d", (int*) &handler->pid); - fclose(f); - if (ret != 1) { - ERROR("reading restore pid failed"); - goto out_fini_handler; - } - - if (lxc_set_state(c->name, handler, RUNNING)) - goto out_fini_handler; - } - } else { - ERROR("CRIU was killed with signal %d\n", WTERMSIG(status)); - goto out_fini_handler; - } - - /* - * See comment in lxcapi_start; we don't care if these - * fail because it's just a beauty thing. We just - * assign the return here to silence potential. - */ - ret = snprintf(title, sizeof(title), "[lxc monitor] %s %s", c->config_path, c->name); - ret = setproctitle(title); - - ret = lxc_poll(c->name, handler); - if (ret) - lxc_abort(c->name, handler); - lxc_fini(c->name, handler); - exit(ret); - } - -out_fini_handler: - lxc_fini(c->name, handler); - -out: - if (pipe >= 0) { - status = 1; - if (write(pipe, &status, sizeof(status)) != sizeof(status)) { - SYSERROR("writing status failed"); - } - close(pipe); - } - - exit(1); -} - static bool lxcapi_restore(struct lxc_container *c, char *directory, bool verbose) { pid_t pid; -- 2.1.4 _______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel