[lxc-devel] [PATCH 3/3] c/r: add a new ->migrate API call
Tycho Andersen
tycho.andersen at canonical.com
Wed Dec 2 21:30:54 UTC 2015
This patch adds a new ->migrate API call with three commands:
MIGRATE_DUMP: this is basically just ->checkpoint()
MIGRATE_RESTORE: this is just ->restore()
MIGRATE_PRE_DUMP: this can be used to invoke criu's pre-dump command on the
container.
A small addition to the (pre-)dump commands is the ability to specify a
previous partial dump directory, so that one can use a pre-dump of a
container.
Finally, this new API call uses a structure to pass options so that it can
be easily extended in the future (e.g. to CRIU's --leave-frozen option in
the future, for potentially smarter failure handling on restore).
Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
src/lxc/criu.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++--
src/lxc/criu.h | 9 ++-
src/lxc/lxccontainer.c | 140 ++++++++++++++++----------------------------
src/lxc/lxccontainer.h | 33 +++++++++++
4 files changed, 237 insertions(+), 100 deletions(-)
diff --git a/src/lxc/criu.c b/src/lxc/criu.c
index 695a763..c0ce965 100644
--- a/src/lxc/criu.c
+++ b/src/lxc/criu.c
@@ -64,12 +64,16 @@ void exec_criu(struct criu_opts *opts)
* --enable-fs hugetlbfs --enable-fs tracefs
* +1 for final NULL */
- if (strcmp(opts->action, "dump") == 0) {
+ if (strcmp(opts->action, "dump") == 0 || strcmp(opts->action, "pre-dump") == 0) {
/* -t pid --freeze-cgroup /lxc/ct */
static_args += 4;
- /* --leave-running */
- if (!opts->stop)
+ /* --prev-images-dir <path-to-directory-A-relative-to-B> */
+ if (opts->predump_dir)
+ static_args += 2;
+
+ /* --leave-running (only for final dump) */
+ if (strcmp(opts->action, "dump") == 0 && !opts->stop)
static_args++;
} else if (strcmp(opts->action, "restore") == 0) {
/* --root $(lxc_mount_point) --restore-detached
@@ -133,13 +137,12 @@ void exec_criu(struct criu_opts *opts)
if (opts->verbose)
DECLARE_ARG("-vvvvvv");
- if (strcmp(opts->action, "dump") == 0) {
+ if (strcmp(opts->action, "dump") == 0 || strcmp(opts->action, "pre-dump") == 0) {
char pid[32], *freezer_relative;
if (sprintf(pid, "%d", opts->c->init_pid(opts->c)) < 0)
goto err;
-
DECLARE_ARG("-t");
DECLARE_ARG(pid);
@@ -158,7 +161,13 @@ void exec_criu(struct criu_opts *opts)
DECLARE_ARG("--freeze-cgroup");
DECLARE_ARG(log);
- if (!opts->stop)
+ if (opts->predump_dir) {
+ DECLARE_ARG("--prev-images-dir");
+ DECLARE_ARG(opts->predump_dir);
+ }
+
+ /* only for final dump */
+ if (strcmp(opts->action, "dump") == 0 && !opts->stop)
DECLARE_ARG("--leave-running");
} else if (strcmp(opts->action, "restore") == 0) {
void *m;
@@ -402,6 +411,8 @@ out_unlock:
return !has_error;
}
+// 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)
{
pid_t pid;
@@ -560,3 +571,135 @@ out:
exit(1);
}
+
+/* do one of either predump or a regular dump */
+static bool do_dump(struct lxc_container *c, char *mode, char *directory,
+ bool stop, bool verbose, char *predump_dir)
+{
+ pid_t pid;
+
+ if (!criu_ok(c))
+ return false;
+
+ if (mkdir_p(directory, 0700) < 0)
+ return false;
+
+ pid = fork();
+ if (pid < 0) {
+ SYSERROR("fork failed");
+ return false;
+ }
+
+ if (pid == 0) {
+ struct criu_opts os;
+
+ os.action = mode;
+ os.directory = directory;
+ os.c = c;
+ os.stop = stop;
+ os.verbose = verbose;
+ os.predump_dir = predump_dir;
+
+ /* exec_criu() returning is an error */
+ exec_criu(&os);
+ exit(1);
+ } else {
+ int status;
+ pid_t w = waitpid(pid, &status, 0);
+ if (w == -1) {
+ SYSERROR("waitpid");
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status)) {
+ ERROR("dump failed with %d\n", WEXITSTATUS(status));
+ return false;
+ }
+
+ return true;
+ } else if (WIFSIGNALED(status)) {
+ ERROR("dump signaled with %d\n", WTERMSIG(status));
+ return false;
+ } else {
+ ERROR("unknown dump exit %d\n", status);
+ return false;
+ }
+ }
+}
+
+bool pre_dump(struct lxc_container *c, char *directory, bool verbose, char *predump_dir)
+{
+ return do_dump(c, "pre-dump", directory, false, verbose, predump_dir);
+}
+
+bool dump(struct lxc_container *c, char *directory, bool stop, bool verbose, char *predump_dir)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ ret = snprintf(path, sizeof(path), "%s/inventory.img", directory);
+ if (ret < 0 || ret >= sizeof(path))
+ return false;
+
+ if (access(path, F_OK) == 0) {
+ ERROR("please use a fresh directory for the dump directory\n");
+ return false;
+ }
+
+ return do_dump(c, "dump", directory, stop, verbose, predump_dir);
+}
+
+bool restore(struct lxc_container *c, char *directory, bool verbose)
+{
+ pid_t pid;
+ int status, nread;
+ int pipefd[2];
+
+ if (!criu_ok(c))
+ return false;
+
+ if (geteuid()) {
+ ERROR("Must be root to restore\n");
+ return false;
+ }
+
+ if (pipe(pipefd)) {
+ ERROR("failed to create pipe");
+ return false;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
+ }
+
+ if (pid == 0) {
+ close(pipefd[0]);
+ // this never returns
+ do_restore(c, pipefd[1], directory, verbose);
+ }
+
+ close(pipefd[1]);
+
+ nread = read(pipefd[0], &status, sizeof(status));
+ close(pipefd[0]);
+ if (sizeof(status) != nread) {
+ ERROR("reading status from pipe failed");
+ goto err_wait;
+ }
+
+ // If the criu process was killed or exited nonzero, wait() for the
+ // handler, since the restore process died. Otherwise, we don't need to
+ // wait, since the child becomes the monitor process.
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ goto err_wait;
+ return true;
+
+err_wait:
+ if (wait_for_pid(pid))
+ ERROR("restore process died");
+ return false;
+}
diff --git a/src/lxc/criu.h b/src/lxc/criu.h
index 9714d17..b7d241b 100644
--- a/src/lxc/criu.h
+++ b/src/lxc/criu.h
@@ -47,6 +47,9 @@ struct criu_opts {
/* Enable criu verbose mode? */
bool verbose;
+ /* (pre-)dump: a directory for the previous dump's images */
+ char *predump_dir;
+
/* dump: stop the container or not after dumping? */
bool stop;
@@ -61,8 +64,8 @@ void exec_criu(struct criu_opts *opts);
* dump. */
bool criu_ok(struct lxc_container *c);
-// 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);
+bool pre_dump(struct lxc_container *c, char *directory, bool verbose, char *predump_dir);
+bool dump(struct lxc_container *c, char *directory, bool stop, bool verbose, char *predump_dir);
+bool restore(struct lxc_container *c, char *directory, bool verbose);
#endif
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 613e894..c496b80 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -3992,112 +3992,69 @@ static bool do_lxcapi_detach_interface(struct lxc_container *c, const char *ifna
WRAP_API_2(bool, lxcapi_detach_interface, const char *, const char *)
-static bool do_lxcapi_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose)
+static int do_lxcapi_migrate(struct lxc_container *c, unsigned int cmd,
+ struct migrate_opts *opts, unsigned int size)
{
- pid_t pid;
- int status;
- char path[PATH_MAX];
-
- if (!criu_ok(c))
- return false;
-
- if (mkdir(directory, 0700) < 0 && errno != EEXIST)
- return false;
-
- status = snprintf(path, sizeof(path), "%s/inventory.img", directory);
- if (status < 0 || status >= sizeof(path))
- return false;
+ int ret;
- if (access(path, F_OK) == 0) {
- ERROR("please use a fresh directory for the dump directory\n");
- return false;
+ /* If the caller has a bigger (newer) struct migrate_opts, let's make
+ * sure that the stuff on the end is zero, i.e. that they didn't ask us
+ * to do anything special.
+ */
+ if (size > sizeof(*opts)) {
+ unsigned char *addr;
+ unsigned char *end;
+
+ addr = (void *)opts + sizeof(*opts);
+ end = (void *)opts + size;
+ for (; addr < end; addr++) {
+ if (*addr) {
+ return -E2BIG;
+ }
+ }
}
- pid = fork();
- if (pid < 0)
- return false;
-
- if (pid == 0) {
- struct criu_opts os;
+ switch (cmd) {
+ case MIGRATE_PRE_DUMP:
+ ret = !pre_dump(c, opts->directory, opts->verbose, opts->predump_dir);
+ break;
+ case MIGRATE_DUMP:
+ ret = !dump(c, opts->directory, opts->stop, opts->verbose, opts->predump_dir);
+ break;
+ case MIGRATE_RESTORE:
+ ret = !restore(c, opts->directory, opts->verbose);
+ break;
+ default:
+ ERROR("invalid migrate command %u", cmd);
+ ret = -EINVAL;
+ }
- os.action = "dump";
- os.directory = directory;
- os.c = c;
- os.stop = stop;
- os.verbose = verbose;
+ return ret;
+}
- /* exec_criu() returning is an error */
- exec_criu(&os);
- exit(1);
- } else {
- pid_t w = waitpid(pid, &status, 0);
- if (w == -1) {
- SYSERROR("waitpid");
- return false;
- }
+WRAP_API_3(int, lxcapi_migrate, unsigned int, struct migrate_opts *, unsigned int)
- if (WIFEXITED(status)) {
- return !WEXITSTATUS(status);
- }
+static bool do_lxcapi_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose)
+{
+ struct migrate_opts opts = {
+ .directory = directory,
+ .stop = stop,
+ .verbose = verbose,
+ };
- return false;
- }
+ return do_lxcapi_migrate(c, MIGRATE_DUMP, &opts, sizeof(opts));
}
WRAP_API_3(bool, lxcapi_checkpoint, char *, bool, bool)
static bool do_lxcapi_restore(struct lxc_container *c, char *directory, bool verbose)
{
- pid_t pid;
- int status, nread;
- int pipefd[2];
-
- if (!criu_ok(c))
- return false;
-
- if (geteuid()) {
- ERROR("Must be root to restore\n");
- return false;
- }
-
- if (pipe(pipefd)) {
- ERROR("failed to create pipe");
- return false;
- }
-
- pid = fork();
- if (pid < 0) {
- close(pipefd[0]);
- close(pipefd[1]);
- return false;
- }
-
- if (pid == 0) {
- close(pipefd[0]);
- // this never returns
- do_restore(c, pipefd[1], directory, verbose);
- }
-
- close(pipefd[1]);
-
- nread = read(pipefd[0], &status, sizeof(status));
- close(pipefd[0]);
- if (sizeof(status) != nread) {
- ERROR("reading status from pipe failed");
- goto err_wait;
- }
-
- // If the criu process was killed or exited nonzero, wait() for the
- // handler, since the restore process died. Otherwise, we don't need to
- // wait, since the child becomes the monitor process.
- if (!WIFEXITED(status) || WEXITSTATUS(status))
- goto err_wait;
- return true;
+ struct migrate_opts opts = {
+ .directory = directory,
+ .verbose = verbose,
+ };
-err_wait:
- if (wait_for_pid(pid))
- ERROR("restore process died");
- return false;
+ return do_lxcapi_migrate(c, MIGRATE_RESTORE, &opts, sizeof(opts));
}
WRAP_API_2(bool, lxcapi_restore, char *, bool)
@@ -4243,6 +4200,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
c->detach_interface = lxcapi_detach_interface;
c->checkpoint = lxcapi_checkpoint;
c->restore = lxcapi_restore;
+ c->migrate = lxcapi_migrate;
return c;
diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h
index 38e1a8e..e909d81 100644
--- a/src/lxc/lxccontainer.h
+++ b/src/lxc/lxccontainer.h
@@ -49,6 +49,8 @@ struct lxc_snapshot;
struct lxc_lock;
+struct migrate_opts;
+
/*!
* An LXC container.
*
@@ -812,6 +814,16 @@ struct lxc_container {
bool (*snapshot_destroy_all)(struct lxc_container *c);
/* Post LXC-1.1 additions */
+ /*!
+ * \brief An API call to perform various migration operations
+ *
+ * \param cmd One of the MIGRATE_ contstants.
+ * \param opts A migrate_opts struct filled with relevant options.
+ * \param size The size of the migrate_opts struct, i.e. sizeof(struct migrate_opts).
+ *
+ * \return \c 0 on success, nonzero on failure.
+ */
+ int (*migrate)(struct lxc_container *c, unsigned int cmd, struct migrate_opts *opts, unsigned int size);
};
/*!
@@ -849,6 +861,27 @@ struct bdev_specs {
};
/*!
+ * \brief Commands for the migrate API call.
+ */
+enum {
+ MIGRATE_PRE_DUMP,
+ MIGRATE_DUMP,
+ MIGRATE_RESTORE,
+};
+
+/*!
+ * \brief Options for the migrate API call.
+ */
+struct migrate_opts {
+ /* new members should be added at the end */
+ char *directory;
+ bool verbose;
+
+ bool stop; /* stop the container after dump? */
+ char *predump_dir; /* relative to directory above */
+};
+
+/*!
* \brief Create a new container.
*
* \param name Name to use for container.
--
2.6.2
More information about the lxc-devel
mailing list