#include <libgen.h>
#include <stdint.h>
#include <grp.h>
+#include <stdio.h>
#include <sys/syscall.h>
#include <lxc/lxccontainer.h>
#include <lxc/version.h>
+#include <lxc/network.h>
#include "config.h"
#include "lxc.h"
#include "console.h"
#include "cgroup.h"
#include "commands.h"
+#include "criu.h"
#include "log.h"
#include "bdev.h"
#include "utils.h"
#include "attach.h"
#include "monitor.h"
#include "namespace.h"
+#include "network.h"
#include "lxclock.h"
+#include "sync.h"
#if HAVE_IFADDRS_H
#include <ifaddrs.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->lxc_unexp_conf) {
- lxc_conf_free(c->lxc_unexp_conf);
- c->lxc_unexp_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)
c->lxc_conf = lxc_conf_init();
- if (!c->lxc_unexp_conf) {
- c->lxc_unexp_conf = lxc_conf_init();
- if (c->lxc_unexp_conf)
- c->lxc_unexp_conf->unexpanded = true;
- }
- if (c->lxc_conf && c->lxc_unexp_conf &&
- !lxc_config_read(fname, c->lxc_conf,
- c->lxc_unexp_conf))
- return true;
- return false;
+ if (!c->lxc_conf)
+ return false;
+ if (lxc_config_read(fname, c->lxc_conf, false) != 0)
+ return false;
+ 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;
"/sbin/init",
NULL,
};
+ char *init_cmd[2];
/* container exists */
if (!c)
}
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;
}
- if (!argv)
- argv = default_args;
+ if (!argv) {
+ if (conf->init_cmd) {
+ init_cmd[0] = conf->init_cmd;
+ init_cmd[1] = NULL;
+ argv = init_cmd;
+ }
+ else
+ argv = default_args;
+ }
/*
* say, I'm not sure - what locks do we want here? Any?
* 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);
}
- 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;
+ char *p = alloca(strlen(path)+1);
+ mode_t mask = umask(0002);
+ ret = mkdir(path, 0770);
+ lasterr = errno;
+ umask(mask);
+ errno = lasterr;
+ if (ret) {
+ if (errno == EEXIST)
+ ret = 0;
+ else {
+ SYSERROR("failed to create container path %s", path);
+ return -1;
+ }
+ }
+ strcpy(p, path);
+ if (!lxc_list_empty(&conf->id_map) && chown_mapped_root(p, conf) != 0) {
+ ERROR("Failed to chown container dir");
+ ret = -1;
+ }
+ return ret;
+}
+
/*
* create the standard expected container dir
*/
free(s);
return false;
}
- ret = mkdir(s, 0755);
- if (ret) {
- if (errno == EEXIST)
- ret = 0;
- else
- SYSERROR("failed to create container path for %s", c->name);
- }
+ ret = do_create_container_dir(s, c->lxc_conf);
free(s);
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 */
if (geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) {
if (chown_mapped_root(bdev->dest, c->lxc_conf) < 0) {
ERROR("Error chowning %s to container root", bdev->dest);
+ suggest_default_idmap();
bdev_put(bdev);
return NULL;
}
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);
}
lxc_conf_free(c->lxc_conf);
c->lxc_conf = NULL;
}
- if (c->lxc_unexp_conf) {
- lxc_conf_free(c->lxc_unexp_conf);
- c->lxc_unexp_conf = NULL;
- }
}
}
-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);
/* reload config to get the rootfs */
lxc_conf_free(c->lxc_conf);
c->lxc_conf = NULL;
- c->lxc_unexp_conf = NULL;
if (!load_config_locked(c, c->configfile))
goto out_unlock;
// 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;
}
-static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key)
+static void do_clear_unexp_config_line(struct lxc_conf *conf, const char *key)
+{
+ if (strcmp(key, "lxc.cgroup") == 0)
+ clear_unexp_config_line(conf, key, true);
+ else if (strcmp(key, "lxc.network") == 0)
+ clear_unexp_config_line(conf, key, true);
+ else if (strcmp(key, "lxc.hook") == 0)
+ clear_unexp_config_line(conf, key, true);
+ else
+ clear_unexp_config_line(conf, key, false);
+ if (!do_append_unexp_config_line(conf, key, ""))
+ WARN("Error clearing configuration for %s", key);
+}
+
+static bool do_lxcapi_clear_config_item(struct lxc_container *c, const char *key)
{
int ret;
if (container_mem_lock(c))
return false;
ret = lxc_clear_config_item(c->lxc_conf, key);
+ if (!ret)
+ do_clear_unexp_config_line(c->lxc_conf, key);
container_mem_unlock(c);
return ret == 0;
}
-static inline bool enter_to_ns(struct lxc_container *c) {
- int netns, userns, ret = 0, init_pid = 0;;
- char new_netns_path[MAXPATHLEN];
- char new_userns_path[MAXPATHLEN];
+WRAP_API_1(bool, lxcapi_clear_config_item, const char *)
- if (!c->is_running(c))
- goto out;
-
- init_pid = c->init_pid(c);
+static inline bool enter_net_ns(struct lxc_container *c)
+{
+ pid_t pid = do_lxcapi_init_pid(c);
- /* Switch to new userns */
if ((geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) && access("/proc/self/ns/user", F_OK) == 0) {
- ret = snprintf(new_userns_path, MAXPATHLEN, "/proc/%d/ns/user", init_pid);
- if (ret < 0 || ret >= MAXPATHLEN)
- goto out;
-
- userns = open(new_userns_path, O_RDONLY);
- if (userns < 0) {
- SYSERROR("failed to open %s", new_userns_path);
- goto out;
- }
-
- if (setns(userns, CLONE_NEWUSER)) {
- SYSERROR("failed to setns for CLONE_NEWUSER");
- close(userns);
- goto out;
- }
- close(userns);
- }
-
- /* Switch to new netns */
- ret = snprintf(new_netns_path, MAXPATHLEN, "/proc/%d/ns/net", init_pid);
- if (ret < 0 || ret >= MAXPATHLEN)
- goto out;
-
- netns = open(new_netns_path, O_RDONLY);
- if (netns < 0) {
- SYSERROR("failed to open %s", new_netns_path);
- goto out;
- }
-
- if (setns(netns, CLONE_NEWNET)) {
- SYSERROR("failed to setns for CLONE_NEWNET");
- close(netns);
- goto out;
+ if (!switch_to_ns(pid, "user"))
+ return false;
}
- close(netns);
- return true;
-out:
- return false;
+ return switch_to_ns(pid, "net");
}
// used by qsort and bsearch functions for comparing names
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];
/* close the read-end of the pipe */
close(pipefd[0]);
- if (!enter_to_ns(c)) {
+ if (!enter_net_ns(c)) {
SYSERROR("failed to enter namespace");
goto out;
}
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];
/* close the read-end of the pipe */
close(pipefd[0]);
- if (!enter_to_ns(c)) {
+ if (!enter_net_ns(c)) {
SYSERROR("failed to enter namespace");
goto out;
}
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;
}
fout = fopen(alt_file, "w");
if (!fout)
goto out;
- write_config(fout, c->lxc_unexp_conf);
+ write_config(fout, c->lxc_conf);
fclose(fout);
ret = true;
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)
{
if (!c->lxc_conf)
c->lxc_conf = lxc_conf_init();
- if (!c->lxc_unexp_conf) {
- c->lxc_unexp_conf = lxc_conf_init();
- if (c->lxc_unexp_conf)
- c->lxc_unexp_conf->unexpanded = true;
- }
- if (!c->lxc_conf || !c->lxc_unexp_conf)
+ if (!c->lxc_conf)
return false;
config = lxc_getconfig(key);
if (!config)
return false;
- if (config->cb(key, v, c->lxc_unexp_conf) != 0)
+ if (config->cb(key, v, c->lxc_conf) != 0)
return false;
- return (0 == config->cb(key, v, c->lxc_conf));
+ 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);
}
}
- c->save_config(c, NULL);
+ if (!clone_update_unexp_hooks(c->lxc_conf, oldc->config_path,
+ c->config_path, oldc->name, c->name)) {
+ ERROR("Error saving new hooks in clone");
+ return -1;
+ }
+ do_lxcapi_save_config(c, NULL);
return 0;
}
-static void new_hwaddr(char *hwaddr)
-{
- FILE *f;
- f = fopen("/dev/urandom", "r");
- if (f) {
- unsigned int seed;
- int ret = fread(&seed, sizeof(seed), 1, f);
- if (ret != 1)
- seed = time(NULL);
- fclose(f);
- srand(seed);
- } else
- srand(time(NULL));
- snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x",
- rand() % 255, rand() % 255, rand() % 255);
-}
-
-static void network_new_hwaddrs(struct lxc_container *c)
-{
- struct lxc_list *it;
-
- lxc_list_for_each(it, &c->lxc_conf->network) {
- struct lxc_netdev *n = it->elem;
- if (n->hwaddr)
- new_hwaddr(n->hwaddr);
- }
-}
static int copy_fstab(struct lxc_container *oldc, struct lxc_container *c)
{
if (!oldpath)
return 0;
+ clear_unexp_config_line(c->lxc_conf, "lxc.mount", false);
+
char *p = strrchr(oldpath, '/');
if (!p)
return -1;
ERROR("error: allocating pathname");
return -1;
}
+ if (!do_append_unexp_config_line(c->lxc_conf, "lxc.mount", newpath)) {
+ ERROR("error saving new lxctab");
+ return -1;
+ }
return 0;
}
ERROR("Out of memory while setting storage path");
return -1;
}
- free(c->lxc_unexp_conf->rootfs.path);
- c->lxc_unexp_conf->rootfs.path = strdup(c->lxc_conf->rootfs.path);
- if (!c->lxc_unexp_conf->rootfs.path) {
- ERROR("Out of memory while setting storage path");
+ // We will simply append a new lxc.rootfs entry to the unexpanded config
+ clear_unexp_config_line(c->lxc_conf, "lxc.rootfs", false);
+ if (!do_append_unexp_config_line(c->lxc_conf, "lxc.rootfs", c->lxc_conf->rootfs.path)) {
+ ERROR("Error saving new rootfs to cloend config");
return -1;
}
if (flags & LXC_CLONE_SNAPSHOT)
return -1;
}
} else { // TODO come up with a better way
- if (bdev->dest)
- free(bdev->dest);
+ free(bdev->dest);
bdev->dest = strdup(bdev->src);
}
only rootfs gets converted (copied/snapshotted) on clone.
*/
-static int create_file_dirname(char *path)
+static int create_file_dirname(char *path, struct lxc_conf *conf)
{
char *p = strrchr(path, '/');
- int ret;
+ int ret = -1;
if (!p)
return -1;
*p = '\0';
- ret = mkdir(path, 0755);
- if (ret && errno != EEXIST)
- SYSERROR("creating container path %s", path);
+ ret = do_create_container_dir(path, conf);
*p = '/';
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)
struct lxc_container *c2 = NULL;
char newpath[MAXPATHLEN];
int ret, storage_copied = 0;
- const char *n, *l;
char *origroot = NULL;
struct clone_update_data data;
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))
}
// Make sure the container doesn't yet exist.
- n = newname ? newname : c->name;
- l = lxcpath ? lxcpath : c->get_config_path(c);
- ret = snprintf(newpath, MAXPATHLEN, "%s/%s/config", l, n);
+ if (!newname)
+ newname = c->name;
+ if (!lxcpath)
+ 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");
goto out;
goto out;
}
- ret = create_file_dirname(newpath);
+ ret = create_file_dirname(newpath, c->lxc_conf);
if (ret < 0 && errno != EEXIST) {
ERROR("Error creating container dir for %s", newpath);
goto out;
SYSERROR("open %s", newpath);
goto out;
}
- write_config(fout, c->lxc_unexp_conf);
+ write_config(fout, c->lxc_conf);
fclose(fout);
c->lxc_conf->rootfs.path = origroot;
- sprintf(newpath, "%s/%s/rootfs", l, n);
+ sprintf(newpath, "%s/%s/rootfs", lxcpath, newname);
if (mkdir(newpath, 0755) < 0) {
SYSERROR("error creating %s", newpath);
goto out;
}
}
- c2 = lxc_container_new(n, l);
+ c2 = lxc_container_new(newname, lxcpath);
if (!c2) {
- ERROR("clone: failed to create new container (%s %s)", n, l);
+ ERROR("clone: failed to create new container (%s %s)", newname,
+ lxcpath);
goto out;
}
if (ret < 0)
goto out;
+ clear_unexp_config_line(c2->lxc_conf, "lxc.utsname", false);
+
// update utsname
if (!set_config_item_locked(c2, "lxc.utsname", newname)) {
ERROR("Error setting new hostname");
}
// update macaddrs
- if (!(flags & LXC_CLONE_KEEPMACADDR))
- network_new_hwaddrs(c2);
+ if (!(flags & LXC_CLONE_KEEPMACADDR)) {
+ if (!network_new_hwaddrs(c2->lxc_conf)) {
+ ERROR("Error updating mac addresses");
+ goto out;
+ }
+ }
// We've now successfully created c2's storage, so clear it out if we
// fail after this
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;
if (!c || !lxcapi_is_defined(c))
return -1;
+ if (!bdev_can_backup(c->lxc_conf)) {
+ ERROR("%s's backing store cannot be backed up.", c->name);
+ ERROR("Your container must use another backing store type.");
+ return -1;
+ }
+
if (!get_snappath_dir(c, snappath))
return -1;
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);
}
+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;
+ if (am_unpriv()) {
+ ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
+ return false;
+ }
+
+ if (!ifname) {
+ ERROR("No source interface name given");
+ return false;
+ }
+
+ ret = lxc_netdev_isup(ifname);
+
+ if (ret > 0) {
+ /* netdev of ifname is up. */
+ ret = lxc_netdev_down(ifname);
+ if (ret)
+ goto err;
+ }
+
+ ret = lxc_netdev_move_by_name(ifname, do_lxcapi_init_pid(c), dst_ifname);
+ if (ret)
+ goto err;
+
+ return true;
+
+err:
+ return false;
+}
+
+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;
+
+ if (am_unpriv()) {
+ ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
+ return false;
+ }
+
+ if (!ifname) {
+ ERROR("No source interface name given");
+ return false;
+ }
+
+ pid_outside = getpid();
+ pid = fork();
+ if (pid < 0) {
+ ERROR("failed to fork task to get interfaces information");
+ return false;
+ }
+
+ if (pid == 0) { // child
+ int ret = 0;
+ if (!enter_net_ns(c)) {
+ ERROR("failed to enter namespace");
+ exit(-1);
+ }
+
+ ret = lxc_netdev_isup(ifname);
+ if (ret < 0)
+ exit(ret);
+
+ /* netdev of ifname is up. */
+ if (ret) {
+ ret = lxc_netdev_down(ifname);
+ if (ret)
+ exit(ret);
+ }
+
+ ret = lxc_netdev_move_by_name(ifname, pid_outside, dst_ifname);
+
+ /* -EINVAL means there is no netdev named as ifanme. */
+ if (ret == -EINVAL) {
+ ERROR("No network device named as %s.", ifname);
+ }
+ exit(ret);
+ }
+
+ if (wait_for_pid(pid) != 0)
+ return false;
+
+ return true;
+}
+
+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)
+{
+ 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;
+
+ if (access(path, F_OK) == 0) {
+ ERROR("please use a fresh directory for the dump directory\n");
+ return false;
+ }
+
+ if (!dump_net_info(c, directory))
+ return false;
+
+ pid = fork();
+ if (pid < 0)
+ return false;
+
+ if (pid == 0) {
+ struct criu_opts os;
+
+ os.action = "dump";
+ os.directory = directory;
+ os.c = c;
+ os.stop = stop;
+ os.verbose = verbose;
+
+ /* 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;
+ }
+
+ if (WIFEXITED(status)) {
+ return !WEXITSTATUS(status);
+ }
+
+ return false;
+ }
+}
+
+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;
+
+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->may_control = lxcapi_may_control;
c->add_device_node = lxcapi_add_device_node;
c->remove_device_node = lxcapi_remove_device_node;
-
- /* 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;
- }
+ c->attach_interface = lxcapi_attach_interface;
+ c->detach_interface = lxcapi_detach_interface;
+ c->checkpoint = lxcapi_checkpoint;
+ c->restore = lxcapi_restore;
return c;
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)
char **ct_name = NULL;
size_t len = 0;
struct lxc_container *c;
+ bool is_hashed;
if (!lxcpath)
lxcpath = lxc_global_config_value("lxc.lxcpath");
return -1;
while (getline(&line, &len, f) != -1) {
+
char *p = strrchr(line, ' '), *p2;
if (!p)
continue;
if (*p != 0x40)
continue;
p++;
- if (strncmp(p, lxcpath, lxcpath_len) != 0)
+
+ is_hashed = false;
+ if (strncmp(p, lxcpath, lxcpath_len) == 0) {
+ p += lxcpath_len;
+ } else if (strncmp(p, "lxc/", 4) == 0) {
+ p += 4;
+ is_hashed = true;
+ } else {
continue;
- p += lxcpath_len;
+ }
+
while (*p == '/')
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';
+ if (is_hashed) {
+ if (strncmp(lxcpath, lxc_cmd_get_lxcpath(p), lxcpath_len) != 0)
+ continue;
+ p = lxc_cmd_get_name(p);
+ }
+
if (array_contains(&ct_name, p, ct_name_cnt))
continue;
}
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++) {