nspawn: make use of the RequestStop logic of scope units

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.
This commit is contained in:
Lennart Poettering
2017-11-23 21:47:48 +01:00
parent 33fe0afe9a
commit abdb9b08f6
3 changed files with 98 additions and 19 deletions
+32 -13
View 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,
+3 -3
View 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);
+63 -3
View 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);