]> git.proxmox.com Git - mirror_lxc.git/blobdiff - src/lxc/start.c
coverity: #1435805
[mirror_lxc.git] / src / lxc / start.c
index eb7331f8a895bef8b75e74577b13c9c8d072b339..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>
 #include "terminal.h"
 #include "utils.h"
 
+#ifndef HAVE_STRLCPY
+#include "include/strlcpy.h"
+#endif
+
 lxc_log_define(lxc_start, lxc);
 
 extern void mod_all_rdeps(struct lxc_container *c, bool inc);
@@ -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 ns_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 ns_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 ns_clone_fl
                handler->nsfd[i] = -EBADF;
 
        for (i = 0; i < LXC_NS_MAX; i++) {
+               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,6 +415,7 @@ 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;
@@ -402,8 +433,9 @@ int lxc_serve_state_clients(const char *name, struct lxc_handler *handler,
                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;
@@ -818,11 +850,18 @@ int lxc_init(const char *name, struct lxc_handler *handler)
        }
        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->ttys);
 out_aborting:
@@ -840,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.
@@ -904,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.
@@ -946,7 +987,7 @@ 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));
 
@@ -1018,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;
@@ -1050,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) {
@@ -1080,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);
@@ -1319,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.
         */
@@ -1435,8 +1486,16 @@ 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);
        }
@@ -1465,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);
@@ -1526,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;
        }
@@ -1590,7 +1643,7 @@ static int lxc_spawn(struct lxc_handler *handler)
                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->ns_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;
        }
@@ -1622,25 +1675,28 @@ static int lxc_spawn(struct lxc_handler *handler)
        if (ret < 0)
                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->ns_clone_flags & CLONE_NEWNET) {
@@ -1692,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->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);
@@ -1773,9 +1830,6 @@ static int lxc_spawn(struct lxc_handler *handler)
        return 0;
 
 out_delete_net:
-       if (cgroups_connected)
-               cgroup_disconnect();
-
        if (handler->ns_clone_flags & CLONE_NEWNET)
                lxc_delete_network(handler);
 
@@ -1792,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;
@@ -1888,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);
@@ -1933,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,