#include <libgen.h>
#include <stdint.h>
#include <grp.h>
+#include <stdio.h>
#include <sys/syscall.h>
#include <lxc/lxccontainer.h>
#include "console.h"
#include "cgroup.h"
#include "commands.h"
+#include "criu.h"
#include "log.h"
#include "bdev.h"
#include "utils.h"
#include <../include/ifaddrs.h>
#endif
+#if IS_BIONIC
+#include <../include/lxcmntent.h>
+#else
+#include <mntent.h>
+#endif
+
#define MAX_BUFFER 4096
#define NOT_SUPPORTED_ERROR "the requested function %s is not currently supported with unprivileged containers"
}
#endif
-
lxc_log_define(lxc_container, lxc);
+static bool do_lxcapi_destroy(struct lxc_container *c);
+static const char *lxcapi_get_config_path(struct lxc_container *c);
+#define do_lxcapi_get_config_path(c) lxcapi_get_config_path(c)
+static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v);
+static bool container_destroy(struct lxc_container *c);
+static bool get_snappath_dir(struct lxc_container *c, char *snappath);
+static bool lxcapi_snapshot_destroy_all(struct lxc_container *c);
+static bool do_lxcapi_save_config(struct lxc_container *c, const char *alt_file);
+
static bool config_file_exists(const char *lxcpath, const char *cname)
{
/* $lxcpath + '/' + $cname + '/config' + \0 */
if (!c)
return;
- if (c->configfile) {
- free(c->configfile);
- c->configfile = NULL;
- }
- if (c->error_string) {
- free(c->error_string);
- c->error_string = NULL;
- }
+ free(c->configfile);
+ c->configfile = NULL;
+ free(c->error_string);
+ c->error_string = NULL;
if (c->slock) {
lxc_putlock(c->slock);
c->slock = NULL;
lxc_putlock(c->privlock);
c->privlock = NULL;
}
- if (c->name) {
- free(c->name);
- c->name = NULL;
- }
+ free(c->name);
+ c->name = NULL;
if (c->lxc_conf) {
lxc_conf_free(c->lxc_conf);
c->lxc_conf = NULL;
}
- if (c->config_path) {
- free(c->config_path);
- c->config_path = NULL;
- }
+ free(c->config_path);
+ c->config_path = NULL;
free(c);
}
return 0;
}
-static bool lxcapi_is_defined(struct lxc_container *c)
+static bool do_lxcapi_is_defined(struct lxc_container *c)
{
struct stat statbuf;
bool ret = false;
return ret;
}
-static const char *lxcapi_state(struct lxc_container *c)
+#define WRAP_API(rettype, fnname) \
+static rettype fnname(struct lxc_container *c) \
+{ \
+ rettype ret; \
+ current_config = c ? c->lxc_conf : NULL; \
+ ret = do_##fnname(c); \
+ current_config = NULL; \
+ return ret; \
+}
+
+#define WRAP_API_1(rettype, fnname, t1) \
+static rettype fnname(struct lxc_container *c, t1 a1) \
+{ \
+ rettype ret; \
+ current_config = c ? c->lxc_conf : NULL; \
+ ret = do_##fnname(c, a1); \
+ current_config = NULL; \
+ return ret; \
+}
+
+#define WRAP_API_2(rettype, fnname, t1, t2) \
+static rettype fnname(struct lxc_container *c, t1 a1, t2 a2) \
+{ \
+ rettype ret; \
+ current_config = c ? c->lxc_conf : NULL; \
+ ret = do_##fnname(c, a1, a2); \
+ current_config = NULL; \
+ return ret; \
+}
+
+#define WRAP_API_3(rettype, fnname, t1, t2, t3) \
+static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3) \
+{ \
+ rettype ret; \
+ current_config = c ? c->lxc_conf : NULL; \
+ ret = do_##fnname(c, a1, a2, a3); \
+ current_config = NULL; \
+ return ret; \
+}
+
+WRAP_API(bool, lxcapi_is_defined)
+
+static const char *do_lxcapi_state(struct lxc_container *c)
{
lxc_state_t s;
return lxc_state2str(s);
}
+WRAP_API(const char *, lxcapi_state)
+
static bool is_stopped(struct lxc_container *c)
{
lxc_state_t s;
return (s == STOPPED);
}
-static bool lxcapi_is_running(struct lxc_container *c)
+static bool do_lxcapi_is_running(struct lxc_container *c)
{
const char *s;
if (!c)
return false;
- s = lxcapi_state(c);
+ s = do_lxcapi_state(c);
if (!s || strcmp(s, "STOPPED") == 0)
return false;
return true;
}
-static bool lxcapi_freeze(struct lxc_container *c)
+WRAP_API(bool, lxcapi_is_running)
+
+static bool do_lxcapi_freeze(struct lxc_container *c)
{
int ret;
if (!c)
return true;
}
-static bool lxcapi_unfreeze(struct lxc_container *c)
+WRAP_API(bool, lxcapi_freeze)
+
+static bool do_lxcapi_unfreeze(struct lxc_container *c)
{
int ret;
if (!c)
return true;
}
-static int lxcapi_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd)
+WRAP_API(bool, lxcapi_unfreeze)
+
+static int do_lxcapi_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd)
{
int ttyfd;
if (!c)
return ttyfd;
}
+WRAP_API_2(int, lxcapi_console_getfd, int *, int *)
+
static int lxcapi_console(struct lxc_container *c, int ttynum, int stdinfd,
int stdoutfd, int stderrfd, int escape)
{
- return lxc_console(c, ttynum, stdinfd, stdoutfd, stderrfd, escape);
+ int ret;
+
+ if (!c)
+ return -1;
+
+ current_config = c->lxc_conf;
+ ret = lxc_console(c, ttynum, stdinfd, stdoutfd, stderrfd, escape);
+ current_config = NULL;
+ return ret;
}
-static pid_t lxcapi_init_pid(struct lxc_container *c)
+static pid_t do_lxcapi_init_pid(struct lxc_container *c)
{
if (!c)
return -1;
return lxc_cmd_get_init_pid(c->name, c->config_path);
}
+WRAP_API(pid_t, lxcapi_init_pid)
+
static bool load_config_locked(struct lxc_container *c, const char *fname)
{
if (!c->lxc_conf)
return true;
}
-static bool lxcapi_load_config(struct lxc_container *c, const char *alt_file)
+static bool do_lxcapi_load_config(struct lxc_container *c, const char *alt_file)
{
bool ret = false, need_disklock = false;
int lret;
return ret;
}
-static bool lxcapi_want_daemonize(struct lxc_container *c, bool state)
+WRAP_API_1(bool, lxcapi_load_config, const char *)
+
+static bool do_lxcapi_want_daemonize(struct lxc_container *c, bool state)
{
if (!c || !c->lxc_conf)
return false;
return false;
}
c->daemonize = state;
- /* daemonize implies close_all_fds so set it */
- if (state == 1)
- c->lxc_conf->close_all_fds = 1;
container_mem_unlock(c);
return true;
}
-static bool lxcapi_want_close_all_fds(struct lxc_container *c, bool state)
+WRAP_API_1(bool, lxcapi_want_daemonize, bool)
+
+static bool do_lxcapi_want_close_all_fds(struct lxc_container *c, bool state)
{
if (!c || !c->lxc_conf)
return false;
return true;
}
-static bool lxcapi_wait(struct lxc_container *c, const char *state, int timeout)
+WRAP_API_1(bool, lxcapi_want_close_all_fds, bool)
+
+static bool do_lxcapi_wait(struct lxc_container *c, const char *state, int timeout)
{
int ret;
return ret == 0;
}
+WRAP_API_2(bool, lxcapi_wait, const char *, int)
-static bool wait_on_daemonized_start(struct lxc_container *c, int pid)
+static bool do_wait_on_daemonized_start(struct lxc_container *c, int pid)
{
/* we'll probably want to make this timeout configurable? */
int timeout = 5, ret, status;
ret = waitpid(pid, &status, 0);
if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
DEBUG("failed waiting for first dual-fork child");
- return lxcapi_wait(c, "RUNNING", timeout);
+ return do_lxcapi_wait(c, "RUNNING", timeout);
}
+WRAP_API_1(bool, wait_on_daemonized_start, int)
+
static bool am_single_threaded(void)
{
struct dirent dirent, *direntp;
* I can't decide if it'd be more convenient for callers if we accept '...',
* or a null-terminated array (i.e. execl vs execv)
*/
-static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv[])
+static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const argv[])
{
int ret;
struct lxc_conf *conf;
}
if (ret == 2) {
ERROR("Error: %s creation was not completed", c->name);
- c->destroy(c);
+ do_lxcapi_destroy(c);
return false;
} else if (ret == 1) {
ERROR("Error: creation of %s is ongoing", c->name);
container_mem_unlock(c);
if (useinit) {
- ret = lxc_execute(c->name, argv, 1, conf, c->config_path);
+ ret = lxc_execute(c->name, argv, 1, conf, c->config_path, daemonize);
return ret == 0 ? true : false;
}
* while container is running...
*/
if (daemonize) {
+ char title[2048];
lxc_monitord_spawn(c->config_path);
pid_t pid = fork();
return wait_on_daemonized_start(c, pid);
}
+ /* We don't really care if this doesn't print all the
+ * characters; all that it means is that the proctitle will be
+ * ugly. Similarly, we also don't care if setproctitle()
+ * fails. */
+ snprintf(title, sizeof(title), "[lxc monitor] %s %s", c->config_path, c->name);
+ INFO("Attempting to set proc title to %s", title);
+ setproctitle(title);
+
/* second fork to be reparented by init */
pid = fork();
if (pid < 0) {
SYSERROR("Error doing dual-fork");
- return false;
+ exit(1);
}
if (pid != 0)
exit(0);
/* like daemon(), chdir to / and redirect 0,1,2 to /dev/null */
if (chdir("/")) {
SYSERROR("Error chdir()ing to /.");
- return false;
+ exit(1);
+ }
+ lxc_check_inherited(conf, true, -1);
+ if (null_stdfds() < 0) {
+ ERROR("failed to close fds");
+ exit(1);
}
- lxc_check_inherited(conf, -1);
- close(0);
- close(1);
- close(2);
- open("/dev/zero", O_RDONLY);
- open("/dev/null", O_RDWR);
- open("/dev/null", O_RDWR);
setsid();
} else {
if (!am_single_threaded()) {
if (pid_fp == NULL) {
SYSERROR("Failed to create pidfile '%s' for '%s'",
c->pidfile, c->name);
+ if (daemonize)
+ exit(1);
return false;
}
SYSERROR("Failed to write '%s'", c->pidfile);
fclose(pid_fp);
pid_fp = NULL;
+ if (daemonize)
+ exit(1);
return false;
}
pid_fp = NULL;
}
-reboot:
conf->reboot = 0;
- ret = lxc_start(c->name, argv, conf, c->config_path);
+
+reboot:
+ if (lxc_check_inherited(conf, daemonize, -1)) {
+ ERROR("Inherited fds found");
+ ret = 1;
+ goto out;
+ }
+
+ ret = lxc_start(c->name, argv, conf, c->config_path, daemonize);
c->error_num = ret;
- if (conf->reboot) {
+ if (conf->reboot == 1) {
INFO("container requested reboot");
- conf->reboot = 0;
+ conf->reboot = 2;
goto reboot;
}
+out:
if (c->pidfile) {
unlink(c->pidfile);
free(c->pidfile);
return (ret == 0 ? true : false);
}
+static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv[])
+{
+ bool ret;
+ current_config = c ? c->lxc_conf : NULL;
+ ret = do_lxcapi_start(c, useinit, argv);
+ current_config = NULL;
+ return ret;
+}
+
/*
* note there MUST be an ending NULL
*/
if (!c)
return false;
+ current_config = c->lxc_conf;
+
va_start(ap, useinit);
inargs = lxc_va_arg_list_to_argv(ap, 0, 1);
va_end(ap);
}
/* pass NULL if no arguments were supplied */
- bret = lxcapi_start(c, useinit, *inargs ? inargs : NULL);
+ bret = do_lxcapi_start(c, useinit, *inargs ? inargs : NULL);
out:
if (inargs) {
free(inargs);
}
+ current_config = NULL;
return bret;
}
-static bool lxcapi_stop(struct lxc_container *c)
+static bool do_lxcapi_stop(struct lxc_container *c)
{
int ret;
return ret == 0;
}
+WRAP_API(bool, lxcapi_stop)
+
static int do_create_container_dir(const char *path, struct lxc_conf *conf)
{
int ret = -1, lasterr;
return ret == 0;
}
-static const char *lxcapi_get_config_path(struct lxc_container *c);
-static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v);
-
/*
* do_bdev_create: thin wrapper around bdev_create(). Like bdev_create(),
* it returns a mounted bdev on success, NULL on error.
dest = alloca(len);
ret = snprintf(dest, len, "%s", rpath);
} else {
- const char *lxcpath = lxcapi_get_config_path(c);
+ const char *lxcpath = do_lxcapi_get_config_path(c);
len = strlen(c->name) + strlen(lxcpath) + 9;
dest = alloca(len);
ret = snprintf(dest, len, "%s/%s/rootfs", lxcpath, c->name);
return NULL;
}
- lxcapi_set_config_item(c, "lxc.rootfs", bdev->src);
+ do_lxcapi_set_config_item(c, "lxc.rootfs", bdev->src);
/* if we are not root, chown the rootfs dir to root in the
* target uidmap */
return bdev;
}
-/*
- * Given the '-t' template option to lxc-create, figure out what to
- * do. If the template is a full executable path, use that. If it
- * is something like 'sshd', then return $templatepath/lxc-sshd.
- * On success return the template, on error return NULL.
- */
-static char *get_template_path(const char *t)
-{
- int ret, len;
- char *tpath;
-
- if (t[0] == '/' && access(t, X_OK) == 0) {
- tpath = strdup(t);
- return tpath;
- }
-
- len = strlen(LXCTEMPLATEDIR) + strlen(t) + strlen("/lxc-") + 1;
- tpath = malloc(len);
- if (!tpath)
- return NULL;
- ret = snprintf(tpath, len, "%s/lxc-%s", LXCTEMPLATEDIR, t);
- if (ret < 0 || ret >= len) {
- free(tpath);
- return NULL;
- }
- if (access(tpath, X_OK) < 0) {
- SYSERROR("bad template: %s", t);
- free(tpath);
- return NULL;
- }
-
- return tpath;
-}
-
static char *lxcbasename(char *path)
{
char *p = path + strlen(path) - 1;
return p;
}
-static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet,
+static bool create_run_template(struct lxc_container *c, char *tpath, bool need_null_stdfds,
char *const argv[])
{
pid_t pid;
char **newargv;
struct lxc_conf *conf = c->lxc_conf;
- if (quiet) {
- close(0);
- close(1);
- close(2);
- open("/dev/zero", O_RDONLY);
- open("/dev/null", O_RDWR);
- open("/dev/null", O_RDWR);
+ if (need_null_stdfds && null_stdfds() < 0) {
+ exit(1);
}
src = c->lxc_conf->rootfs.path;
exit(1);
}
} else { // TODO come up with a better way here!
- if (bdev->dest)
- free(bdev->dest);
+ free(bdev->dest);
bdev->dest = strdup(bdev->src);
}
}
}
-static bool lxcapi_destroy(struct lxc_container *c);
-static bool container_destroy(struct lxc_container *c);
-static bool get_snappath_dir(struct lxc_container *c, char *snappath);
+#define do_lxcapi_clear_config(c) lxcapi_clear_config(c)
+
/*
* lxcapi_create:
* create a container with the given parameters.
* @argv: the arguments to pass to the template, terminated by NULL. If no
* arguments, you can just pass NULL.
*/
-static bool lxcapi_create(struct lxc_container *c, const char *t,
+static bool do_lxcapi_create(struct lxc_container *c, const char *t,
const char *bdevtype, struct bdev_specs *specs, int flags,
char *const argv[])
{
* an existing container. Return an error, but do NOT delete the
* container.
*/
- if (lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path &&
+ if (do_lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path &&
access(c->lxc_conf->rootfs.path, F_OK) == 0 && tpath) {
ERROR("Container %s:%s already exists", c->config_path, c->name);
goto free_tpath;
}
if (!c->lxc_conf) {
- if (!c->load_config(c, lxc_global_config_value("lxc.default_config"))) {
+ if (!do_lxcapi_load_config(c, lxc_global_config_value("lxc.default_config"))) {
ERROR("Error loading default configuration file %s", lxc_global_config_value("lxc.default_config"));
goto free_tpath;
}
if (c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) != 0)
/* rootfs passed into configuration, but does not exist: error */
goto out;
- if (lxcapi_is_defined(c) && c->lxc_conf->rootfs.path && !tpath) {
+ if (do_lxcapi_is_defined(c) && c->lxc_conf->rootfs.path && !tpath) {
/* Rootfs already existed, user just wanted to save the
* loaded configuration */
ret = true;
}
/* save config file again to store the new rootfs location */
- if (!c->save_config(c, NULL)) {
+ if (!do_lxcapi_save_config(c, NULL)) {
ERROR("failed to save starting configuration for %s", c->name);
// parent task won't see bdev in config so we delete it
bdev->ops->umount(bdev);
// now clear out the lxc_conf we have, reload from the created
// container
- lxcapi_clear_config(c);
+ do_lxcapi_clear_config(c);
if (t) {
if (!prepend_lxc_header(c->configfile, tpath, argv)) {
if (partial_fd >= 0)
remove_partial(c, partial_fd);
out:
- if (!ret && c)
+ if (!ret)
container_destroy(c);
free_tpath:
- if (tpath)
- free(tpath);
+ free(tpath);
+ return ret;
+}
+
+static bool lxcapi_create(struct lxc_container *c, const char *t,
+ const char *bdevtype, struct bdev_specs *specs, int flags,
+ char *const argv[])
+{
+ bool ret;
+ current_config = c ? c->lxc_conf : NULL;
+ ret = do_lxcapi_create(c, t, bdevtype, specs, flags, argv);
+ current_config = NULL;
return ret;
}
-static bool lxcapi_reboot(struct lxc_container *c)
+static bool do_lxcapi_reboot(struct lxc_container *c)
{
pid_t pid;
+ int rebootsignal = SIGINT;
if (!c)
return false;
- if (!c->is_running(c))
+ if (!do_lxcapi_is_running(c))
return false;
- pid = c->init_pid(c);
+ pid = do_lxcapi_init_pid(c);
if (pid <= 0)
return false;
- if (kill(pid, SIGINT) < 0)
+ if (c->lxc_conf && c->lxc_conf->rebootsignal)
+ rebootsignal = c->lxc_conf->rebootsignal;
+ if (kill(pid, rebootsignal) < 0)
return false;
return true;
}
-static bool lxcapi_shutdown(struct lxc_container *c, int timeout)
+WRAP_API(bool, lxcapi_reboot)
+
+static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout)
{
bool retv;
pid_t pid;
if (!c)
return false;
- if (!c->is_running(c))
+ if (!do_lxcapi_is_running(c))
return true;
- pid = c->init_pid(c);
+ pid = do_lxcapi_init_pid(c);
if (pid <= 0)
return true;
if (c->lxc_conf && c->lxc_conf->haltsignal)
haltsignal = c->lxc_conf->haltsignal;
kill(pid, haltsignal);
- retv = c->wait(c, "STOPPED", timeout);
+ retv = do_lxcapi_wait(c, "STOPPED", timeout);
return retv;
}
+WRAP_API_1(bool, lxcapi_shutdown, int)
+
static bool lxcapi_createl(struct lxc_container *c, const char *t,
const char *bdevtype, struct bdev_specs *specs, int flags, ...)
{
if (!c)
return false;
+ current_config = c->lxc_conf;
+
/*
* since we're going to wait for create to finish, I don't think we
* need to get a copy of the arguments.
goto out;
}
- bret = c->create(c, t, bdevtype, specs, flags, args);
+ bret = do_lxcapi_create(c, t, bdevtype, specs, flags, args);
out:
free(args);
+ current_config = NULL;
return bret;
}
WARN("Error clearing configuration for %s", key);
}
-static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key)
+static bool do_lxcapi_clear_config_item(struct lxc_container *c, const char *key)
{
int ret;
return ret == 0;
}
+WRAP_API_1(bool, lxcapi_clear_config_item, const char *)
+
static inline bool enter_net_ns(struct lxc_container *c)
{
- pid_t pid = c->init_pid(c);
+ pid_t pid = do_lxcapi_init_pid(c);
if ((geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) && access("/proc/self/ns/user", F_OK) == 0) {
if (!switch_to_ns(pid, "user"))
return false;
}
-static char** lxcapi_get_interfaces(struct lxc_container *c)
+static char ** do_lxcapi_get_interfaces(struct lxc_container *c)
{
pid_t pid;
int i, count = 0, pipefd[2];
return interfaces;
}
-static char** lxcapi_get_ips(struct lxc_container *c, const char* interface, const char* family, int scope)
+WRAP_API(char **, lxcapi_get_interfaces)
+
+static char** do_lxcapi_get_ips(struct lxc_container *c, const char* interface, const char* family, int scope)
{
pid_t pid;
int i, count = 0, pipefd[2];
return addresses;
}
-static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen)
+WRAP_API_3(char **, lxcapi_get_ips, const char *, const char *, int)
+
+static int do_lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen)
{
int ret;
return ret;
}
-static char* lxcapi_get_running_config_item(struct lxc_container *c, const char *key)
+WRAP_API_3(int, lxcapi_get_config_item, const char *, char *, int)
+
+static char* do_lxcapi_get_running_config_item(struct lxc_container *c, const char *key)
{
char *ret;
return NULL;
if (container_mem_lock(c))
return NULL;
- ret = lxc_cmd_get_config_item(c->name, key, c->get_config_path(c));
+ ret = lxc_cmd_get_config_item(c->name, key, do_lxcapi_get_config_path(c));
container_mem_unlock(c);
return ret;
}
-static int lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, int inlen)
+WRAP_API_1(char *, lxcapi_get_running_config_item, const char *)
+
+static int do_lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, int inlen)
{
if (!key)
return lxc_listconfigs(retv, inlen);
return ret;
}
-static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file)
+WRAP_API_3(int, lxcapi_get_keys, const char *, char *, int)
+
+static bool do_lxcapi_save_config(struct lxc_container *c, const char *alt_file)
{
FILE *fout;
bool ret = false, need_disklock = false;
// If we haven't yet loaded a config, load the stock config
if (!c->lxc_conf) {
- if (!c->load_config(c, lxc_global_config_value("lxc.default_config"))) {
+ if (!do_lxcapi_load_config(c, lxc_global_config_value("lxc.default_config"))) {
ERROR("Error loading default configuration file %s while saving %s", lxc_global_config_value("lxc.default_config"), c->name);
return false;
}
return ret;
}
+WRAP_API_1(bool, lxcapi_save_config, const char *)
+
static bool mod_rdep(struct lxc_container *c, bool inc)
{
char path[MAXPATHLEN];
lxc_container_put(p);
}
out:
- if (lxcpath) free(lxcpath);
- if (lxcname) free(lxcname);
+ free(lxcpath);
+ free(lxcname);
fclose(f);
}
{
bool bret = false;
int ret;
+ struct lxc_conf *conf;
- if (!c || !lxcapi_is_defined(c))
+ if (!c || !do_lxcapi_is_defined(c))
return false;
+ conf = c->lxc_conf;
if (container_disk_lock(c))
return false;
goto out;
}
- if (c->lxc_conf && c->lxc_conf->rootfs.path && c->lxc_conf->rootfs.mount) {
+ if (conf && !lxc_list_empty(&conf->hooks[LXCHOOK_DESTROY])) {
+ /* Start of environment variable setup for hooks */
+ if (setenv("LXC_NAME", c->name, 1)) {
+ SYSERROR("failed to set environment variable for container name");
+ }
+ if (setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) {
+ SYSERROR("failed to set environment variable for config path");
+ }
+ if (setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1)) {
+ SYSERROR("failed to set environment variable for rootfs mount");
+ }
+ if (setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) {
+ SYSERROR("failed to set environment variable for rootfs mount");
+ }
+ if (conf->console.path && setenv("LXC_CONSOLE", conf->console.path, 1)) {
+ SYSERROR("failed to set environment variable for console path");
+ }
+ if (conf->console.log_path && setenv("LXC_CONSOLE_LOGPATH", conf->console.log_path, 1)) {
+ SYSERROR("failed to set environment variable for console log");
+ }
+ /* End of environment variable setup for hooks */
+
+ if (run_lxc_hooks(c->name, "destroy", conf, c->get_config_path(c), NULL)) {
+ ERROR("Error executing clone hook for %s", c->name);
+ goto out;
+ }
+ }
+
+ if (current_config && conf == current_config) {
+ current_config = NULL;
+ if (conf->logfd != -1) {
+ close(conf->logfd);
+ conf->logfd = -1;
+ }
+ }
+
+ if (conf && conf->rootfs.path && conf->rootfs.mount) {
if (am_unpriv())
- ret = userns_exec_1(c->lxc_conf, bdev_destroy_wrapper, c->lxc_conf);
+ ret = userns_exec_1(conf, bdev_destroy_wrapper, conf);
else
- ret = do_bdev_destroy(c->lxc_conf);
+ ret = do_bdev_destroy(conf);
if (ret < 0) {
ERROR("Error destroying rootfs for %s", c->name);
goto out;
mod_all_rdeps(c, false);
- const char *p1 = lxcapi_get_config_path(c);
+ const char *p1 = do_lxcapi_get_config_path(c);
char *path = alloca(strlen(p1) + strlen(c->name) + 2);
sprintf(path, "%s/%s", p1, c->name);
if (am_unpriv())
- ret = userns_exec_1(c->lxc_conf, lxc_rmdir_onedev_wrapper, path);
+ ret = userns_exec_1(conf, lxc_rmdir_onedev_wrapper, path);
else
ret = lxc_rmdir_onedev(path, "snaps");
if (ret < 0) {
return bret;
}
-static bool lxcapi_destroy(struct lxc_container *c)
+static bool do_lxcapi_destroy(struct lxc_container *c)
{
if (!c || !lxcapi_is_defined(c))
return false;
return container_destroy(c);
}
-static bool lxcapi_snapshot_destroy_all(struct lxc_container *c);
+WRAP_API(bool, lxcapi_destroy)
-static bool lxcapi_destroy_with_snapshots(struct lxc_container *c)
+static bool do_lxcapi_destroy_with_snapshots(struct lxc_container *c)
{
if (!c || !lxcapi_is_defined(c))
return false;
return lxcapi_destroy(c);
}
+WRAP_API(bool, lxcapi_destroy_with_snapshots)
+
static bool set_config_item_locked(struct lxc_container *c, const char *key, const char *v)
{
struct lxc_config_t *config;
return do_append_unexp_config_line(c->lxc_conf, key, v);
}
-static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v)
+static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v)
{
bool b = false;
return b;
}
+WRAP_API_2(bool, lxcapi_set_config_item, const char *, const char *)
+
static char *lxcapi_config_file_name(struct lxc_container *c)
{
if (!c || !c->configfile)
return false;
}
- if (c->configfile)
- free(c->configfile);
+ free(c->configfile);
c->configfile = newpath;
return true;
}
-static bool lxcapi_set_config_path(struct lxc_container *c, const char *path)
+static bool do_lxcapi_set_config_path(struct lxc_container *c, const char *path)
{
char *p;
bool b = false;
oldpath = NULL;
}
err:
- if (oldpath)
- free(oldpath);
+ free(oldpath);
container_mem_unlock(c);
return b;
}
+WRAP_API_1(bool, lxcapi_set_config_path, const char *)
-static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, const char *value)
+static bool do_lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, const char *value)
{
int ret;
return ret == 0;
}
-static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, char *retv, int inlen)
+WRAP_API_2(bool, lxcapi_set_cgroup_item, const char *, const char *)
+
+static int do_lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, char *retv, int inlen)
{
int ret;
return ret;
}
+WRAP_API_3(int, lxcapi_get_cgroup_item, const char *, char *, int)
+
const char *lxc_get_global_config_item(const char *key)
{
return lxc_global_config_value(key);
ERROR("Error saving new hooks in clone");
return -1;
}
- c->save_config(c, NULL);
+ do_lxcapi_save_config(c, NULL);
return 0;
}
return -1;
}
} else { // TODO come up with a better way
- if (bdev->dest)
- free(bdev->dest);
+ free(bdev->dest);
bdev->dest = strdup(bdev->src);
}
return ret;
}
-static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
+static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char *newname,
const char *lxcpath, int flags,
const char *bdevtype, const char *bdevdata, uint64_t newsize,
char **hookargs)
FILE *fout;
pid_t pid;
- if (!c || !c->is_defined(c))
+ if (!c || !do_lxcapi_is_defined(c))
return NULL;
if (container_mem_lock(c))
if (!newname)
newname = c->name;
if (!lxcpath)
- lxcpath = c->get_config_path(c);
+ lxcpath = do_lxcapi_get_config_path(c);
ret = snprintf(newpath, MAXPATHLEN, "%s/%s/config", lxcpath, newname);
if (ret < 0 || ret >= MAXPATHLEN) {
SYSERROR("clone: failed making config pathname");
return NULL;
}
-static bool lxcapi_rename(struct lxc_container *c, const char *newname)
+static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
+ const char *lxcpath, int flags,
+ const char *bdevtype, const char *bdevdata, uint64_t newsize,
+ char **hookargs)
+{
+ struct lxc_container * ret;
+ current_config = c ? c->lxc_conf : NULL;
+ ret = do_lxcapi_clone(c, newname, lxcpath, flags, bdevtype, bdevdata, newsize, hookargs);
+ current_config = NULL;
+ return ret;
+}
+
+static bool do_lxcapi_rename(struct lxc_container *c, const char *newname)
{
struct bdev *bdev;
struct lxc_container *newc;
return true;
}
+WRAP_API_1(bool, lxcapi_rename, const char *)
+
static int lxcapi_attach(struct lxc_container *c, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process)
{
+ int ret;
+
if (!c)
return -1;
- return lxc_attach(c->name, c->config_path, exec_function, exec_payload, options, attached_process);
+ current_config = c->lxc_conf;
+
+ ret = lxc_attach(c->name, c->config_path, exec_function, exec_payload, options, attached_process);
+ current_config = NULL;
+ return ret;
}
-static int lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char * const argv[])
+static int do_lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char * const argv[])
{
lxc_attach_command_t command;
pid_t pid;
return lxc_wait_for_pid_status(pid);
}
+static int lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char * const argv[])
+{
+ int ret;
+ current_config = c ? c->lxc_conf : NULL;
+ ret = do_lxcapi_attach_run_wait(c, options, program, argv);
+ current_config = NULL;
+ return ret;
+}
+
static int get_next_index(const char *lxcpath, char *cname)
{
char *fname;
return true;
}
-static int lxcapi_snapshot(struct lxc_container *c, const char *commentfile)
+static int do_lxcapi_snapshot(struct lxc_container *c, const char *commentfile)
{
int i, flags, ret;
struct lxc_container *c2;
ERROR("and keep the original container pristine.");
flags &= ~LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT;
}
- c2 = c->clone(c, newname, snappath, flags, NULL, NULL, 0, NULL);
+ c2 = do_lxcapi_clone(c, newname, snappath, flags, NULL, NULL, 0, NULL);
if (!c2) {
ERROR("clone of %s:%s failed", c->config_path, c->name);
return -1;
return i;
}
+WRAP_API_1(int, lxcapi_snapshot, const char *)
+
static void lxcsnap_free(struct lxc_snapshot *s)
{
- if (s->name)
- free(s->name);
- if (s->comment_pathname)
- free(s->comment_pathname);
- if (s->timestamp)
- free(s->timestamp);
- if (s->lxcpath)
- free(s->lxcpath);
+ free(s->name);
+ free(s->comment_pathname);
+ free(s->timestamp);
+ free(s->lxcpath);
}
static char *get_snapcomment_path(char* snappath, char *name)
return s;
}
-static int lxcapi_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret_snaps)
+static int do_lxcapi_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret_snaps)
{
char snappath[MAXPATHLEN], path2[MAXPATHLEN];
int count = 0, ret;
return -1;
}
-static bool lxcapi_snapshot_restore(struct lxc_container *c, const char *snapname, const char *newname)
+WRAP_API_1(int, lxcapi_snapshot_list, struct lxc_snapshot **)
+
+static bool do_lxcapi_snapshot_restore(struct lxc_container *c, const char *snapname, const char *newname)
{
char clonelxcpath[MAXPATHLEN];
int flags = 0;
return b;
}
+WRAP_API_2(bool, lxcapi_snapshot_restore, const char *, const char *)
+
static bool do_snapshot_destroy(const char *snapname, const char *clonelxcpath)
{
struct lxc_container *snap = NULL;
goto err;
}
- if (!lxcapi_destroy(snap)) {
+ if (!do_lxcapi_destroy(snap)) {
ERROR("Could not destroy snapshot %s", snapname);
goto err;
}
return bret;
}
-static bool lxcapi_snapshot_destroy(struct lxc_container *c, const char *snapname)
+static bool do_lxcapi_snapshot_destroy(struct lxc_container *c, const char *snapname)
{
char clonelxcpath[MAXPATHLEN];
return do_snapshot_destroy(snapname, clonelxcpath);
}
-static bool lxcapi_snapshot_destroy_all(struct lxc_container *c)
+WRAP_API_1(bool, lxcapi_snapshot_destroy, const char *)
+
+static bool do_lxcapi_snapshot_destroy_all(struct lxc_container *c)
{
char clonelxcpath[MAXPATHLEN];
return remove_all_snapshots(clonelxcpath);
}
-static bool lxcapi_may_control(struct lxc_container *c)
+WRAP_API(bool, lxcapi_snapshot_destroy_all)
+
+static bool do_lxcapi_may_control(struct lxc_container *c)
{
return lxc_try_cmd(c->name, c->config_path) == 0;
}
+WRAP_API(bool, lxcapi_may_control)
+
static bool do_add_remove_node(pid_t init_pid, const char *path, bool add,
struct stat *st)
{
const char *p;
/* make sure container is running */
- if (!c->is_running(c)) {
+ if (!do_lxcapi_is_running(c)) {
ERROR("container is not running");
return false;
}
if (ret < 0 || ret >= MAX_BUFFER)
return false;
- if (!do_add_remove_node(c->init_pid(c), p, add, &st))
+ if (!do_add_remove_node(do_lxcapi_init_pid(c), p, add, &st))
return false;
/* add or remove device to/from cgroup access list */
if (add) {
- if (!c->set_cgroup_item(c, "devices.allow", value)) {
+ if (!do_lxcapi_set_cgroup_item(c, "devices.allow", value)) {
ERROR("set_cgroup_item failed while adding the device node");
return false;
}
} else {
- if (!c->set_cgroup_item(c, "devices.deny", value)) {
+ if (!do_lxcapi_set_cgroup_item(c, "devices.deny", value)) {
ERROR("set_cgroup_item failed while removing the device node");
return false;
}
return true;
}
-static bool lxcapi_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path)
+static bool do_lxcapi_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path)
{
if (am_unpriv()) {
ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
return add_remove_device_node(c, src_path, dest_path, true);
}
-static bool lxcapi_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path)
+WRAP_API_2(bool, lxcapi_add_device_node, const char *, const char *)
+
+static bool do_lxcapi_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path)
{
if (am_unpriv()) {
ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
return add_remove_device_node(c, src_path, dest_path, false);
}
-static bool lxcapi_attach_interface(struct lxc_container *c, const char *ifname,
+WRAP_API_2(bool, lxcapi_remove_device_node, const char *, const char *)
+
+static bool do_lxcapi_attach_interface(struct lxc_container *c, const char *ifname,
const char *dst_ifname)
{
int ret = 0;
goto err;
}
- ret = lxc_netdev_move_by_name(ifname, c->init_pid(c), dst_ifname);
+ ret = lxc_netdev_move_by_name(ifname, do_lxcapi_init_pid(c), dst_ifname);
if (ret)
goto err;
return false;
}
-static bool lxcapi_detach_interface(struct lxc_container *c, const char *ifname,
+WRAP_API_2(bool, lxcapi_attach_interface, const char *, const char *)
+
+static bool do_lxcapi_detach_interface(struct lxc_container *c, const char *ifname,
const char *dst_ifname)
{
pid_t pid, pid_outside;
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;
+WRAP_API_2(bool, lxcapi_detach_interface, const char *, const char *)
- /* restore: the file to write the init process' pid into */
- char *pidfile;
- const char *cgroup_path;
-};
-
-static void exec_criu(struct criu_opts *opts)
+static bool do_lxcapi_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose)
{
- char **argv, log[PATH_MAX], buf[257];
- int static_args = 14, argc = 0, i, ret;
- int netnr = 0;
- struct lxc_list *it;
-
- /* 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
- * +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("--action-script");
- DECLARE_ARG(DATADIR "/lxc/lxc-restore-net");
- 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) {
- 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);
-
- lxc_list_for_each(it, &opts->c->lxc_conf->network) {
- char eth[128], *veth;
- void *m;
- 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", eth, veth);
- if (ret < 0 || ret >= sizeof(buf))
- goto err;
-
- /* final NULL and --veth-pair eth0=vethASDF */
- m = realloc(argv, (argc + 1 + 2) * sizeof(*argv));
- if (!m)
- goto err;
- argv = m;
-
- DECLARE_ARG("--veth-pair");
- DECLARE_ARG(buf);
- argv[argc] = NULL;
-
- }
- }
-
- netnr = 0;
- lxc_list_for_each(it, &opts->c->lxc_conf->network) {
- struct lxc_netdev *n = it->elem;
- char veth[128];
-
- /*
- * Here, we set some parameters that lxc-restore-net
- * will examine to figure out the right network to
- * restore.
- */
- snprintf(buf, sizeof(buf), "LXC_CRIU_BRIDGE%d", netnr);
- if (setenv(buf, n->link, 1))
- goto err;
-
- if (strcmp("restore", opts->action) == 0)
- strncpy(veth, n->priv.veth_attr.pair, sizeof(veth));
- else {
- char *tmp;
- ret = snprintf(buf, sizeof(buf), "lxc.network.%d.veth.pair", netnr);
- if (ret < 0 || ret >= sizeof(buf))
- goto err;
- tmp = lxcapi_get_running_config_item(opts->c, buf);
- strncpy(veth, tmp, sizeof(veth));
- free(tmp);
- }
-
- snprintf(buf, sizeof(buf), "LXC_CRIU_VETH%d", netnr);
- if (setenv(buf, veth, 1))
- goto err;
-
- netnr++;
- }
-
-#undef DECLARE_ARG
- execv(argv[0], argv);
-err:
- 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;
+ pid_t pid;
+ int status;
+ char path[PATH_MAX];
- if (geteuid()) {
- ERROR("Must be root to checkpoint\n");
+ if (!criu_ok(c))
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");
+ if (mkdir(directory, 0700) < 0 && errno != EEXIST)
return false;
- }
- if (c->lxc_conf->tty != 0) {
- ERROR("lxc.tty must be 0\n");
+ status = snprintf(path, sizeof(path), "%s/inventory.img", directory);
+ if (status < 0 || status >= sizeof(path))
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");
+ if (access(path, F_OK) == 0) {
+ ERROR("please use a fresh directory for the dump directory\n");
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:
- if (veth)
- free(veth);
- if (bridge)
- 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;
- int status;
-
- if (!criu_ok(c))
- return false;
-
- if (mkdir(directory, 0700) < 0 && errno != EEXIST)
- return false;
-
if (!dump_net_info(c, directory))
return false;
} else {
pid_t w = waitpid(pid, &status, 0);
if (w == -1) {
- perror("waitpid");
+ SYSERROR("waitpid");
return false;
}
}
}
-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");
+WRAP_API_3(bool, lxcapi_checkpoint, char *, bool, bool)
- 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;
-}
-
-static bool lxcapi_restore(struct lxc_container *c, char *directory, bool verbose)
+static bool do_lxcapi_restore(struct lxc_container *c, char *directory, bool verbose)
{
pid_t pid;
- struct lxc_rootfs *rootfs;
- char pidfile[L_tmpnam];
- struct lxc_handler *handler;
- bool has_error = true;
+ int status, nread;
+ int pipefd[2];
if (!criu_ok(c))
return false;
return false;
}
- if (!tmpnam(pidfile))
- return false;
-
- handler = lxc_init(c->name, c->lxc_conf, c->config_path);
- if (!handler)
+ if (pipe(pipefd)) {
+ ERROR("failed to create pipe");
return false;
-
- 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;
}
pid = fork();
- if (pid < 0)
- goto out_fini_handler;
+ if (pid < 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
+ }
if (pid == 0) {
- struct criu_opts os;
-
- if (unshare(CLONE_NEWNS))
- exit(1);
-
- /* 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)
- exit(1);
- }
- else {
- if (mkdir(rootfs->mount, 0755) < 0 && errno != EEXIST)
- exit(1);
-
- if (mount(rootfs->path, rootfs->mount, NULL, MS_BIND, NULL) < 0) {
- rmdir(rootfs->mount);
- exit(1);
- }
- }
-
- 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);
- exit(1);
- } else {
- int status;
-
- pid_t w = waitpid(pid, &status, 0);
-
- if (w == -1) {
- perror("waitpid");
- goto out_fini_handler;
- }
-
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status)) {
- goto out_fini_handler;
- }
- else {
- int ret;
- FILE *f = fopen(pidfile, "r");
- if (!f) {
- perror("reading pidfile");
- ERROR("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;
- }
+ close(pipefd[0]);
+ // this never returns
+ do_restore(c, pipefd[1], directory, verbose);
+ }
- 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;
- }
+ close(pipefd[1]);
- if (lxc_poll(c->name, handler)) {
- lxc_abort(c->name, handler);
- goto out_fini_handler;
- }
+ nread = read(pipefd[0], &status, sizeof(status));
+ close(pipefd[0]);
+ if (sizeof(status) != nread) {
+ ERROR("reading status from pipe failed");
+ goto err_wait;
}
- has_error = false;
+ // 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;
-out_fini_handler:
- lxc_fini(c->name, handler);
- return !has_error;
+err_wait:
+ if (wait_for_pid(pid))
+ ERROR("restore process died");
+ return false;
}
+WRAP_API_2(bool, lxcapi_restore, char *, bool)
+
static int lxcapi_attach_run_waitl(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...)
{
va_list ap;
if (!c)
return -1;
+ current_config = c->lxc_conf;
+
va_start(ap, arg);
argv = lxc_va_arg_list_to_argv_const(ap, 1);
va_end(ap);
if (!argv) {
ERROR("Memory allocation error.");
- return -1;
+ ret = -1;
+ goto out;
}
argv[0] = arg;
- ret = lxcapi_attach_run_wait(c, options, program, (const char * const *)argv);
+ ret = do_lxcapi_attach_run_wait(c, options, program, (const char * const *)argv);
free((void*)argv);
+out:
+ current_config = NULL;
return ret;
}
c->checkpoint = lxcapi_checkpoint;
c->restore = lxcapi_restore;
- /* we'll allow the caller to update these later */
- if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0, c->config_path)) {
- fprintf(stderr, "failed to open log\n");
- goto err;
- }
-
return c;
err:
goto free_bad;
continue;
}
- if (!lxcapi_is_defined(c)) {
+ if (!do_lxcapi_is_defined(c)) {
INFO("Container %s:%s has a config but is not defined",
lxcpath, direntp->d_name);
if (names)
p++;
// Now p is the start of lxc_name
- p2 = index(p, '/');
+ p2 = strchr(p, '/');
if (!p2 || strncmp(p2, "/command", 8) != 0)
continue;
*p2 = '\0';
}
out:
- if (line)
- free(line);
+ free(line);
fclose(f);
return ret;
for (i = 0; i < ct_list_cnt; i++) {
lxc_container_put(ct_list[i]);
}
- if (ct_list)
- free(ct_list);
+ free(ct_list);
free_active_name:
for (i = 0; i < active_cnt; i++) {
- if (active_name[i])
- free(active_name[i]);
+ free(active_name[i]);
}
- if (active_name)
- free(active_name);
+ free(active_name);
free_ct_name:
for (i = 0; i < ct_cnt; i++) {