nspawn: make use of the RequestStop logic of scope units
authorLennart Poettering <lennart@poettering.net>
Thu, 23 Nov 2017 18:27:47 +0000 (19:27 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 23 Nov 2017 20:47:48 +0000 (21:47 +0100)
Since time began, scope units had a concept of "Controllers", a bus peer
that would be notified when somebody requested a unit to stop. None of
our code used that facility so far, let's change that.

This way, nspawn can print a nice message when somebody invokes
"systemctl stop" on the container's scope unit, and then react with the
right action to shut it down.

src/nspawn/nspawn-register.c
src/nspawn/nspawn-register.h
src/nspawn/nspawn.c

index 9d58953..ef9db31 100644 (file)
@@ -99,7 +99,26 @@ static int append_machine_properties(
         return 0;
 }
 
+static int append_controller_property(sd_bus *bus, sd_bus_message *m) {
+        const char *unique;
+        int r;
+
+        assert(bus);
+        assert(m);
+
+        r = sd_bus_get_unique_name(bus, &unique);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get unique name: %m");
+
+        r = sd_bus_message_append(m, "(sv)", "Controller", "s", unique);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        return 0;
+}
+
 int register_machine(
+                sd_bus *bus,
                 const char *machine_name,
                 pid_t pid,
                 const char *directory,
@@ -114,12 +133,9 @@ int register_machine(
                 const char *service) {
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
 
-        r = sd_bus_default_system(&bus);
-        if (r < 0)
-                return log_error_errno(r, "Failed to open system bus: %m");
+        assert(bus);
 
         if (keep_unit) {
                 r = sd_bus_call_method(
@@ -174,6 +190,10 @@ int register_machine(
                                 return bus_log_create_error(r);
                 }
 
+                r = append_controller_property(bus, m);
+                if (r < 0)
+                        return r;
+
                 r = append_machine_properties(
                                 m,
                                 mounts,
@@ -202,16 +222,13 @@ int register_machine(
         return 0;
 }
 
-int terminate_machine(pid_t pid) {
+int terminate_machine(sd_bus *bus, pid_t pid) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         const char *path;
         int r;
 
-        r = sd_bus_default_system(&bus);
-        if (r < 0)
-                return log_error_errno(r, "Failed to open system bus: %m");
+        assert(bus);
 
         r = sd_bus_call_method(
                         bus,
@@ -253,6 +270,7 @@ int terminate_machine(pid_t pid) {
 }
 
 int allocate_scope(
+                sd_bus *bus,
                 const char *machine_name,
                 pid_t pid,
                 const char *slice,
@@ -263,16 +281,13 @@ int allocate_scope(
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
         _cleanup_free_ char *scope = NULL;
         const char *description, *object;
         int r;
 
-        r = sd_bus_default_system(&bus);
-        if (r < 0)
-                return log_error_errno(r, "Failed to open system bus: %m");
+        assert(bus);
 
         r = bus_wait_for_jobs_new(bus, &w);
         if (r < 0)
@@ -311,6 +326,10 @@ int allocate_scope(
         if (r < 0)
                 return bus_log_create_error(r);
 
+        r = append_controller_property(bus, m);
+        if (r < 0)
+                return r;
+
         r = append_machine_properties(
                         m,
                         mounts,
index 76c13bb..fa3644c 100644 (file)
@@ -26,7 +26,7 @@
 
 #include "nspawn-mount.h"
 
-int register_machine(const char *machine_name, pid_t pid, const char *directory, sd_id128_t uuid, int local_ifindex, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, bool keep_unit, const char *service);
-int terminate_machine(pid_t pid);
+int register_machine(sd_bus *bus, const char *machine_name, pid_t pid, const char *directory, sd_id128_t uuid, int local_ifindex, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, bool keep_unit, const char *service);
+int terminate_machine(sd_bus *bus, pid_t pid);
 
-int allocate_scope(const char *machine_name, pid_t pid, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties);
+int allocate_scope(sd_bus *bus, const char *machine_name, pid_t pid, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties);
index 3bf4d6e..8fc39dd 100644 (file)
@@ -2039,18 +2039,27 @@ static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo
 }
 
 static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *ssi, void *userdata) {
+        pid_t pid;
+
+        assert(s);
+        assert(ssi);
+
+        pid = PTR_TO_PID(userdata);
+
         for (;;) {
                 siginfo_t si = {};
+
                 if (waitid(P_ALL, 0, &si, WNOHANG|WNOWAIT|WEXITED) < 0)
                         return log_error_errno(errno, "Failed to waitid(): %m");
                 if (si.si_pid == 0) /* No pending children. */
                         break;
-                if (si.si_pid == PTR_TO_PID(userdata)) {
+                if (si.si_pid == pid) {
                         /* The main process we care for has exited. Return from
                          * signal handler but leave the zombie. */
                         sd_event_exit(sd_event_source_get_event(s), 0);
                         break;
                 }
+
                 /* Reap all other children. */
                 (void) waitid(P_PID, si.si_pid, &si, WNOHANG|WEXITED);
         }
@@ -2058,6 +2067,24 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *ssi, vo
         return 0;
 }
 
+static int on_request_stop(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        pid_t pid;
+
+        assert(m);
+
+        pid = PTR_TO_PID(userdata);
+
+        if (arg_kill_signal > 0) {
+                log_info("Container termination requested. Attempting to halt container.");
+                (void) kill(pid, arg_kill_signal);
+        } else {
+                log_info("Container termination requested. Exiting.");
+                sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0);
+        }
+
+        return 0;
+}
+
 static int determine_names(void) {
         int r;
 
@@ -3214,6 +3241,7 @@ static int run(int master,
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         ContainerStatus container_status = 0;
         char last_char = 0;
         int ifi = 0, r;
@@ -3444,8 +3472,31 @@ static int run(int master,
                         return r;
         }
 
+        if (arg_register || !arg_keep_unit) {
+                r = sd_bus_default_system(&bus);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to open system bus: %m");
+        }
+
+        if (!arg_keep_unit) {
+                /* When a new scope is created for this container, then we'll be registered as its controller, in which
+                 * case PID 1 will send us a friendly RequestStop signal, when it is asked to terminate the
+                 * scope. Let's hook into that, and cleanly shut down the container, and print a friendly message. */
+
+                r = sd_bus_add_match(bus, NULL,
+                                     "type='signal',"
+                                     "sender='org.freedesktop.systemd1',"
+                                     "interface='org.freedesktop.systemd1.Scope',"
+                                     "member='RequestStop'",
+                                     on_request_stop, PID_TO_PTR(*pid));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to install request stop match: %m");
+        }
+
         if (arg_register) {
+
                 r = register_machine(
+                                bus,
                                 arg_machine,
                                 *pid,
                                 arg_directory,
@@ -3459,8 +3510,11 @@ static int run(int master,
                                 arg_container_service_name);
                 if (r < 0)
                         return r;
+
         } else if (!arg_keep_unit) {
+
                 r = allocate_scope(
+                                bus,
                                 arg_machine,
                                 *pid,
                                 arg_slice,
@@ -3506,6 +3560,12 @@ static int run(int master,
         if (r < 0)
                 return log_error_errno(r, "Failed to get default event source: %m");
 
+        if (bus) {
+                r = sd_bus_attach_event(bus, event, 0);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to attach bus to event loop: %m");
+        }
+
         r = setup_sd_notify_parent(event, notify_socket, PID_TO_PTR(*pid), &notify_event_source);
         if (r < 0)
                 return r;
@@ -3567,8 +3627,8 @@ static int run(int master,
                 putc('\n', stdout);
 
         /* Kill if it is not dead yet anyway */
-        if (arg_register && !arg_keep_unit)
-                terminate_machine(*pid);
+        if (arg_register && !arg_keep_unit && bus)
+                terminate_machine(bus, *pid);
 
         /* Normally redundant, but better safe than sorry */
         (void) kill(*pid, SIGKILL);