chiark / gitweb /
core: fix SetUnitProperties
[elogind.git] / src / nspawn / nspawn.c
index cb2f05c02d0c74c1cce1d4db64dce29840c70605..cd757c497b73ee63867b855aba686c1f94e90ea9 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"
 
 #ifndef TTY_GID
@@ -129,6 +127,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 +146,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 +162,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 +181,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 +192,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;
                         }
 
@@ -224,6 +224,9 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case 'S':
                         arg_slice = strdup(optarg);
+                        if (!arg_slice)
+                                return log_oom();
+
                         break;
 
                 case 'M':
@@ -243,7 +246,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;
 
@@ -262,7 +266,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;
@@ -315,11 +323,11 @@ static int parse_argv(int argc, char *argv[]) {
 
                         r = strv_extend(x, a);
                         if (r < 0)
-                                return r;
+                                return log_oom();
 
                         r = strv_extend(x, b);
                         if (r < 0)
-                                return r;
+                                return log_oom();
 
                         break;
                 }
@@ -328,8 +336,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");
                 }
         }
 
@@ -952,13 +959,65 @@ 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;
+}
+
 static bool audit_enabled(void) {
         int fd;
 
@@ -976,11 +1035,8 @@ int main(int argc, char *argv[]) {
         _cleanup_close_ int master = -1;
         int n_fd_passed;
         const char *console = NULL;
-        struct termios saved_attr, raw_attr;
         sigset_t mask;
-        bool saved_attr_valid = false;
-        struct winsize ws;
-        int kmsg_socket_pair[2] = { -1, -1 };
+        _cleanup_close_pipe_ int kmsg_socket_pair[2] = { -1, -1 };
         _cleanup_fdset_free_ FDSet *fds = NULL;
 
         log_parse_environment();
@@ -1075,24 +1131,13 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
-        log_info("Spawning namespace container on %s (console is %s).", arg_directory, console);
-
-        if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
-                ioctl(master, TIOCSWINSZ, &ws);
+        log_info("Spawning container %s on %s. Press ^] three times within 1s to abort execution.", arg_machine, arg_directory);
 
         if (unlockpt(master) < 0) {
                 log_error("Failed to unlock tty: %m");
                 goto finish;
         }
 
-        if (tcgetattr(STDIN_FILENO, &saved_attr) >= 0) {
-                saved_attr_valid = true;
-
-                raw_attr = saved_attr;
-                cfmakeraw(&raw_attr);
-                raw_attr.c_lflag &= ~ECHO;
-        }
-
         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;
@@ -1106,18 +1151,6 @@ int main(int argc, char *argv[]) {
 
         for (;;) {
                 siginfo_t status;
-                int pipefd[2], pipefd2[2];
-
-                if (pipe2(pipefd, O_NONBLOCK|O_CLOEXEC) < 0) {
-                        log_error("pipe2(): %m");
-                        goto finish;
-                }
-
-                if (pipe2(pipefd2, O_NONBLOCK|O_CLOEXEC) < 0) {
-                        log_error("pipe2(): %m");
-                        close_pipe(pipefd);
-                        goto finish;
-                }
 
                 pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL);
                 if (pid < 0) {
@@ -1152,21 +1185,9 @@ int main(int argc, char *argv[]) {
                         if (envp[n_env])
                                 n_env ++;
 
-                        /* Wait for the parent process to log our PID */
-                        close_nointr_nofail(pipefd[1]);
-                        fd_wait_for_event(pipefd[0], POLLHUP, -1);
-                        close_nointr_nofail(pipefd[0]);
-
                         close_nointr_nofail(master);
                         master = -1;
 
-                        if (saved_attr_valid) {
-                                if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) {
-                                        log_error("Failed to set terminal attributes: %m");
-                                        goto child_fail;
-                                }
-                        }
-
                         close_nointr(STDIN_FILENO);
                         close_nointr(STDOUT_FILENO);
                         close_nointr(STDERR_FILENO);
@@ -1206,8 +1227,6 @@ int main(int argc, char *argv[]) {
                                 goto child_fail;
                         }
 
-                        close_pipe(pipefd2);
-
                         r = register_machine();
                         if (r < 0)
                                 goto finish;
@@ -1416,25 +1435,26 @@ int main(int argc, char *argv[]) {
                         _exit(EXIT_FAILURE);
                 }
 
-                log_info("Init process in the container running as PID %lu.", (unsigned long) pid);
-                close_nointr_nofail(pipefd[0]);
-                close_nointr_nofail(pipefd[1]);
-
-                /* Wait for the child process to establish cgroup hierarchy */
-                close_nointr_nofail(pipefd2[1]);
-                fd_wait_for_event(pipefd2[0], POLLHUP, -1);
-                close_nointr_nofail(pipefd2[0]);
-
                 fdset_free(fds);
                 fds = NULL;
 
-                if (process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3) < 0)
-                        goto finish;
+                k = process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3);
+                if (k < 0) {
+                        r = EXIT_FAILURE;
+                        break;
+                }
 
-                if (saved_attr_valid)
-                        tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
+                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);
+                pid = 0;
+
                 if (k < 0) {
                         r = EXIT_FAILURE;
                         break;
@@ -1443,40 +1463,35 @@ int main(int argc, char *argv[]) {
                 if (status.si_code == CLD_EXITED) {
                         r = status.si_status;
                         if (status.si_status != 0) {
-                                log_error("Container failed with error code %i.", status.si_status);
+                                log_error("Container %s failed with error code %i.", arg_machine, status.si_status);
                                 break;
                         }
 
-                        log_debug("Container exited successfully.");
+                        log_debug("Container %s exited successfully.", arg_machine);
                         break;
                 } else if (status.si_code == CLD_KILLED &&
                            status.si_status == SIGINT) {
-                        log_info("Container has been shut down.");
+                        log_info("Container %s has been shut down.", arg_machine);
                         r = 0;
                         break;
                 } else if (status.si_code == CLD_KILLED &&
                            status.si_status == SIGHUP) {
-                        log_info("Container is being rebooted.");
+                        log_info("Container %s is being rebooted.", arg_machine);
                         continue;
                 } else if (status.si_code == CLD_KILLED ||
                            status.si_code == CLD_DUMPED) {
 
-                        log_error("Container terminated by signal %s.", signal_to_string(status.si_status));
+                        log_error("Container %s terminated by signal %s.", arg_machine,  signal_to_string(status.si_status));
                         r = EXIT_FAILURE;
                         break;
                 } else {
-                        log_error("Container failed due to unknown reason.");
+                        log_error("Container %s failed due to unknown reason.", arg_machine);
                         r = EXIT_FAILURE;
                         break;
                 }
         }
 
 finish:
-        if (saved_attr_valid)
-                tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
-
-        close_pipe(kmsg_socket_pair);
-
         if (pid > 0)
                 kill(pid, SIGKILL);