]> git.proxmox.com Git - mirror_lxc.git/blobdiff - src/lxc/start.c
lxc-start: exit early and cleanly if we have insufficient privs
[mirror_lxc.git] / src / lxc / start.c
index 92f44e3d272dae38b20db007245d507a747275fd..91ce5fa821771e0a33ceee3337e34ed5306d3c3f 100644 (file)
@@ -21,7 +21,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
-#include "../config.h"
+#include "config.h"
+
 #include <stdio.h>
 #undef _GNU_SOURCE
 #include <string.h>
@@ -32,7 +33,6 @@
 #include <signal.h>
 #include <fcntl.h>
 #include <termios.h>
-#include <namespace.h>
 #include <sys/param.h>
 #include <sys/file.h>
 #include <sys/mount.h>
@@ -114,7 +114,6 @@ int signalfd(int fd, const sigset_t *mask, int flags)
 
 #include "start.h"
 #include "conf.h"
-#include "cgroup.h"
 #include "log.h"
 #include "cgroup.h"
 #include "error.h"
@@ -126,12 +125,10 @@ int signalfd(int fd, const sigset_t *mask, int flags)
 #include "commands.h"
 #include "console.h"
 #include "sync.h"
+#include "namespace.h"
 
 lxc_log_define(lxc_start, lxc);
 
-LXC_TTY_HANDLER(SIGINT);
-LXC_TTY_HANDLER(SIGQUIT);
-
 static int match_fd(int fd)
 {
        return (fd == 0 || fd == 1 || fd == 2);
@@ -142,7 +139,6 @@ int lxc_check_inherited(int fd_to_ignore)
        struct dirent dirent, *direntp;
        int fd, fddir;
        DIR *dir;
-       int ret = 0;
 
        dir = opendir("/proc/self/fd");
        if (!dir) {
@@ -153,9 +149,6 @@ int lxc_check_inherited(int fd_to_ignore)
        fddir = dirfd(dir);
 
        while (!readdir_r(dir, &dirent, &direntp)) {
-               char procpath[64];
-               char path[PATH_MAX];
-
                if (!direntp)
                        break;
 
@@ -172,25 +165,15 @@ int lxc_check_inherited(int fd_to_ignore)
 
                if (match_fd(fd))
                        continue;
-               /*
-                * found inherited fd
-                */
-               ret = -1;
-
-               snprintf(procpath, sizeof(procpath), "/proc/self/fd/%d", fd);
 
-               if (readlink(procpath, path, sizeof(path)) == -1)
-                       ERROR("readlink(%s) failed : %m", procpath);
-               else
-                       ERROR("inherited fd %d on %s", fd, path);
+               WARN("inherited fd %d", fd);
        }
 
-       if (closedir(dir))
-               ERROR("failed to close directory");
-       return ret;
+       closedir(dir); /* cannot fail */
+       return 0;
 }
 
-static int setup_sigchld_fd(sigset_t *oldmask)
+static int setup_signal_fd(sigset_t *oldmask)
 {
        sigset_t mask;
        int fd;
@@ -222,7 +205,7 @@ static int setup_sigchld_fd(sigset_t *oldmask)
        return fd;
 }
 
-static int sigchld_handler(int fd, void *data,
+static int signal_handler(int fd, void *data,
                           struct lxc_epoll_descr *descr)
 {
        struct signalfd_siginfo siginfo;
@@ -305,7 +288,7 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
                goto out_sigfd;
        }
 
-       if (lxc_mainloop_add_handler(&descr, sigfd, sigchld_handler, &pid)) {
+       if (lxc_mainloop_add_handler(&descr, sigfd, signal_handler, &pid)) {
                ERROR("failed to add handler for the signal");
                goto out_mainloop_open;
        }
@@ -320,9 +303,11 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
                goto out_mainloop_open;
        }
 
-       if (lxc_utmp_mainloop_add(&descr, handler)) {
-               ERROR("failed to add utmp handler to mainloop");
-               goto out_mainloop_open;
+       if (handler->conf->need_utmp_watch) {
+               if (lxc_utmp_mainloop_add(&descr, handler)) {
+                       ERROR("failed to add utmp handler to mainloop");
+                       goto out_mainloop_open;
+               }
        }
 
        return lxc_mainloop(&descr);
@@ -334,10 +319,17 @@ out_sigfd:
        return -1;
 }
 
+extern int lxc_caps_check(void);
+
 struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
 {
        struct lxc_handler *handler;
 
+       if (!lxc_caps_check()) {
+               ERROR("Not running with sufficient privilege");
+               return NULL;
+       }
+
        handler = malloc(sizeof(*handler));
        if (!handler)
                return NULL;
@@ -371,7 +363,7 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
        /* the signal fd has to be created before forking otherwise
         * if the child process exits before we setup the signal fd,
         * the event will be lost and the command will be stuck */
-       handler->sigfd = setup_sigchld_fd(&handler->oldmask);
+       handler->sigfd = setup_signal_fd(&handler->oldmask);
        if (handler->sigfd < 0) {
                ERROR("failed to set sigchild fd handler");
                goto out_delete_console;
@@ -402,7 +394,7 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
        lxc_set_state(name, handler, STOPPING);
        lxc_set_state(name, handler, STOPPED);
 
-       /* reset mask set by setup_sigchld_fd */
+       /* reset mask set by setup_signal_fd */
        if (sigprocmask(SIG_SETMASK, &handler->oldmask, NULL))
                WARN("failed to restore sigprocmask");
 
@@ -419,6 +411,28 @@ void lxc_abort(const char *name, struct lxc_handler *handler)
                kill(handler->pid, SIGKILL);
 }
 
+#include <sys/reboot.h>
+#include <linux/reboot.h>
+
+static int must_drop_cap_sys_boot(void)
+{
+       FILE *f = fopen("/proc/sys/kernel/ctrl-alt-del", "r");
+       int ret;
+       int v;
+
+       if (!f)
+               return 1;
+
+       ret = fscanf(f, "%d", &v);
+       fclose(f);
+       if (ret != 1)
+               return 1;
+       ret = reboot(v ? LINUX_REBOOT_CMD_CAD_ON : LINUX_REBOOT_CMD_CAD_OFF);
+       if (ret != -1)
+               return 1;
+       return 0;
+}
+
 static int do_start(void *data)
 {
        struct lxc_handler *handler = data;
@@ -453,10 +467,14 @@ static int do_start(void *data)
                goto out_warn_father;
        }
 
-       if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0)) {
-               SYSERROR("failed to remove CAP_SYS_BOOT capability");
-               return -1;
-       }
+       if (must_drop_cap_sys_boot()) {
+               if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0)) {
+                       SYSERROR("failed to remove CAP_SYS_BOOT capability");
+                       return -1;
+               }
+               handler->conf->need_utmp_watch = 1;
+       } else
+               handler->conf->need_utmp_watch = 0;
 
        close(handler->sigfd);
 
@@ -484,17 +502,26 @@ int lxc_spawn(struct lxc_handler *handler)
 
                clone_flags |= CLONE_NEWNET;
 
+               /* Find gateway addresses from the link device, which is
+                * no longer accessible inside the container. Do this
+                * before creating network interfaces, since goto
+                * out_delete_net does not work before lxc_clone. */
+               if (lxc_find_gateway_addresses(handler)) {
+                       ERROR("failed to find gateway addresses");
+                       lxc_sync_fini(handler);
+                       return -1;
+               }
+
                /* that should be done before the clone because we will
                 * fill the netdev index and use them in the child
                 */
-               if (lxc_create_network(&handler->conf->network)) {
+               if (lxc_create_network(handler)) {
                        ERROR("failed to create the network");
                        lxc_sync_fini(handler);
                        return -1;
                }
        }
 
-
        /* Create a process in a new set of namespaces */
        handler->pid = lxc_clone(do_start, handler, clone_flags);
        if (handler->pid < 0) {
@@ -507,7 +534,7 @@ int lxc_spawn(struct lxc_handler *handler)
        if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
                failed_before_rename = 1;
 
-       if (lxc_rename_nsgroup(name, handler))
+       if (lxc_cgroup_create(name, handler->pid))
                goto out_delete_net;
 
        if (failed_before_rename)
@@ -569,10 +596,6 @@ int __lxc_start(const char *name, struct lxc_conf *conf,
                goto out_fini;
        }
 
-       /* Avoid signals from terminal */
-       LXC_TTY_ADD_HANDLER(SIGINT);
-       LXC_TTY_ADD_HANDLER(SIGQUIT);
-
        err = lxc_poll(name, handler);
        if (err) {
                ERROR("mainloop exited with an error");
@@ -582,11 +605,27 @@ int __lxc_start(const char *name, struct lxc_conf *conf,
        while (waitpid(handler->pid, &status, 0) < 0 && errno == EINTR)
                continue;
 
+        if (!WIFSIGNALED(status)) {
+                printf("child process exited but was not signaled\n");
+                return -1;
+        }
+
+       switch(WTERMSIG(status)) {
+       case SIGINT: /* halt */
+               DEBUG("Container halting");
+               break;
+       case SIGHUP: /* reboot */
+               DEBUG("Container rebooting");
+               handler->conf->reboot = 1;
+               break;
+       default:
+               DEBUG("unknown exit status for init: %d\n", WTERMSIG(status));
+               break;
+        }
+
        err =  lxc_error_set_and_log(handler->pid, status);
 out_fini:
-       LXC_TTY_DEL_HANDLER(SIGQUIT);
-       LXC_TTY_DEL_HANDLER(SIGINT);
-       lxc_unlink_nsgroup(name);
+       lxc_cgroup_destroy(name);
        lxc_fini(name, handler);
        return err;
 
@@ -632,5 +671,6 @@ int lxc_start(const char *name, char *const argv[], struct lxc_conf *conf)
        if (lxc_check_inherited(-1))
                return -1;
 
+       conf->need_utmp_watch = 1;
        return __lxc_start(name, conf, &start_ops, &start_arg);
 }