chiark / gitweb /
bus: connect directly via kdbus in sd_bus_open_system_container()
[elogind.git] / src / nspawn / nspawn.c
index 80903a7aef0069b8a2b810edaa2133fcb0b685c0..b3ca10ea9116c67168e904592f55e532d194e1a3 100644 (file)
@@ -33,8 +33,6 @@
 #include <sys/prctl.h>
 #include <sys/capability.h>
 #include <getopt.h>
-#include <sys/poll.h>
-#include <sys/epoll.h>
 #include <termios.h>
 #include <sys/signalfd.h>
 #include <grp.h>
@@ -43,9 +41,9 @@
 #include <sys/socket.h>
 #include <linux/netlink.h>
 
-#include <systemd/sd-daemon.h>
-#include <systemd/sd-bus.h>
-
+#include "sd-daemon.h"
+#include "sd-bus.h"
+#include "sd-id128.h"
 #include "log.h"
 #include "util.h"
 #include "mkdir.h"
 #include "strv.h"
 #include "path-util.h"
 #include "loopback-setup.h"
-#include "sd-id128.h"
 #include "dev-setup.h"
 #include "fdset.h"
 #include "build.h"
 #include "fileio.h"
 #include "bus-util.h"
+#include "bus-error.h"
 #include "ptyfwd.h"
+#include "bus-kernel.h"
 
 #ifndef TTY_GID
 #define TTY_GID 5
@@ -129,6 +128,7 @@ static int help(void) {
                "     --read-only           Mount the root directory read-only\n"
                "     --capability=CAP      In addition to the default, retain specified\n"
                "                           capability\n"
+               "     --drop-capability=CAP Drop the specified capability from the default set\n"
                "     --link-journal=MODE   Link up guest journal, one of no, auto, guest, host\n"
                "  -j                       Equivalent to --link-journal=host\n"
                "     --bind=PATH[:PATH]    Bind mount a file or directory from the host into\n"
@@ -147,6 +147,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_UUID,
                 ARG_READ_ONLY,
                 ARG_CAPABILITY,
+                ARG_DROP_CAPABILITY,
                 ARG_LINK_JOURNAL,
                 ARG_BIND,
                 ARG_BIND_RO
@@ -162,12 +163,13 @@ static int parse_argv(int argc, char *argv[]) {
                 { "uuid",            required_argument, NULL, ARG_UUID            },
                 { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
                 { "capability",      required_argument, NULL, ARG_CAPABILITY      },
+                { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY },
                 { "link-journal",    required_argument, NULL, ARG_LINK_JOURNAL    },
                 { "bind",            required_argument, NULL, ARG_BIND            },
                 { "bind-ro",         required_argument, NULL, ARG_BIND_RO         },
                 { "machine",         required_argument, NULL, 'M'                 },
                 { "slice",           required_argument, NULL, 'S'                 },
-                { NULL,              0,                 NULL, 0                   }
+                {}
         };
 
         int c, r;
@@ -180,8 +182,7 @@ static int parse_argv(int argc, char *argv[]) {
                 switch (c) {
 
                 case 'h':
-                        help();
-                        return 0;
+                        return help();
 
                 case ARG_VERSION:
                         puts(PACKAGE_STRING);
@@ -192,7 +193,7 @@ static int parse_argv(int argc, char *argv[]) {
                         free(arg_directory);
                         arg_directory = canonicalize_file_name(optarg);
                         if (!arg_directory) {
-                                log_error("Failed to canonicalize root directory.");
+                                log_error("Invalid root directory: %m");
                                 return -ENOMEM;
                         }
 
@@ -246,7 +247,8 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_read_only = true;
                         break;
 
-                case ARG_CAPABILITY: {
+                case ARG_CAPABILITY:
+                case ARG_DROP_CAPABILITY: {
                         char *state, *word;
                         size_t length;
 
@@ -265,7 +267,11 @@ static int parse_argv(int argc, char *argv[]) {
                                 }
 
                                 free(t);
-                                arg_retain |= 1ULL << (uint64_t) cap;
+
+                                if (c == ARG_CAPABILITY)
+                                        arg_retain |= 1ULL << (uint64_t) cap;
+                                else
+                                        arg_retain &= ~(1ULL << (uint64_t) cap);
                         }
 
                         break;
@@ -331,8 +337,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
+                        assert_not_reached("Unhandled option");
                 }
         }
 
@@ -413,39 +418,46 @@ static int mount_binds(const char *dest, char **l, unsigned long flags) {
         char **x, **y;
 
         STRV_FOREACH_PAIR(x, y, l) {
-                _cleanup_free_ char *where = NULL;
+                char *where;
                 struct stat source_st, dest_st;
+                int r;
 
                 if (stat(*x, &source_st) < 0) {
                         log_error("failed to stat %s: %m", *x);
                         return -errno;
                 }
 
-                where = strjoin(dest, "/", *y, NULL);
-                if (!where)
-                        return log_oom();
-
-                if (stat(where, &dest_st) == 0) {
+                where = strappenda(dest, *y);
+                r = stat(where, &dest_st);
+                if (r == 0) {
                         if ((source_st.st_mode & S_IFMT) != (dest_st.st_mode & S_IFMT)) {
                                 log_error("The file types of %s and %s do not match. Refusing bind mount",
                                                 *x, where);
                                 return -EINVAL;
                         }
-                } else {
-                        /* Create the mount point, but be conservative -- refuse to create block
-                         * and char devices. */
-                        if (S_ISDIR(source_st.st_mode))
-                                mkdir_p_label(where, 0755);
-                        else if (S_ISFIFO(source_st.st_mode))
-                                mkfifo(where, 0644);
-                        else if (S_ISSOCK(source_st.st_mode))
-                                mknod(where, 0644 | S_IFSOCK, 0);
-                        else if (S_ISREG(source_st.st_mode))
-                                touch(where);
-                        else {
-                                log_error("Refusing to create mountpoint for file: %s", *x);
-                                return -ENOTSUP;
+                } else if (errno == ENOENT) {
+                        r = mkdir_parents_label(where, 0755);
+                        if (r < 0) {
+                                log_error("Failed to bind mount %s: %s", *x, strerror(-r));
+                                return r;
                         }
+                } else {
+                        log_error("Failed to bind mount %s: %s", *x, strerror(errno));
+                        return -errno;
+                }
+                /* Create the mount point, but be conservative -- refuse to create block
+                * and char devices. */
+                if (S_ISDIR(source_st.st_mode))
+                        mkdir_label(where, 0755);
+                else if (S_ISFIFO(source_st.st_mode))
+                        mkfifo(where, 0644);
+                else if (S_ISSOCK(source_st.st_mode))
+                        mknod(where, 0644 | S_IFSOCK, 0);
+                else if (S_ISREG(source_st.st_mode))
+                        touch(where);
+                else {
+                        log_error("Refusing to create mountpoint for file: %s", *x);
+                        return -ENOTSUP;
                 }
 
                 if (mount(*x, where, "bind", MS_BIND, NULL) < 0) {
@@ -923,6 +935,26 @@ static int setup_journal(const char *directory) {
         return 0;
 }
 
+static int setup_kdbus(const char *dest, const char *path) {
+        const char *p;
+
+        if (!path)
+                return 0;
+
+        p = strappenda(dest, "/dev/kdbus");
+        if (mkdir(p, 0755) < 0) {
+                log_error("Failed to create kdbus path: %m");
+                return  -errno;
+        }
+
+        if (mount(path, p, "bind", MS_BIND, NULL) < 0) {
+                log_error("Failed to mount kdbus namespace path: %m");
+                return -errno;
+        }
+
+        return 0;
+}
+
 static int drop_capabilities(void) {
         return capability_bounding_set_drop(~arg_retain, false);
 }
@@ -955,10 +987,62 @@ static int register_machine(void) {
                         strempty(arg_directory),
                         !isempty(arg_slice), "Slice", "s", arg_slice);
         if (r < 0) {
-                log_error("Failed to register machine: %s", error.message ? error.message : strerror(-r));
+                log_error("Failed to register machine: %s", bus_error_message(&error, r));
+                return r;
+        }
+
+        return 0;
+}
+
+static int terminate_machine(pid_t pid) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        _cleanup_bus_unref_ sd_bus *bus = NULL;
+        const char *path;
+        int r;
+
+        r = sd_bus_default_system(&bus);
+        if (r < 0) {
+                log_error("Failed to open system bus: %s", strerror(-r));
                 return r;
         }
 
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.machine1",
+                        "/org/freedesktop/machine1",
+                        "org.freedesktop.machine1.Manager",
+                        "GetMachineByPID",
+                        &error,
+                        &reply,
+                        "u",
+                        (uint32_t) pid);
+        if (r < 0) {
+                /* Note that the machine might already have been
+                 * cleaned up automatically, hence don't consider it a
+                 * failure if we cannot get the machine object. */
+                log_debug("Failed to get machine: %s", bus_error_message(&error, r));
+                return 0;
+        }
+
+        r = sd_bus_message_read(reply, "o", &path);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.machine1",
+                        path,
+                        "org.freedesktop.machine1.Machine",
+                        "Terminate",
+                        &error,
+                        NULL,
+                        NULL);
+        if (r < 0) {
+                log_debug("Failed to terminate machine: %s", bus_error_message(&error, r));
+                return 0;
+        }
+
         return 0;
 }
 
@@ -976,12 +1060,14 @@ static bool audit_enabled(void) {
 int main(int argc, char *argv[]) {
         pid_t pid = 0;
         int r = EXIT_FAILURE, k;
-        _cleanup_close_ int master = -1;
+        _cleanup_close_ int master = -1, kdbus_fd = -1;
         int n_fd_passed;
         const char *console = NULL;
         sigset_t mask;
         _cleanup_close_pipe_ int kmsg_socket_pair[2] = { -1, -1 };
         _cleanup_fdset_free_ FDSet *fds = NULL;
+        _cleanup_free_ char *kdbus_namespace = NULL;
+        const char *ns;
 
         log_parse_environment();
         log_open();
@@ -1011,7 +1097,7 @@ int main(int argc, char *argv[]) {
         path_kill_slashes(arg_directory);
 
         if (!arg_machine) {
-                arg_machine = strdup(path_get_file_name(arg_directory));
+                arg_machine = strdup(basename(arg_directory));
                 if (!arg_machine) {
                         log_oom();
                         goto finish;
@@ -1082,6 +1168,13 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
+        ns = strappenda("machine-", arg_machine);
+        kdbus_fd = bus_kernel_create_namespace(ns, &kdbus_namespace);
+        if (r < 0)
+                log_debug("Failed to create kdbus namespace: %s", strerror(-r));
+        else
+                log_debug("Successfully created kdbus namespace as %s", kdbus_namespace);
+
         if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) {
                 log_error("Failed to create kmsg socket pair.");
                 goto finish;
@@ -1233,6 +1326,9 @@ int main(int argc, char *argv[]) {
                         if (mount_binds(arg_directory, arg_bind_ro, MS_RDONLY) < 0)
                                 goto child_fail;
 
+                        if (setup_kdbus(arg_directory, kdbus_namespace) < 0)
+                                goto child_fail;
+
                         if (chdir(arg_directory) < 0) {
                                 log_error("chdir(%s) failed: %m", arg_directory);
                                 goto child_fail;
@@ -1391,6 +1487,9 @@ int main(int argc, char *argv[]) {
                 putc('\n', stdout);
 
                 /* Kill if it is not dead yet anyway */
+                terminate_machine(pid);
+
+                /* Redundant, but better safe than sorry */
                 kill(pid, SIGKILL);
 
                 k = wait_for_terminate(pid, &status);