]> git.proxmox.com Git - mirror_lxc.git/blobdiff - src/lxc/lxccontainer.c
pass on reboot flag and delete old veth on reboot
[mirror_lxc.git] / src / lxc / lxccontainer.c
index 8e611c7d38a56ee1c24c6804c5d6d051385c6a5c..223e78e096bd4813cdd13889e8aae9ea48a1cce7 100644 (file)
 #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"
@@ -79,9 +90,17 @@ return -1;
 }
 #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 */
@@ -214,14 +233,10 @@ static void lxc_container_free(struct lxc_container *c)
        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;
@@ -230,18 +245,14 @@ static void lxc_container_free(struct lxc_container *c)
                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);
 }
@@ -306,7 +317,7 @@ int lxc_container_put(struct lxc_container *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;
@@ -329,7 +340,49 @@ out:
        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;
 
@@ -339,6 +392,8 @@ static const char *lxcapi_state(struct lxc_container *c)
        return lxc_state2str(s);
 }
 
+WRAP_API(const char *, lxcapi_state)
+
 static bool is_stopped(struct lxc_container *c)
 {
        lxc_state_t s;
@@ -346,19 +401,21 @@ static bool is_stopped(struct lxc_container *c)
        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)
@@ -370,7 +427,9 @@ static bool lxcapi_freeze(struct lxc_container *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)
@@ -382,7 +441,9 @@ static bool lxcapi_unfreeze(struct lxc_container *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)
@@ -392,13 +453,23 @@ static int lxcapi_console_getfd(struct lxc_container *c, int *ttynum, int *maste
        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;
@@ -406,16 +477,20 @@ static pid_t lxcapi_init_pid(struct lxc_container *c)
        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_conf && !lxc_config_read(fname, c->lxc_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;
@@ -452,7 +527,9 @@ static bool lxcapi_load_config(struct lxc_container *c, const char *alt_file)
        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;
@@ -461,14 +538,13 @@ static bool lxcapi_want_daemonize(struct lxc_container *c, bool state)
                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;
@@ -481,7 +557,9 @@ static bool lxcapi_want_close_all_fds(struct lxc_container *c, bool state)
        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;
 
@@ -492,8 +570,9 @@ static bool lxcapi_wait(struct lxc_container *c, const char *state, int timeout)
        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;
@@ -505,9 +584,11 @@ static bool wait_on_daemonized_start(struct lxc_container *c, int pid)
        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;
@@ -540,7 +621,7 @@ static bool am_single_threaded(void)
  * 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;
@@ -550,6 +631,7 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
                "/sbin/init",
                NULL,
        };
+       char *init_cmd[2];
 
        /* container exists */
        if (!c)
@@ -564,7 +646,7 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
        }
        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);
@@ -582,12 +664,19 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
        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?
@@ -596,6 +685,7 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
        * while container is running...
        */
        if (daemonize) {
+               char title[2048];
                lxc_monitord_spawn(c->config_path);
 
                pid_t pid = fork();
@@ -610,25 +700,32 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
                        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()) {
@@ -645,6 +742,8 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
                if (pid_fp == NULL) {
                        SYSERROR("Failed to create pidfile '%s' for '%s'",
                                 c->pidfile, c->name);
+                       if (daemonize)
+                               exit(1);
                        return false;
                }
 
@@ -652,6 +751,8 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
                        SYSERROR("Failed to write '%s'", c->pidfile);
                        fclose(pid_fp);
                        pid_fp = NULL;
+                       if (daemonize)
+                               exit(1);
                        return false;
                }
 
@@ -659,16 +760,25 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
                pid_fp = NULL;
        }
 
-reboot:
        conf->reboot = 0;
-       ret = lxc_start(c->name, argv, conf, c->config_path);
 
-       if (conf->reboot) {
+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 == 1) {
                INFO("container requested reboot");
-               conf->reboot = 0;
+               conf->reboot = 2;
                goto reboot;
        }
 
+out:
        if (c->pidfile) {
                unlink(c->pidfile);
                free(c->pidfile);
@@ -681,6 +791,15 @@ reboot:
                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
  */
@@ -694,6 +813,8 @@ static bool lxcapi_startl(struct lxc_container *c, int useinit, ...)
        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);
@@ -704,7 +825,7 @@ static bool lxcapi_startl(struct lxc_container *c, int useinit, ...)
        }
 
        /* 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) {
@@ -714,10 +835,11 @@ out:
                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;
 
@@ -729,6 +851,33 @@ static bool lxcapi_stop(struct lxc_container *c)
        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
  */
@@ -746,20 +895,11 @@ static bool create_container_dir(struct lxc_container *c)
                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.
@@ -779,7 +919,7 @@ static struct bdev *do_bdev_create(struct lxc_container *c, const char *type,
                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);
@@ -793,7 +933,7 @@ static struct bdev *do_bdev_create(struct lxc_container *c, const char *type,
                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 */
@@ -801,6 +941,7 @@ static struct bdev *do_bdev_create(struct lxc_container *c, const char *type,
        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;
                }
@@ -809,40 +950,6 @@ static struct bdev *do_bdev_create(struct lxc_container *c, const char *type,
        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;
@@ -851,7 +958,7 @@ static char *lxcbasename(char *path)
        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;
@@ -873,13 +980,8 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
                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;
@@ -921,8 +1023,7 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
                                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);
                }
 
@@ -1170,7 +1271,7 @@ static bool prepend_lxc_header(char *path, const char *t, char *const argv[])
                fprintf(f, "%02x", md_value[i]);
        fprintf(f, "\n");
 #endif
-       fprintf(f, "# For additional config options, please look at lxc.conf(5)\n");
+       fprintf(f, "# For additional config options, please look at lxc.container.conf(5)\n");
        if (fwrite(contents, 1, flen, f) != flen) {
                SYSERROR("Writing original contents");
                free(contents);
@@ -1196,13 +1297,16 @@ out_error:
 
 static void lxcapi_clear_config(struct lxc_container *c)
 {
-       if (c && c->lxc_conf) {
-               lxc_conf_free(c->lxc_conf);
-               c->lxc_conf = NULL;
+       if (c) {
+               if (c->lxc_conf) {
+                       lxc_conf_free(c->lxc_conf);
+                       c->lxc_conf = NULL;
+               }
        }
 }
 
-static bool lxcapi_destroy(struct lxc_container *c);
+#define do_lxcapi_clear_config(c) lxcapi_clear_config(c)
+
 /*
  * lxcapi_create:
  * create a container with the given parameters.
@@ -1217,7 +1321,7 @@ static bool lxcapi_destroy(struct lxc_container *c);
  * @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[])
 {
@@ -1243,14 +1347,14 @@ static bool lxcapi_create(struct lxc_container *c, const char *t,
         * 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;
                }
@@ -1270,7 +1374,7 @@ static bool lxcapi_create(struct lxc_container *c, const char *t,
        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;
@@ -1306,7 +1410,7 @@ static bool lxcapi_create(struct lxc_container *c, const char *t,
                }
 
                /* 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);
@@ -1329,7 +1433,7 @@ static bool lxcapi_create(struct lxc_container *c, const char *t,
 
        // 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)) {
@@ -1343,32 +1447,47 @@ out_unlock:
        if (partial_fd >= 0)
                remove_partial(c, partial_fd);
 out:
-       if (!ret && c)
-               lxcapi_destroy(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;
@@ -1377,18 +1496,20 @@ static bool lxcapi_shutdown(struct lxc_container *c, int timeout)
        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, ...)
 {
@@ -1399,6 +1520,8 @@ static bool lxcapi_createl(struct lxc_container *c, const char *t,
        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.
@@ -1411,14 +1534,29 @@ static bool lxcapi_createl(struct lxc_container *c, const char *t,
                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;
 
@@ -1427,60 +1565,23 @@ static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key)
        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
@@ -1554,7 +1655,7 @@ static bool remove_from_array(char ***names, char *cname, int size)
        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];
@@ -1581,7 +1682,7 @@ static char** lxcapi_get_interfaces(struct lxc_container *c)
                /* 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;
                }
@@ -1641,7 +1742,9 @@ static char** lxcapi_get_interfaces(struct lxc_container *c)
        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];
@@ -1671,7 +1774,7 @@ static char** lxcapi_get_ips(struct lxc_container *c, const char* interface, con
                /* 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;
                }
@@ -1758,7 +1861,9 @@ static char** lxcapi_get_ips(struct lxc_container *c, const char* interface, con
        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;
 
@@ -1771,7 +1876,9 @@ static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char
        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;
 
@@ -1779,12 +1886,14 @@ static char* lxcapi_get_running_config_item(struct lxc_container *c, const char
                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);
@@ -1804,7 +1913,9 @@ static int lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv,
        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;
@@ -1817,7 +1928,7 @@ static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file)
 
        // 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;
                }
@@ -1857,6 +1968,8 @@ out:
        return ret;
 }
 
+WRAP_API_1(bool, lxcapi_save_config, const char *)
+
 static bool mod_rdep(struct lxc_container *c, bool inc)
 {
        char path[MAXPATHLEN];
@@ -1945,12 +2058,12 @@ static void mod_all_rdeps(struct lxc_container *c, bool inc)
                lxc_container_put(p);
        }
 out:
-       if (lxcpath) free(lxcpath);
-       if (lxcname) free(lxcname);
+       free(lxcpath);
+       free(lxcname);
        fclose(f);
 }
 
-static bool has_snapshots(struct lxc_container *c)
+static bool has_fs_snapshots(struct lxc_container *c)
 {
        char path[MAXPATHLEN];
        int ret, v;
@@ -1974,10 +2087,38 @@ out:
        return bret;
 }
 
+static bool has_snapshots(struct lxc_container *c)
+{
+       char path[MAXPATHLEN];
+       struct dirent dirent, *direntp;
+       int count=0;
+       DIR *dir;
+
+       if (!get_snappath_dir(c, path))
+               return false;
+       dir = opendir(path);
+       if (!dir)
+               return false;
+       while (!readdir_r(dir, &dirent, &direntp)) {
+               if (!direntp)
+                       break;
+
+               if (!strcmp(direntp->d_name, "."))
+                       continue;
+
+               if (!strcmp(direntp->d_name, ".."))
+                       continue;
+               count++;
+               break;
+       }
+       closedir(dir);
+       return count > 0;
+}
+
 static int lxc_rmdir_onedev_wrapper(void *data)
 {
        char *arg = (char *) data;
-       return lxc_rmdir_onedev(arg);
+       return lxc_rmdir_onedev(arg, "snaps");
 }
 
 static int do_bdev_destroy(struct lxc_conf *conf)
@@ -2012,15 +2153,16 @@ static int bdev_destroy_wrapper(void *data)
        return do_bdev_destroy(conf);
 }
 
-// do we want the api to support --force, or leave that to the caller?
-static bool lxcapi_destroy(struct lxc_container *c)
+static bool container_destroy(struct lxc_container *c)
 {
        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;
 
@@ -2030,16 +2172,47 @@ static bool lxcapi_destroy(struct lxc_container *c)
                goto out;
        }
 
-       if (c->lxc_conf && has_snapshots(c)) {
-               ERROR("container %s has dependent snapshots", c->name);
-               goto out;
+       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 (c->lxc_conf && c->lxc_conf->rootfs.path && c->lxc_conf->rootfs.mount) {
+       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;
@@ -2048,13 +2221,13 @@ static bool lxcapi_destroy(struct lxc_container *c)
 
        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);
+               ret = lxc_rmdir_onedev(path, "snaps");
        if (ret < 0) {
                ERROR("Error destroying container directory for %s", c->name);
                goto out;
@@ -2066,6 +2239,38 @@ out:
        return bret;
 }
 
+static bool do_lxcapi_destroy(struct lxc_container *c)
+{
+       if (!c || !lxcapi_is_defined(c))
+               return false;
+       if (has_snapshots(c)) {
+               ERROR("Container %s has snapshots;  not removing", c->name);
+               return false;
+       }
+
+       if (has_fs_snapshots(c)) {
+               ERROR("container %s has snapshots on its rootfs", c->name);
+               return false;
+       }
+
+       return container_destroy(c);
+}
+
+WRAP_API(bool, lxcapi_destroy)
+
+static bool do_lxcapi_destroy_with_snapshots(struct lxc_container *c)
+{
+       if (!c || !lxcapi_is_defined(c))
+               return false;
+       if (!lxcapi_snapshot_destroy_all(c)) {
+               ERROR("Error deleting all snapshots");
+               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;
@@ -2077,10 +2282,12 @@ static bool set_config_item_locked(struct lxc_container *c, const char *key, con
        config = lxc_getconfig(key);
        if (!config)
                return false;
-       return (0 == config->cb(key, v, c->lxc_conf));
+       if (config->cb(key, v, c->lxc_conf) != 0)
+               return false;
+       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;
 
@@ -2096,6 +2303,8 @@ static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, con
        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)
@@ -2137,14 +2346,13 @@ static bool set_config_filename(struct lxc_container *c)
                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;
@@ -2177,14 +2385,14 @@ static bool lxcapi_set_config_path(struct lxc_container *c, const char *path)
                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;
 
@@ -2203,7 +2411,9 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys,
        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;
 
@@ -2222,6 +2432,8 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c
        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);
@@ -2333,37 +2545,15 @@ static int copyhooks(struct lxc_container *oldc, struct lxc_container *c)
                }
        }
 
-       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)
 {
@@ -2374,6 +2564,8 @@ 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;
@@ -2398,6 +2590,10 @@ static int copy_fstab(struct lxc_container *oldc, struct lxc_container *c)
                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;
 }
@@ -2467,6 +2663,12 @@ static int copy_storage(struct lxc_container *c0, struct lxc_container *c,
                ERROR("Out of memory while setting storage path");
                return -1;
        }
+       // 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)
                copy_rdepends(c, c0);
        if (need_rdep) {
@@ -2535,8 +2737,7 @@ static int clone_update_rootfs(struct clone_update_data *data)
                        return -1;
                }
        } else { // TODO come up with a better way
-               if (bdev->dest)
-                       free(bdev->dest);
+               free(bdev->dest);
                bdev->dest = strdup(bdev->src);
        }
 
@@ -2608,22 +2809,20 @@ sudo lxc-clone -o o1 -n n1 -s -L|-fssize fssize -v|--vgname vgname \
 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)
@@ -2631,13 +2830,12 @@ static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *n
        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))
@@ -2649,9 +2847,11 @@ static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *n
        }
 
        // 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;
@@ -2661,7 +2861,7 @@ static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *n
                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;
@@ -2681,7 +2881,7 @@ static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *n
        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;
@@ -2694,9 +2894,10 @@ static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *n
                }
        }
 
-       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;
        }
 
@@ -2705,6 +2906,8 @@ static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *n
        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");
@@ -2724,8 +2927,12 @@ static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *n
        }
 
        // 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
@@ -2772,7 +2979,19 @@ out:
        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;
@@ -2780,6 +2999,10 @@ static bool lxcapi_rename(struct lxc_container *c, const char *newname)
        if (!c || !c->name || !c->config_path || !c->lxc_conf)
                return false;
 
+       if (has_fs_snapshots(c) || has_snapshots(c)) {
+               ERROR("Renaming a container with snapshots is not supported");
+               return false;
+       }
        bdev = bdev_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
        if (!bdev) {
                ERROR("Failed to find original backing store type");
@@ -2796,22 +3019,30 @@ static bool lxcapi_rename(struct lxc_container *c, const char *newname)
        if (newc && lxcapi_is_defined(newc))
                lxc_container_put(newc);
 
-       if (!lxcapi_destroy(c)) {
+       if (!container_destroy(c)) {
                ERROR("Could not destroy existing container %s", c->name);
                return false;
        }
        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;
@@ -2830,6 +3061,15 @@ static int lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t
        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;
@@ -2846,16 +3086,51 @@ static int get_next_index(const char *lxcpath, char *cname)
        }
 }
 
-static int lxcapi_snapshot(struct lxc_container *c, const char *commentfile)
+static bool get_snappath_dir(struct lxc_container *c, char *snappath)
+{
+       int ret;
+       /*
+        * If the old style snapshot path exists, use it
+        * /var/lib/lxc -> /var/lib/lxcsnaps
+        */
+       ret = snprintf(snappath, MAXPATHLEN, "%ssnaps", c->config_path);
+       if (ret < 0 || ret >= MAXPATHLEN)
+               return false;
+       if (dir_exists(snappath)) {
+               ret = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
+               if (ret < 0 || ret >= MAXPATHLEN)
+                       return false;
+               return true;
+       }
+
+       /*
+        * Use the new style path
+        * /var/lib/lxc -> /var/lib/lxc + c->name + /snaps + \0
+        */
+       ret = snprintf(snappath, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name);
+       if (ret < 0 || ret >= MAXPATHLEN)
+               return false;
+       return true;
+}
+
+static int do_lxcapi_snapshot(struct lxc_container *c, const char *commentfile)
 {
        int i, flags, ret;
        struct lxc_container *c2;
        char snappath[MAXPATHLEN], newname[20];
 
-       // /var/lib/lxc -> /var/lib/lxcsnaps \0
-       ret = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
-       if (ret < 0 || ret >= MAXPATHLEN)
+       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;
+
        i = get_next_index(snappath, c->name);
 
        if (mkdir_p(snappath, 0755) < 0) {
@@ -2880,7 +3155,7 @@ static int lxcapi_snapshot(struct lxc_container *c, const char *commentfile)
                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;
@@ -2928,16 +3203,14 @@ static int lxcapi_snapshot(struct lxc_container *c, const char *commentfile)
        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)
@@ -2986,10 +3259,10 @@ static char *get_timestamp(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 dirlen, count = 0, ret;
+       int count = 0, ret;
        struct dirent dirent, *direntp;
        struct lxc_snapshot *snaps =NULL, *nsnaps;
        DIR *dir;
@@ -2997,9 +3270,7 @@ static int lxcapi_snapshot_list(struct lxc_container *c, struct lxc_snapshot **r
        if (!c || !lxcapi_is_defined(c))
                return -1;
 
-       // snappath is ${lxcpath}snaps/${lxcname}/
-       dirlen = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
-       if (dirlen < 0 || dirlen >= MAXPATHLEN) {
+       if (!get_snappath_dir(c, snappath)) {
                ERROR("path name too long");
                return -1;
        }
@@ -3064,10 +3335,12 @@ out_free:
        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,ret;
+       int flags = 0;
        struct lxc_container *snap, *rest;
        struct bdev *bdev;
        bool b = false;
@@ -3075,6 +3348,11 @@ static bool lxcapi_snapshot_restore(struct lxc_container *c, const char *snapnam
        if (!c || !c->name || !c->config_path)
                return false;
 
+       if (has_fs_snapshots(c)) {
+               ERROR("container rootfs has dependent snapshots");
+               return false;
+       }
+
        bdev = bdev_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
        if (!bdev) {
                ERROR("Failed to find original backing store type");
@@ -3084,8 +3362,7 @@ static bool lxcapi_snapshot_restore(struct lxc_container *c, const char *snapnam
        if (!newname)
                newname = c->name;
 
-       ret = snprintf(clonelxcpath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
-       if (ret < 0 || ret >= MAXPATHLEN) {
+       if (!get_snappath_dir(c, clonelxcpath)) {
                bdev_put(bdev);
                return false;
        }
@@ -3100,7 +3377,7 @@ static bool lxcapi_snapshot_restore(struct lxc_container *c, const char *snapnam
        }
 
        if (strcmp(c->name, newname) == 0) {
-               if (!lxcapi_destroy(c)) {
+               if (!container_destroy(c)) {
                        ERROR("Could not destroy existing container %s", newname);
                        lxc_container_put(snap);
                        bdev_put(bdev);
@@ -3121,43 +3398,100 @@ static bool lxcapi_snapshot_restore(struct lxc_container *c, const char *snapnam
        return b;
 }
 
-static bool lxcapi_snapshot_destroy(struct lxc_container *c, const char *snapname)
+WRAP_API_2(bool, lxcapi_snapshot_restore, const char *, const char *)
+
+static bool do_snapshot_destroy(const char *snapname, const char *clonelxcpath)
 {
-       int ret;
-       char clonelxcpath[MAXPATHLEN];
        struct lxc_container *snap = NULL;
-
-       if (!c || !c->name || !c->config_path)
-               return false;
-
-       ret = snprintf(clonelxcpath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
-       if (ret < 0 || ret >= MAXPATHLEN)
-               goto err;
+       bool bret = false;
 
        snap = lxc_container_new(snapname, clonelxcpath);
-       if (!snap || !lxcapi_is_defined(snap)) {
+       if (!snap) {
                ERROR("Could not find snapshot %s", snapname);
                goto err;
        }
 
-       if (!lxcapi_destroy(snap)) {
+       if (!do_lxcapi_destroy(snap)) {
                ERROR("Could not destroy snapshot %s", snapname);
                goto err;
        }
-       lxc_container_put(snap);
+       bret = true;
 
-       return true;
 err:
        if (snap)
                lxc_container_put(snap);
-       return false;
+       return bret;
+}
+
+static bool remove_all_snapshots(const char *path)
+{
+       DIR *dir;
+       struct dirent dirent, *direntp;
+       bool bret = true;
+
+       dir = opendir(path);
+       if (!dir) {
+               SYSERROR("opendir on snapshot path %s", path);
+               return false;
+       }
+       while (!readdir_r(dir, &dirent, &direntp)) {
+               if (!direntp)
+                       break;
+               if (!strcmp(direntp->d_name, "."))
+                       continue;
+               if (!strcmp(direntp->d_name, ".."))
+                       continue;
+               if (!do_snapshot_destroy(direntp->d_name, path)) {
+                       bret = false;
+                       continue;
+               }
+       }
+
+       closedir(dir);
+
+       if (rmdir(path))
+               SYSERROR("Error removing directory %s", path);
+
+       return bret;
 }
 
-static bool lxcapi_may_control(struct lxc_container *c)
+static bool do_lxcapi_snapshot_destroy(struct lxc_container *c, const char *snapname)
+{
+       char clonelxcpath[MAXPATHLEN];
+
+       if (!c || !c->name || !c->config_path || !snapname)
+               return false;
+
+       if (!get_snappath_dir(c, clonelxcpath))
+               return false;
+
+       return do_snapshot_destroy(snapname, clonelxcpath);
+}
+
+WRAP_API_1(bool, lxcapi_snapshot_destroy, const char *)
+
+static bool do_lxcapi_snapshot_destroy_all(struct lxc_container *c)
+{
+       char clonelxcpath[MAXPATHLEN];
+
+       if (!c || !c->name || !c->config_path)
+               return false;
+
+       if (!get_snappath_dir(c, clonelxcpath))
+               return false;
+
+       return remove_all_snapshots(clonelxcpath);
+}
+
+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)
 {
@@ -3221,7 +3555,7 @@ static bool add_remove_device_node(struct lxc_container *c, const char *src_path
        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;
        }
@@ -3245,17 +3579,17 @@ static bool add_remove_device_node(struct lxc_container *c, const char *src_path
        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;
                }
@@ -3264,7 +3598,7 @@ static bool add_remove_device_node(struct lxc_container *c, const char *src_path
        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__);
@@ -3273,7 +3607,9 @@ static bool lxcapi_add_device_node(struct lxc_container *c, const char *src_path
        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__);
@@ -3282,6 +3618,213 @@ static bool lxcapi_remove_device_node(struct lxc_container *c, const char *src_p
        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;
@@ -3291,18 +3834,23 @@ static int lxcapi_attach_run_waitl(struct lxc_container *c, lxc_attach_options_t
        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;
 }
 
@@ -3310,6 +3858,9 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
 {
        struct lxc_container *c;
 
+       if (!name)
+               return NULL;
+
        c = malloc(sizeof(*c));
        if (!c) {
                fprintf(stderr, "failed to malloc lxc_container\n");
@@ -3356,7 +3907,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
 
        if (ongoing_create(c) == 2) {
                ERROR("Error: %s creation was not completed", c->name);
-               lxcapi_destroy(c);
+               container_destroy(c);
                lxcapi_clear_config(c);
        }
        c->daemonize = true;
@@ -3381,6 +3932,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
        c->wait = lxcapi_wait;
        c->set_config_item = lxcapi_set_config_item;
        c->destroy = lxcapi_destroy;
+       c->destroy_with_snapshots = lxcapi_destroy_with_snapshots;
        c->rename = lxcapi_rename;
        c->save_config = lxcapi_save_config;
        c->get_keys = lxcapi_get_keys;
@@ -3406,15 +3958,14 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
        c->snapshot_list = lxcapi_snapshot_list;
        c->snapshot_restore = lxcapi_snapshot_restore;
        c->snapshot_destroy = lxcapi_snapshot_destroy;
+       c->snapshot_destroy_all = lxcapi_snapshot_destroy_all;
        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;
 
@@ -3489,7 +4040,7 @@ int list_defined_containers(const char *lxcpath, char ***names, struct lxc_conta
                                        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)
@@ -3533,6 +4084,7 @@ int list_active_containers(const char *lxcpath, char ***nret,
        char **ct_name = NULL;
        size_t len = 0;
        struct lxc_container *c;
+       bool is_hashed;
 
        if (!lxcpath)
                lxcpath = lxc_global_config_value("lxc.lxcpath");
@@ -3548,6 +4100,7 @@ int list_active_containers(const char *lxcpath, char ***nret,
                return -1;
 
        while (getline(&line, &len, f) != -1) {
+
                char *p = strrchr(line, ' '), *p2;
                if (!p)
                        continue;
@@ -3555,18 +4108,32 @@ int list_active_containers(const char *lxcpath, char ***nret,
                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;
 
@@ -3622,8 +4189,7 @@ free_ct_name:
        }
 
 out:
-       if (line)
-               free(line);
+       free(line);
 
        fclose(f);
        return ret;
@@ -3695,16 +4261,13 @@ free_ct_list:
        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++) {