]> git.proxmox.com Git - mirror_lxc.git/blobdiff - src/lxc/start.c
coverity: #1435805
[mirror_lxc.git] / src / lxc / start.c
index 10dfbcb7ee4b3a616f0d612ffbebcb498ae4a66a..638e195d6356573d38738d5cd50c743e293ebf54 100644 (file)
@@ -32,6 +32,7 @@
 #include <fcntl.h>
 #include <grp.h>
 #include <poll.h>
+#include <pthread.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -71,9 +72,9 @@
 #include "commands_utils.h"
 #include "conf.h"
 #include "confile_utils.h"
-#include "console.h"
 #include "error.h"
 #include "list.h"
+#include "lsm/lsm.h"
 #include "log.h"
 #include "lxccontainer.h"
 #include "lxclock.h"
 #include "namespace.h"
 #include "network.h"
 #include "start.h"
-#include "sync.h"
-#include "utils.h"
-#include "lsm/lsm.h"
 #include "storage/storage.h"
 #include "storage/storage_utils.h"
+#include "sync.h"
+#include "terminal.h"
+#include "utils.h"
+
+#ifndef HAVE_STRLCPY
+#include "include/strlcpy.h"
+#endif
 
 lxc_log_define(lxc_start, lxc);
 
@@ -142,11 +147,31 @@ static void lxc_put_nsfds(struct lxc_handler *handler)
        }
 }
 
-/* lxc_preserve_namespaces: open /proc/@pid/ns/@ns for each namespace specified
- * in clone_flags.
+static int lxc_try_preserve_ns(const int pid, const char *ns)
+{
+       int fd;
+
+       fd = lxc_preserve_ns(pid, ns);
+       if (fd < 0) {
+               if (errno != ENOENT) {
+                       SYSERROR("Failed to preserve %s namespace", ns);
+                       return -EINVAL;
+               }
+
+               WARN("%s - Kernel does not support preserving %s namespaces",
+                    strerror(errno), ns);
+               return -EOPNOTSUPP;
+       }
+
+       return fd;
+}
+
+/* lxc_try_preserve_namespaces: open /proc/@pid/ns/@ns for each namespace
+ * specified in ns_clone_flags.
  * Return true on success, false on failure.
  */
-static bool lxc_preserve_namespaces(struct lxc_handler *handler, int clone_flags, pid_t pid)
+static bool lxc_try_preserve_namespaces(struct lxc_handler *handler,
+                                       int ns_clone_flags, pid_t pid)
 {
        int i;
 
@@ -154,27 +179,32 @@ static bool lxc_preserve_namespaces(struct lxc_handler *handler, int clone_flags
                handler->nsfd[i] = -EBADF;
 
        for (i = 0; i < LXC_NS_MAX; i++) {
-               if ((clone_flags & ns_info[i].clone_flag) == 0)
+               int fd;
+
+               if ((ns_clone_flags & ns_info[i].clone_flag) == 0)
                        continue;
 
-               handler->nsfd[i] = lxc_preserve_ns(pid, ns_info[i].proc_name);
-               if (handler->nsfd[i] < 0)
-                       goto error;
+               fd = lxc_try_preserve_ns(pid, ns_info[i].proc_name);
+               if (fd < 0) {
+                       handler->nsfd[i] = -EBADF;
+
+                       /* Do not fail to start container on kernels that do
+                        * not support interacting with namespaces through
+                        * /proc.
+                        */
+                       if (fd == -EOPNOTSUPP)
+                               continue;
+
+                       lxc_put_nsfds(handler);
+                       return false;
+               }
 
-               DEBUG("Preserved %s namespace via fd %d", ns_info[i].proc_name, handler->nsfd[i]);
+               handler->nsfd[i] = fd;
+               DEBUG("Preserved %s namespace via fd %d", ns_info[i].proc_name,
+                     handler->nsfd[i]);
        }
 
        return true;
-
-error:
-       if (errno == ENOENT)
-               SYSERROR("Kernel does not support attaching to %s namespaces",
-                        ns_info[i].proc_name);
-       else
-               SYSERROR("Failed to open file descriptor for %s namespace",
-                        ns_info[i].proc_name);
-       lxc_put_nsfds(handler);
-       return false;
 }
 
 static int match_fd(int fd)
@@ -285,7 +315,7 @@ static int setup_signal_fd(sigset_t *oldmask)
                        return -EBADF;
        }
 
-       ret = sigprocmask(SIG_BLOCK, &mask, oldmask);
+       ret = pthread_sigmask(SIG_BLOCK, &mask, oldmask);
        if (ret < 0) {
                SYSERROR("Failed to set signal mask");
                return -EBADF;
@@ -355,6 +385,12 @@ static int signal_handler(int fd, uint32_t events, void *data,
                return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0;
        }
 
+       if (siginfo.ssi_signo != SIGCHLD) {
+               kill(hdlr->pid, siginfo.ssi_signo);
+               INFO("Forwarded signal %d to pid %d", siginfo.ssi_signo, hdlr->pid);
+               return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0;
+       }
+
        /* More robustness, protect ourself from a SIGCHLD sent
         * by a process different from the container init.
         */
@@ -364,12 +400,6 @@ static int signal_handler(int fd, uint32_t events, void *data,
                return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0;
        }
 
-       if (siginfo.ssi_signo != SIGCHLD) {
-               kill(hdlr->pid, siginfo.ssi_signo);
-               INFO("Forwarded signal %d to pid %d", siginfo.ssi_signo, hdlr->pid);
-               return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0;
-       }
-
        if (siginfo.ssi_code == CLD_STOPPED) {
                INFO("Container init process was stopped");
                return hdlr->init_died ? LXC_MAINLOOP_CLOSE : 0;
@@ -385,12 +415,12 @@ static int signal_handler(int fd, uint32_t events, void *data,
 int lxc_serve_state_clients(const char *name, struct lxc_handler *handler,
                            lxc_state_t state)
 {
+       size_t retlen;
        ssize_t ret;
        struct lxc_list *cur, *next;
        struct lxc_state_client *client;
        struct lxc_msg msg = {.type = lxc_msg_state, .value = state};
 
-       process_lock();
        if (state == THAWED)
                handler->state = RUNNING;
        else
@@ -400,12 +430,12 @@ int lxc_serve_state_clients(const char *name, struct lxc_handler *handler,
 
        if (lxc_list_empty(&handler->conf->state_clients)) {
                TRACE("No state clients registered");
-               process_unlock();
                return 0;
        }
 
-       strncpy(msg.name, name, sizeof(msg.name));
-       msg.name[sizeof(msg.name) - 1] = 0;
+       retlen = strlcpy(msg.name, name, sizeof(msg.name));
+       if (retlen >= sizeof(msg.name))
+               return -E2BIG;
 
        lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) {
                client = cur->elem;
@@ -437,7 +467,6 @@ int lxc_serve_state_clients(const char *name, struct lxc_handler *handler,
                free(cur->elem);
                free(cur);
        }
-       process_unlock();
 
        return 0;
 }
@@ -532,15 +561,15 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
        }
 
        if (has_console) {
-               struct lxc_console *console = &handler->conf->console;
+               struct lxc_terminal *console = &handler->conf->console;
 
-               ret = lxc_console_mainloop_add(&descr, console);
+               ret = lxc_terminal_mainloop_add(&descr, console);
                if (ret < 0) {
                        ERROR("Failed to add console handlers to mainloop");
                        goto out_mainloop_console;
                }
 
-               ret = lxc_console_mainloop_add(&descr_console, console);
+               ret = lxc_terminal_mainloop_add(&descr_console, console);
                if (ret < 0) {
                        ERROR("Failed to add console handlers to console mainloop");
                        goto out_mainloop_console;
@@ -590,7 +619,7 @@ void lxc_zero_handler(struct lxc_handler *handler)
 
        memset(handler, 0, sizeof(struct lxc_handler));
 
-       handler->clone_flags = -1;
+       handler->ns_clone_flags = -1;
 
        handler->pinfd = -1;
 
@@ -721,7 +750,7 @@ int lxc_init(const char *name, struct lxc_handler *handler)
                ERROR("Failed to set state to \"%s\"", lxc_state2str(STARTING));
                goto out_close_maincmd_fd;
        }
-       TRACE("set container state to \"STARTING\"");
+       TRACE("Set container state to \"STARTING\"");
 
        /* Start of environment variable setup for hooks. */
        if (name) {
@@ -807,27 +836,34 @@ int lxc_init(const char *name, struct lxc_handler *handler)
        TRACE("Set up signal fd");
 
        /* Do this after setting up signals since it might unblock SIGWINCH. */
-       ret = lxc_console_create(conf);
+       ret = lxc_terminal_setup(conf);
        if (ret < 0) {
                ERROR("Failed to create console");
                goto out_restore_sigmask;
        }
        TRACE("Created console");
 
-       ret = lxc_pty_map_ids(conf, &conf->console);
+       ret = lxc_terminal_map_ids(conf, &conf->console);
        if (ret < 0) {
                ERROR("Failed to chown console");
                goto out_restore_sigmask;
        }
        TRACE("Chowned console");
 
+       handler->cgroup_ops = cgroup_init(handler);
+       if (!handler->cgroup_ops) {
+               ERROR("Failed to initialize cgroup driver");
+               goto out_restore_sigmask;
+       }
+       TRACE("Initialized cgroup driver");
+
        INFO("Container \"%s\" is initialized", name);
        return 0;
 
 out_restore_sigmask:
-       sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
+       pthread_sigmask(SIG_SETMASK, &handler->oldmask, NULL);
 out_delete_tty:
-       lxc_delete_tty(&conf->tty_info);
+       lxc_delete_tty(&conf->ttys);
 out_aborting:
        lxc_set_state(name, handler, ABORTING);
 out_close_maincmd_fd:
@@ -843,6 +879,7 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
        struct lxc_list *cur, *next;
        char *namespaces[LXC_NS_MAX + 1];
        size_t namespace_count = 0;
+       struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
 
        /* The STOPPING state is there for future cleanup code which can take
         * awhile.
@@ -907,7 +944,8 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
        while (namespace_count--)
                free(namespaces[namespace_count]);
 
-       cgroup_destroy(handler);
+       cgroup_ops->destroy(cgroup_ops, handler);
+       cgroup_exit(cgroup_ops);
 
        if (handler->conf->reboot == 0) {
                /* For all new state clients simply close the command socket.
@@ -934,7 +972,8 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
                lxc_set_state(name, handler, STOPPED);
        }
 
-       if (run_lxc_hooks(name, "post-stop", handler->conf, NULL)) {
+       ret = run_lxc_hooks(name, "post-stop", handler->conf, NULL);
+       if (ret < 0) {
                ERROR("Failed to run lxc.hook.post-stop for container \"%s\"", name);
                if (handler->conf->reboot) {
                        WARN("Container will be stopped instead of rebooted");
@@ -948,12 +987,12 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
        }
 
        /* Reset mask set by setup_signal_fd. */
-       ret = sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
+       ret = pthread_sigmask(SIG_SETMASK, &handler->oldmask, NULL);
        if (ret < 0)
                WARN("%s - Failed to restore signal mask", strerror(errno));
 
-       lxc_console_delete(&handler->conf->console);
-       lxc_delete_tty(&handler->conf->tty_info);
+       lxc_terminal_delete(&handler->conf->console);
+       lxc_delete_tty(&handler->conf->ttys);
 
        /* The command socket is now closed, no more state clients can register
         * themselves from now on. So free the list of state clients.
@@ -995,32 +1034,6 @@ void lxc_abort(const char *name, struct lxc_handler *handler)
        }
 }
 
-static int lxc_set_death_signal(int signal)
-{
-       int ret;
-       pid_t ppid;
-
-       ret = prctl(PR_SET_PDEATHSIG, signal, 0, 0, 0);
-
-       /* Check whether we have been orphaned. */
-       ppid = (pid_t)syscall(SYS_getppid);
-       if (ppid == 1) {
-               pid_t self;
-
-               self = lxc_raw_getpid();
-               ret = kill(self, SIGKILL);
-               if (ret < 0)
-                       return -1;
-       }
-
-       if (ret < 0) {
-               SYSERROR("Failed to set PR_SET_PDEATHSIG to %d", signal);
-               return -1;
-       }
-
-       return 0;
-}
-
 static int do_start(void *data)
 {
        int ret;
@@ -1046,7 +1059,13 @@ static int do_start(void *data)
                goto out_warn_father;
        }
 
-       ret = sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
+       ret = lxc_ambient_caps_up();
+       if (ret < 0) {
+               SYSERROR("Failed to raise ambient capabilities");
+               goto out_warn_father;
+       }
+
+       ret = pthread_sigmask(SIG_SETMASK, &handler->oldmask, NULL);
        if (ret < 0) {
                SYSERROR("Failed to set signal mask");
                goto out_warn_father;
@@ -1063,7 +1082,7 @@ static int do_start(void *data)
        /* Unshare CLONE_NEWNET after CLONE_NEWUSER. See
         * https://github.com/lxc/lxd/issues/1978.
         */
-       if ((handler->clone_flags & (CLONE_NEWNET | CLONE_NEWUSER)) ==
+       if ((handler->ns_clone_flags & (CLONE_NEWNET | CLONE_NEWUSER)) ==
            (CLONE_NEWNET | CLONE_NEWUSER)) {
                ret = unshare(CLONE_NEWNET);
                if (ret < 0) {
@@ -1078,7 +1097,7 @@ static int do_start(void *data)
         */
        ret = lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE);
        if (ret < 0)
-               return -1;
+               goto out_error;
 
        ret = lxc_network_recv_veth_names_from_parent(handler);
        if (ret < 0) {
@@ -1108,11 +1127,9 @@ static int do_start(void *data)
                if (ret < 0 && (handler->am_root || errno != EPERM))
                        goto out_warn_father;
 
-               if (!handler->am_root) {
-                       ret = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
-                       if (ret < 0)
-                               goto out_warn_father;
-               }
+               ret = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+               if (ret < 0)
+                       goto out_warn_father;
 
                /* set{g,u}id() clears deathsignal */
                ret = lxc_set_death_signal(SIGKILL);
@@ -1173,7 +1190,7 @@ static int do_start(void *data)
         *
         *      8:cpuset:/
         */
-       if (handler->clone_flags & CLONE_NEWCGROUP) {
+       if (handler->ns_clone_flags & CLONE_NEWCGROUP) {
                ret = unshare(CLONE_NEWCGROUP);
                if (ret < 0) {
                        INFO("Failed to unshare CLONE_NEWCGROUP");
@@ -1226,14 +1243,14 @@ static int do_start(void *data)
        /* Some init's such as busybox will set sane tty settings on stdin,
         * stdout, stderr which it thinks is the console. We already set them
         * the way we wanted on the real terminal, and we want init to do its
-        * setup on its console ie. the pty allocated in lxc_console_create() so
+        * setup on its console ie. the pty allocated in lxc_terminal_setup() so
         * make sure that that pty is stdin,stdout,stderr.
         */
         if (handler->conf->console.slave >= 0) {
                 if (handler->backgrounded || handler->conf->is_execute == 0)
                         ret = set_stdfds(handler->conf->console.slave);
                 else
-                        ret = lxc_console_set_stdfds(handler->conf->console.slave);
+                        ret = lxc_terminal_set_stdfds(handler->conf->console.slave);
                 if (ret < 0) {
                        ERROR("Failed to redirect std{in,out,err} to pty file "
                              "descriptor %d", handler->conf->console.slave);
@@ -1259,7 +1276,6 @@ static int do_start(void *data)
 
        if (devnull_fd < 0) {
                devnull_fd = open_devnull();
-
                if (devnull_fd < 0)
                        goto out_warn_father;
        }
@@ -1348,6 +1364,12 @@ static int do_start(void *data)
        if (ret < 0)
                goto out_warn_father;
 
+       ret = lxc_ambient_caps_down();
+       if (ret < 0) {
+               SYSERROR("Failed to clear ambient capabilities");
+               goto out_warn_father;
+       }
+
        /* After this call, we are in error because this ops should not return
         * as it execs.
         */
@@ -1369,17 +1391,17 @@ out_error:
 static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
 {
        int i;
-       struct lxc_pty_info *pty_info;
+       struct lxc_terminal_info *tty;
        int ret = -1;
        int sock = handler->data_sock[1];
        struct lxc_conf *conf = handler->conf;
-       struct lxc_tty_info *tty_info = &conf->tty_info;
+       struct lxc_tty_info *ttys = &conf->ttys;
 
        if (!conf->tty)
                return 0;
 
-       tty_info->pty_info = malloc(sizeof(*tty_info->pty_info) * conf->tty);
-       if (!tty_info->pty_info)
+       ttys->tty = malloc(sizeof(*ttys->tty) * conf->tty);
+       if (!ttys->tty)
                return -1;
 
        for (i = 0; i < conf->tty; i++) {
@@ -1389,12 +1411,12 @@ static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
                if (ret < 0)
                        break;
 
-               pty_info = &tty_info->pty_info[i];
-               pty_info->busy = 0;
-               pty_info->master = ttyfds[0];
-               pty_info->slave = ttyfds[1];
+               tty = &ttys->tty[i];
+               tty->busy = 0;
+               tty->master = ttyfds[0];
+               tty->slave = ttyfds[1];
                TRACE("Received pty with master fd %d and slave fd %d from "
-                     "parent", pty_info->master, pty_info->slave);
+                     "parent", tty->master, tty->slave);
        }
        if (ret < 0)
                ERROR("Failed to receive %d ttys from child: %s", conf->tty,
@@ -1402,7 +1424,7 @@ static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
        else
                TRACE("Received %d ttys from child", conf->tty);
 
-       tty_info->nbtty = conf->tty;
+       ttys->nbtty = conf->tty;
 
        return ret;
 }
@@ -1415,10 +1437,10 @@ int resolve_clone_flags(struct lxc_handler *handler)
        for (i = 0; i < LXC_NS_MAX; i++) {
                if (conf->ns_keep != 0) {
                        if ((conf->ns_keep & ns_info[i].clone_flag) == 0)
-                               handler->clone_flags |= ns_info[i].clone_flag;
+                               handler->ns_clone_flags |= ns_info[i].clone_flag;
                } else if (conf->ns_clone != 0) {
                        if ((conf->ns_clone & ns_info[i].clone_flag) > 0)
-                               handler->clone_flags |= ns_info[i].clone_flag;
+                               handler->ns_clone_flags |= ns_info[i].clone_flag;
                } else {
                        if (i == LXC_NS_USER && lxc_list_empty(&handler->conf->id_map))
                                continue;
@@ -1429,13 +1451,13 @@ int resolve_clone_flags(struct lxc_handler *handler)
                        if (i == LXC_NS_CGROUP && !cgns_supported())
                                continue;
 
-                       handler->clone_flags |= ns_info[i].clone_flag;
+                       handler->ns_clone_flags |= ns_info[i].clone_flag;
                }
 
                if (!conf->ns_share[i])
                        continue;
 
-               handler->clone_flags &= ~ns_info[i].clone_flag;
+               handler->ns_clone_flags &= ~ns_info[i].clone_flag;
                TRACE("Sharing %s namespace", ns_info[i].proc_name);
        }
 
@@ -1464,13 +1486,21 @@ static inline int do_share_ns(void *arg)
                        continue;
 
                ret = setns(handler->nsfd[i], 0);
-               if (ret < 0)
+               if (ret < 0) {
+                       /*
+                        * Note that joining a user and/or mount namespace
+                        * requires the process is not multithreaded otherwise
+                        * setns() will fail here.
+                        */
+                       SYSERROR("Failed to inherit %s namespace",
+                                ns_info[i].proc_name);
                        return -1;
+               }
 
                DEBUG("Inherited %s namespace", ns_info[i].proc_name);
        }
 
-       flags = handler->on_clone_flags;
+       flags = handler->ns_on_clone_flags;
        flags |= CLONE_PARENT;
        handler->pid = lxc_raw_clone_cb(do_start, handler, flags);
        if (handler->pid < 0)
@@ -1494,8 +1524,9 @@ static int lxc_spawn(struct lxc_handler *handler)
        struct lxc_list *id_map;
        const char *name = handler->name;
        const char *lxcpath = handler->lxcpath;
-       bool cgroups_connected = false, share_ns = false;
+       bool share_ns = false;
        struct lxc_conf *conf = handler->conf;
+       struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
 
        id_map = &conf->id_map;
        wants_to_map_ids = !lxc_list_empty(id_map);
@@ -1528,7 +1559,7 @@ static int lxc_spawn(struct lxc_handler *handler)
                return -1;
        }
 
-       if (handler->clone_flags & CLONE_NEWNET) {
+       if (handler->ns_clone_flags & CLONE_NEWNET) {
                if (!lxc_list_empty(&conf->network)) {
 
                        /* Find gateway addresses from the link device, which is
@@ -1555,14 +1586,7 @@ static int lxc_spawn(struct lxc_handler *handler)
                }
        }
 
-       if (!cgroup_init(handler)) {
-               ERROR("Failed initializing cgroup support");
-               goto out_delete_net;
-       }
-
-       cgroups_connected = true;
-
-       if (!cgroup_create(handler)) {
+       if (!cgroup_ops->create(cgroup_ops, handler)) {
                ERROR("Failed creating cgroups");
                goto out_delete_net;
        }
@@ -1578,17 +1602,17 @@ static int lxc_spawn(struct lxc_handler *handler)
        }
 
        /* Create a process in a new set of namespaces. */
-       handler->on_clone_flags = handler->clone_flags;
-       if (handler->clone_flags & CLONE_NEWUSER) {
+       handler->ns_on_clone_flags = handler->ns_clone_flags;
+       if (handler->ns_clone_flags & CLONE_NEWUSER) {
                /* If CLONE_NEWUSER and CLONE_NEWNET was requested, we need to
                 * clone a new user namespace first and only later unshare our
                 * network namespace to ensure that network devices ownership is
                 * set up correctly.
                 */
-               handler->on_clone_flags &= ~CLONE_NEWNET;
+               handler->ns_on_clone_flags &= ~CLONE_NEWNET;
        }
        /* The cgroup namespace gets unshare()ed not clone()ed. */
-       handler->on_clone_flags &= ~CLONE_NEWCGROUP;
+       handler->ns_on_clone_flags &= ~CLONE_NEWCGROUP;
 
        if (share_ns) {
                pid_t attacher_pid;
@@ -1607,7 +1631,7 @@ static int lxc_spawn(struct lxc_handler *handler)
                }
        } else {
                handler->pid = lxc_raw_clone_cb(do_start, handler,
-                                               handler->on_clone_flags);
+                                               handler->ns_on_clone_flags);
        }
        if (handler->pid < 0) {
                SYSERROR(LXC_CLONE_ERROR);
@@ -1616,10 +1640,10 @@ static int lxc_spawn(struct lxc_handler *handler)
        TRACE("Cloned child process %d", handler->pid);
 
        for (i = 0; i < LXC_NS_MAX; i++)
-               if (handler->on_clone_flags & ns_info[i].clone_flag)
+               if (handler->ns_on_clone_flags & ns_info[i].clone_flag)
                        INFO("Cloned %s", ns_info[i].flag_name);
 
-       if (!lxc_preserve_namespaces(handler, handler->on_clone_flags, handler->pid)) {
+       if (!lxc_try_preserve_namespaces(handler, handler->ns_on_clone_flags, handler->pid)) {
                ERROR("Failed to preserve cloned namespaces for lxc.hook.stop");
                goto out_delete_net;
        }
@@ -1651,33 +1675,31 @@ static int lxc_spawn(struct lxc_handler *handler)
        if (ret < 0)
                goto out_delete_net;
 
-       if (!cgroup_create_legacy(handler)) {
-               ERROR("Failed to setup legacy cgroups for container \"%s\"", name);
-               goto out_delete_net;
-       }
-
-       if (!cgroup_setup_limits(handler, false)) {
+       if (!cgroup_ops->setup_limits(cgroup_ops, handler->conf, false)) {
                ERROR("Failed to setup cgroup limits for container \"%s\"", name);
                goto out_delete_net;
        }
 
-       if (!cgroup_enter(handler))
+       if (!cgroup_ops->enter(cgroup_ops, handler->pid))
                goto out_delete_net;
 
-       if (!cgroup_chown(handler))
+       if (!cgroup_ops->chown(cgroup_ops, handler->conf))
                goto out_delete_net;
 
        /* Now we're ready to preserve the network namespace */
-       ret = lxc_preserve_ns(handler->pid, "net");
+       ret = lxc_try_preserve_ns(handler->pid, "net");
        if (ret < 0) {
-               ERROR("%s - Failed to preserve net namespace", strerror(errno));
-               goto out_delete_net;
+               if (ret != -EOPNOTSUPP) {
+                       ERROR("%s - Failed to preserve net namespace", strerror(errno));
+                       goto out_delete_net;
+               }
+       } else {
+               handler->nsfd[LXC_NS_NET] = ret;
+               DEBUG("Preserved net namespace via fd %d", ret);
        }
-       handler->nsfd[LXC_NS_NET] = ret;
-       DEBUG("Preserved net namespace via fd %d", ret);
 
        /* Create the network configuration. */
-       if (handler->clone_flags & CLONE_NEWNET) {
+       if (handler->ns_clone_flags & CLONE_NEWNET) {
                ret = lxc_network_move_created_netdev_priv(handler->lxcpath,
                                                           handler->name,
                                                           &conf->network,
@@ -1726,24 +1748,25 @@ static int lxc_spawn(struct lxc_handler *handler)
        if (ret < 0)
                goto out_delete_net;
 
-       if (!cgroup_setup_limits(handler, true)) {
+       if (!cgroup_ops->setup_limits(cgroup_ops, handler->conf, true)) {
                ERROR("Failed to setup legacy device cgroup controller limits");
                goto out_delete_net;
        }
        TRACE("Set up legacy device cgroup controller limits");
 
-       cgroup_disconnect();
-       cgroups_connected = false;
-
-       if (handler->clone_flags & CLONE_NEWCGROUP) {
+       if (handler->ns_clone_flags & CLONE_NEWCGROUP) {
                /* Now we're ready to preserve the cgroup namespace */
-               ret = lxc_preserve_ns(handler->pid, "cgroup");
+               ret = lxc_try_preserve_ns(handler->pid, "cgroup");
                if (ret < 0) {
-                       ERROR("%s - Failed to preserve cgroup namespace", strerror(errno));
-                       goto out_delete_net;
+                       if (ret != -EOPNOTSUPP) {
+                               ERROR("%s - Failed to preserve cgroup namespace",
+                                     strerror(errno));
+                               goto out_delete_net;
+                       }
+               } else {
+                       handler->nsfd[LXC_NS_CGROUP] = ret;
+                       DEBUG("Preserved cgroup namespace via fd %d", ret);
                }
-               handler->nsfd[LXC_NS_CGROUP] = ret;
-               DEBUG("Preserved cgroup namespace via fd %d", ret);
        }
 
        ret = snprintf(pidstr, 20, "%d", handler->pid);
@@ -1807,10 +1830,7 @@ static int lxc_spawn(struct lxc_handler *handler)
        return 0;
 
 out_delete_net:
-       if (cgroups_connected)
-               cgroup_disconnect();
-
-       if (handler->clone_flags & CLONE_NEWNET)
+       if (handler->ns_clone_flags & CLONE_NEWNET)
                lxc_delete_network(handler);
 
 out_abort:
@@ -1826,7 +1846,7 @@ out_abort:
 
 int __lxc_start(const char *name, struct lxc_handler *handler,
                struct lxc_operations* ops, void *data, const char *lxcpath,
-               bool backgrounded)
+               bool backgrounded, int *error_num)
 {
        int ret, status;
        struct lxc_conf *conf = handler->conf;
@@ -1922,6 +1942,8 @@ int __lxc_start(const char *name, struct lxc_handler *handler,
 
        lxc_monitor_send_exit_code(name, status, handler->lxcpath);
        lxc_error_set_and_log(handler->pid, status);
+       if (error_num)
+               *error_num = handler->exit_status;
 
 out_fini:
        lxc_delete_network(handler);
@@ -1967,13 +1989,14 @@ static struct lxc_operations start_ops = {
 };
 
 int lxc_start(const char *name, char *const argv[], struct lxc_handler *handler,
-             const char *lxcpath, bool backgrounded)
+             const char *lxcpath, bool backgrounded, int *error_num)
 {
        struct start_args start_arg = {
                .argv = argv,
        };
 
-       return __lxc_start(name, handler, &start_ops, &start_arg, lxcpath, backgrounded);
+       TRACE("Doing lxc_start");
+       return __lxc_start(name, handler, &start_ops, &start_arg, lxcpath, backgrounded, error_num);
 }
 
 static void lxc_destroy_container_on_signal(struct lxc_handler *handler,