core: Don't racily stop Type=dbus services when the bus broker restarts (#42455)
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
|
||||
<!--
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
-->
|
||||
|
||||
<busconfig>
|
||||
<policy user="root">
|
||||
<allow own="systemd.test.DBusReconnect"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
@@ -233,6 +233,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_START] = "start",
|
||||
[SERVICE_START_POST] = "start-post",
|
||||
[SERVICE_RUNNING] = "running",
|
||||
[SERVICE_RUNNING_REVALIDATING] = "running-revalidating",
|
||||
[SERVICE_EXITED] = "exited",
|
||||
[SERVICE_REFRESH_EXTENSIONS] = "refresh-extensions",
|
||||
[SERVICE_REFRESH_CREDENTIALS] = "refresh-credentials",
|
||||
|
||||
@@ -132,6 +132,7 @@ typedef enum ServiceState {
|
||||
SERVICE_START,
|
||||
SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_RUNNING_REVALIDATING, /* Temporarily waiting for the D-Bus name to reappear */
|
||||
SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
|
||||
SERVICE_REFRESH_EXTENSIONS, /* Refreshing extensions for a reload request */
|
||||
SERVICE_REFRESH_CREDENTIALS, /* ditto, but for credentials */
|
||||
|
||||
@@ -2048,6 +2048,7 @@ static bool manager_dbus_is_running(Manager *m, bool deserialized) {
|
||||
return false;
|
||||
if (!IN_SET(deserialized ? SERVICE(u)->deserialized_state : SERVICE(u)->state,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_RUNNING_REVALIDATING,
|
||||
SERVICE_REFRESH_EXTENSIONS,
|
||||
SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD,
|
||||
|
||||
+114
-21
@@ -70,6 +70,8 @@
|
||||
|
||||
#define SERVICE_FD_STORE_POPULATED(s) (!!(s)->fd_store)
|
||||
|
||||
#define SERVICE_BUS_NAME_GRACE_USEC (2 * USEC_PER_SEC)
|
||||
|
||||
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_DEAD] = UNIT_INACTIVE,
|
||||
[SERVICE_CONDITION] = UNIT_ACTIVATING,
|
||||
@@ -77,6 +79,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_START] = UNIT_ACTIVATING,
|
||||
[SERVICE_START_POST] = UNIT_ACTIVATING,
|
||||
[SERVICE_RUNNING] = UNIT_ACTIVE,
|
||||
[SERVICE_RUNNING_REVALIDATING] = UNIT_ACTIVE,
|
||||
[SERVICE_EXITED] = UNIT_ACTIVE,
|
||||
[SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING,
|
||||
[SERVICE_REFRESH_CREDENTIALS] = UNIT_REFRESHING,
|
||||
@@ -111,6 +114,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
|
||||
[SERVICE_START] = UNIT_ACTIVE,
|
||||
[SERVICE_START_POST] = UNIT_ACTIVE,
|
||||
[SERVICE_RUNNING] = UNIT_ACTIVE,
|
||||
[SERVICE_RUNNING_REVALIDATING] = UNIT_ACTIVE,
|
||||
[SERVICE_EXITED] = UNIT_ACTIVE,
|
||||
[SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING,
|
||||
[SERVICE_REFRESH_CREDENTIALS] = UNIT_REFRESHING,
|
||||
@@ -140,6 +144,7 @@ static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t
|
||||
static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
|
||||
static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata);
|
||||
static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
|
||||
static int service_dispatch_bus_name_grace(sd_event_source *source, usec_t usec, void *userdata);
|
||||
|
||||
static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
|
||||
|
||||
@@ -153,7 +158,7 @@ static void service_set_state(Service *s, ServiceState state);
|
||||
static bool SERVICE_STATE_WITH_MAIN_PROCESS(ServiceState state) {
|
||||
return IN_SET(state,
|
||||
SERVICE_START, SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_RUNNING, SERVICE_RUNNING_REVALIDATING,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_MOUNTING,
|
||||
@@ -175,7 +180,7 @@ static bool SERVICE_STATE_WITH_CONTROL_PROCESS(ServiceState state) {
|
||||
static bool SERVICE_STATE_WITH_WATCHDOG(ServiceState state) {
|
||||
return IN_SET(state,
|
||||
SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_RUNNING, SERVICE_RUNNING_REVALIDATING,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_MOUNTING);
|
||||
@@ -213,6 +218,7 @@ static void service_init(Unit *u) {
|
||||
|
||||
s->oom_policy = _OOM_POLICY_INVALID;
|
||||
s->reload_begin_usec = USEC_INFINITY;
|
||||
s->revalidate_runtime_begin_usec = USEC_INFINITY;
|
||||
s->reload_signal = SIGHUP;
|
||||
|
||||
s->fd_store_preserve_mode = EXEC_PRESERVE_RESTART;
|
||||
@@ -618,6 +624,7 @@ static void service_done(Unit *u) {
|
||||
service_stop_watchdog(s);
|
||||
|
||||
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
|
||||
s->bus_name_grace_event_source = sd_event_source_disable_unref(s->bus_name_grace_event_source);
|
||||
s->exec_fd_event_source = sd_event_source_disable_unref(s->exec_fd_event_source);
|
||||
|
||||
s->bus_name_pid_lookup_slot = sd_bus_slot_unref(s->bus_name_pid_lookup_slot);
|
||||
@@ -1668,6 +1675,9 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
SERVICE_CLEANING))
|
||||
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
|
||||
|
||||
if (state != SERVICE_RUNNING_REVALIDATING)
|
||||
s->bus_name_grace_event_source = sd_event_source_disable_unref(s->bus_name_grace_event_source);
|
||||
|
||||
if (!SERVICE_STATE_WITH_MAIN_PROCESS(state)) {
|
||||
service_unwatch_main_pid(s);
|
||||
s->main_command = NULL;
|
||||
@@ -1781,6 +1791,14 @@ static int service_coldplug(Unit *u) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (s->deserialized_state == SERVICE_RUNNING_REVALIDATING) {
|
||||
r = unit_arm_timer(UNIT(s), &s->bus_name_grace_event_source, /* relative= */ true,
|
||||
SERVICE_BUS_NAME_GRACE_USEC,
|
||||
service_dispatch_bus_name_grace);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (pidref_is_set(&s->main_pid) &&
|
||||
pidref_is_unwaited(&s->main_pid) > 0 &&
|
||||
SERVICE_STATE_WITH_MAIN_PROCESS(s->deserialized_state)) {
|
||||
@@ -2739,14 +2757,11 @@ static void service_enter_stop(Service *s, ServiceResult f) {
|
||||
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
|
||||
}
|
||||
|
||||
static bool service_good(Service *s) {
|
||||
static bool service_good_except_bus_name(Service *s) {
|
||||
int main_pid_ok;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (s->type == SERVICE_DBUS && !s->bus_name_good)
|
||||
return false;
|
||||
|
||||
main_pid_ok = main_pid_good(s);
|
||||
if (main_pid_ok > 0) /* It's alive */
|
||||
return true;
|
||||
@@ -2760,6 +2775,26 @@ static bool service_good(Service *s) {
|
||||
return cgroup_good(s) != 0;
|
||||
}
|
||||
|
||||
static bool service_good(Service *s) {
|
||||
assert(s);
|
||||
|
||||
if (s->type == SERVICE_DBUS && !s->bus_name_good)
|
||||
return false;
|
||||
|
||||
return service_good_except_bus_name(s);
|
||||
}
|
||||
|
||||
static void service_enter_exited_or_stop(Service *s) {
|
||||
assert(s);
|
||||
|
||||
s->revalidate_runtime_begin_usec = USEC_INFINITY;
|
||||
|
||||
if (s->remain_after_exit)
|
||||
service_set_state(s, SERVICE_EXITED);
|
||||
else
|
||||
service_enter_stop(s, SERVICE_SUCCESS);
|
||||
}
|
||||
|
||||
static void service_enter_running(Service *s, ServiceResult f) {
|
||||
int r;
|
||||
|
||||
@@ -2782,7 +2817,17 @@ static void service_enter_running(Service *s, ServiceResult f) {
|
||||
else {
|
||||
service_set_state(s, SERVICE_RUNNING);
|
||||
|
||||
r = service_arm_timer(s, /* relative= */ false, service_running_timeout(s));
|
||||
/* We're resuming. Recompute the budget from the current config (which was measured
|
||||
* from when we entered revalidation). */
|
||||
usec_t remaining = USEC_INFINITY;
|
||||
if (s->revalidate_runtime_begin_usec != USEC_INFINITY)
|
||||
remaining = usec_sub_unsigned(service_running_timeout(s), s->revalidate_runtime_begin_usec);
|
||||
s->revalidate_runtime_begin_usec = USEC_INFINITY;
|
||||
|
||||
if (remaining != USEC_INFINITY)
|
||||
r = service_arm_timer(s, /* relative= */ true, remaining);
|
||||
else
|
||||
r = service_arm_timer(s, /* relative= */ false, service_running_timeout(s));
|
||||
if (r < 0) {
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m");
|
||||
service_enter_running(s, SERVICE_FAILURE_RESOURCES);
|
||||
@@ -2790,10 +2835,33 @@ static void service_enter_running(Service *s, ServiceResult f) {
|
||||
}
|
||||
}
|
||||
|
||||
} else if (s->remain_after_exit)
|
||||
service_set_state(s, SERVICE_EXITED);
|
||||
else
|
||||
service_enter_stop(s, SERVICE_SUCCESS);
|
||||
} else if (s->type == SERVICE_DBUS && !s->bus_name_good && service_good_except_bus_name(s)) {
|
||||
|
||||
/* If we're already revalidating, just keep waiting on the timer we armed on the way in, so a
|
||||
* flapping broker that reconnects repeatedly can't keep pushing the deadline out. */
|
||||
if (s->state == SERVICE_RUNNING_REVALIDATING)
|
||||
return;
|
||||
|
||||
log_unit_warning(UNIT(s),
|
||||
"D-Bus name %s vanished (possibly from an unsupported broker restart), giving it %s to return.",
|
||||
s->bus_name,
|
||||
FORMAT_TIMESPAN(SERVICE_BUS_NAME_GRACE_USEC, USEC_PER_SEC));
|
||||
|
||||
r = unit_arm_timer(UNIT(s), &s->bus_name_grace_event_source, /* relative= */ true,
|
||||
SERVICE_BUS_NAME_GRACE_USEC, service_dispatch_bus_name_grace);
|
||||
if (r < 0) {
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to arm D-Bus name grace timer: %m");
|
||||
service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->state == SERVICE_RUNNING)
|
||||
s->revalidate_runtime_begin_usec = now(CLOCK_MONOTONIC);
|
||||
|
||||
service_set_state(s, SERVICE_RUNNING_REVALIDATING);
|
||||
|
||||
} else
|
||||
service_enter_exited_or_stop(s);
|
||||
}
|
||||
|
||||
static void service_enter_start_post(Service *s) {
|
||||
@@ -3559,6 +3627,7 @@ static int service_start(Unit *u) {
|
||||
s->result = SERVICE_SUCCESS;
|
||||
s->reload_result = SERVICE_SUCCESS;
|
||||
s->reload_begin_usec = USEC_INFINITY;
|
||||
s->revalidate_runtime_begin_usec = USEC_INFINITY;
|
||||
|
||||
s->status_text = mfree(s->status_text);
|
||||
s->status_errno = 0;
|
||||
@@ -3677,6 +3746,7 @@ static int service_stop(Unit *u) {
|
||||
return 0;
|
||||
|
||||
case SERVICE_RUNNING:
|
||||
case SERVICE_RUNNING_REVALIDATING:
|
||||
case SERVICE_EXITED:
|
||||
service_enter_stop(s, SERVICE_SUCCESS);
|
||||
return 1;
|
||||
@@ -3695,7 +3765,7 @@ static int service_stop(Unit *u) {
|
||||
static int service_reload(Unit *u) {
|
||||
Service *s = ASSERT_PTR(SERVICE(u));
|
||||
|
||||
assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED));
|
||||
assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_RUNNING_REVALIDATING, SERVICE_EXITED));
|
||||
|
||||
s->reload_result = SERVICE_SUCCESS;
|
||||
s->refreshed_mask = 0;
|
||||
@@ -3923,6 +3993,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
(void) serialize_usec(f, "watchdog-override-usec", s->watchdog_override_usec);
|
||||
|
||||
(void) serialize_usec(f, "reload-begin-usec", s->reload_begin_usec);
|
||||
(void) serialize_usec(f, "revalidate-runtime-begin-usec", s->revalidate_runtime_begin_usec);
|
||||
|
||||
if (s->refreshed_mask > 0) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
@@ -4358,6 +4429,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
|
||||
|
||||
} else if (streq(key, "reload-begin-usec"))
|
||||
(void) deserialize_usec(value, &s->reload_begin_usec);
|
||||
else if (streq(key, "revalidate-runtime-begin-usec"))
|
||||
(void) deserialize_usec(value, &s->revalidate_runtime_begin_usec);
|
||||
else if (streq(key, "refreshed-mask")) {
|
||||
r = service_refresh_on_reload_from_string_many(value, &s->refreshed_mask);
|
||||
if (r < 0)
|
||||
@@ -4593,6 +4666,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
|
||||
break;
|
||||
|
||||
case SERVICE_RUNNING:
|
||||
case SERVICE_RUNNING_REVALIDATING:
|
||||
/* service_enter_running() will figure out what to do */
|
||||
service_enter_running(s, SERVICE_SUCCESS);
|
||||
break;
|
||||
@@ -4654,6 +4728,7 @@ static void service_notify_cgroup_oom_event(Unit *u, bool managed_oom) {
|
||||
|
||||
case SERVICE_EXITED:
|
||||
case SERVICE_RUNNING:
|
||||
case SERVICE_RUNNING_REVALIDATING:
|
||||
if (s->oom_policy == OOM_STOP)
|
||||
service_enter_stop(s, SERVICE_FAILURE_OOM_KILL);
|
||||
else if (s->oom_policy == OOM_KILL)
|
||||
@@ -4818,6 +4893,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
|
||||
_fallthrough_;
|
||||
case SERVICE_RUNNING:
|
||||
case SERVICE_RUNNING_REVALIDATING:
|
||||
service_enter_running(s, f);
|
||||
break;
|
||||
|
||||
@@ -5440,7 +5516,7 @@ static void service_notify_message_process_state(Service *s, char * const *tags)
|
||||
if (strv_contains(tags, "STOPPING=1")) {
|
||||
s->notify_state = NOTIFY_STOPPING;
|
||||
|
||||
if (IN_SET(s->state, SERVICE_RUNNING,
|
||||
if (IN_SET(s->state, SERVICE_RUNNING, SERVICE_RUNNING_REVALIDATING,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY))
|
||||
service_enter_stop_by_notify(s);
|
||||
@@ -5477,7 +5553,7 @@ static void service_notify_message_process_state(Service *s, char * const *tags)
|
||||
/* Valid Type=notify-reload protocol? Then we're all good. */
|
||||
service_enter_reload_post(s);
|
||||
|
||||
else if (s->state == SERVICE_RUNNING) {
|
||||
else if (IN_SET(s->state, SERVICE_RUNNING, SERVICE_RUNNING_REVALIDATING)) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
/* Propagate a reload explicitly for plain RELOADING=1 (semantically equivalent to
|
||||
@@ -5518,6 +5594,8 @@ static void service_notify_message_process_state(Service *s, char * const *tags)
|
||||
* don't need reload propagation nor do we want to restart the timeout. */
|
||||
service_set_state(s, SERVICE_RELOAD_NOTIFY);
|
||||
|
||||
/* The reload will still be preserved by service_enter_running() replaying it once the bus
|
||||
* name returns. */
|
||||
if (s->state == SERVICE_RUNNING)
|
||||
service_enter_reload_by_notify(s);
|
||||
}
|
||||
@@ -5552,7 +5630,7 @@ static void service_notify_message(
|
||||
|
||||
r = service_notify_message_parse_new_pid(u, tags, fds, &new_main_pid);
|
||||
if (r > 0 &&
|
||||
IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING,
|
||||
IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RUNNING_REVALIDATING,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_STOP, SERVICE_STOP_SIGTERM) &&
|
||||
@@ -5696,7 +5774,7 @@ static void service_notify_message(
|
||||
}
|
||||
|
||||
/* Interpret RESTART_RESET=1 */
|
||||
if (strv_contains(tags, "RESTART_RESET=1") && IN_SET(s->state, SERVICE_RUNNING, SERVICE_STOP)) {
|
||||
if (strv_contains(tags, "RESTART_RESET=1") && IN_SET(s->state, SERVICE_RUNNING, SERVICE_RUNNING_REVALIDATING, SERVICE_STOP)) {
|
||||
log_unit_struct(u, LOG_NOTICE,
|
||||
LOG_UNIT_MESSAGE(u, "Got RESTART_RESET=1, resetting restart counter from %u.", s->n_restarts),
|
||||
LOG_ITEM("N_RESTARTS=0"),
|
||||
@@ -5871,7 +5949,19 @@ static int bus_name_pid_lookup_callback(sd_bus_message *reply, void *userdata, s
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void service_bus_name_owner_change(Unit *u, const char *new_owner) {
|
||||
static int service_dispatch_bus_name_grace(sd_event_source *source, usec_t usec, void *userdata) {
|
||||
Service *s = ASSERT_PTR(SERVICE(userdata));
|
||||
|
||||
assert(source == s->bus_name_grace_event_source);
|
||||
assert(s->state == SERVICE_RUNNING_REVALIDATING);
|
||||
|
||||
log_unit_warning(UNIT(s), "D-Bus name %s still not owned after grace period, giving up.", s->bus_name);
|
||||
service_enter_exited_or_stop(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void service_bus_name_owner_change(Unit *u, const char *new_owner, bool from_signal) {
|
||||
Service *s = ASSERT_PTR(SERVICE(u));
|
||||
int r;
|
||||
|
||||
@@ -5883,10 +5973,12 @@ static void service_bus_name_owner_change(Unit *u, const char *new_owner) {
|
||||
s->bus_name_good = new_owner;
|
||||
|
||||
if (s->type == SERVICE_DBUS) {
|
||||
/* service_enter_running() will figure out what to do */
|
||||
if (s->state == SERVICE_RUNNING)
|
||||
service_enter_running(s, SERVICE_SUCCESS);
|
||||
else if (s->state == SERVICE_START && new_owner)
|
||||
if (IN_SET(s->state, SERVICE_RUNNING, SERVICE_RUNNING_REVALIDATING)) {
|
||||
if (!new_owner && from_signal)
|
||||
service_enter_exited_or_stop(s);
|
||||
else
|
||||
service_enter_running(s, SERVICE_SUCCESS);
|
||||
} else if (s->state == SERVICE_START && new_owner)
|
||||
service_enter_start_post(s);
|
||||
|
||||
} else if (new_owner && pick_up_pid_from_bus_name(s)) {
|
||||
@@ -6009,6 +6101,7 @@ static bool service_needs_console(Unit *u) {
|
||||
SERVICE_START,
|
||||
SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_RUNNING_REVALIDATING,
|
||||
SERVICE_REFRESH_EXTENSIONS,
|
||||
SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD,
|
||||
|
||||
@@ -163,6 +163,7 @@ typedef struct Service {
|
||||
bool timeout_abort_set;
|
||||
usec_t runtime_max_usec;
|
||||
usec_t runtime_rand_extra_usec;
|
||||
usec_t revalidate_runtime_begin_usec;
|
||||
ServiceTimeoutFailureMode timeout_start_failure_mode;
|
||||
ServiceTimeoutFailureMode timeout_stop_failure_mode;
|
||||
|
||||
@@ -226,6 +227,7 @@ typedef struct Service {
|
||||
bool exec_fd_hot:1;
|
||||
|
||||
char *bus_name;
|
||||
sd_event_source *bus_name_grace_event_source;
|
||||
|
||||
char *status_text;
|
||||
char *status_bus_error;
|
||||
|
||||
+6
-3
@@ -3593,7 +3593,7 @@ static int signal_name_owner_changed_install_handler(sd_bus_message *message, vo
|
||||
u->get_name_owner_slot = sd_bus_slot_unref(u->get_name_owner_slot);
|
||||
|
||||
if (UNIT_VTABLE(u)->bus_name_owner_change)
|
||||
UNIT_VTABLE(u)->bus_name_owner_change(u, NULL);
|
||||
UNIT_VTABLE(u)->bus_name_owner_change(u, NULL, /* from_signal= */ true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3612,7 +3612,7 @@ static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd
|
||||
}
|
||||
|
||||
if (UNIT_VTABLE(u)->bus_name_owner_change)
|
||||
UNIT_VTABLE(u)->bus_name_owner_change(u, empty_to_null(new_owner));
|
||||
UNIT_VTABLE(u)->bus_name_owner_change(u, empty_to_null(new_owner), /* from_signal= */ true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3645,8 +3645,11 @@ static int get_name_owner_handler(sd_bus_message *message, void *userdata, sd_bu
|
||||
assert(!isempty(new_owner));
|
||||
}
|
||||
|
||||
/* Given we're in a GetNameOwner() refresh, which we issue right after (re)connecting to the broker,
|
||||
* a NULL owner here may simply mean the owning client is still getting its bearings, so treat it as
|
||||
* non-definitive and worth waiting a bit for. */
|
||||
if (UNIT_VTABLE(u)->bus_name_owner_change)
|
||||
UNIT_VTABLE(u)->bus_name_owner_change(u, new_owner);
|
||||
UNIT_VTABLE(u)->bus_name_owner_change(u, new_owner, /* from_signal= */ false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+1
-1
@@ -670,7 +670,7 @@ typedef struct UnitVTable {
|
||||
void (*notify_pidref)(Unit *u, PidRef *parent_pidref, PidRef *child_pidref);
|
||||
|
||||
/* Called whenever a name this Unit registered for comes or goes away. */
|
||||
void (*bus_name_owner_change)(Unit *u, const char *new_owner);
|
||||
void (*bus_name_owner_change)(Unit *u, const char *new_owner, bool from_signal);
|
||||
|
||||
/* Called for each property that is being set */
|
||||
int (*bus_set_property)(Unit *u, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *reterr_error);
|
||||
|
||||
@@ -315,6 +315,10 @@ executables += [
|
||||
'dependencies' : libopenssl_cflags,
|
||||
'conditions' : ['HAVE_OPENSSL'],
|
||||
},
|
||||
test_template + {
|
||||
'sources' : files('test-dbus-name-holder.c'),
|
||||
'type' : 'manual',
|
||||
},
|
||||
test_template + {
|
||||
'sources' : files('test-display-quota.c'),
|
||||
'type' : 'manual',
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
#include "sd-daemon.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "time-util.h"
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
if (argc < 3 || argc > 5)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Usage: %s BUSNAME READY_MARKER [RELEASE_MARKER] [NOTIFY_MARKER]",
|
||||
program_invocation_short_name);
|
||||
|
||||
const char
|
||||
*name = argv[1],
|
||||
*marker = argv[2],
|
||||
*release_marker = argc > 3 ? argv[3] : NULL,
|
||||
*notify_marker = argc > 4 ? argv[4] : NULL;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open system bus: %m");
|
||||
|
||||
r = sd_bus_request_name(bus, name, /* flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire dbus name '%s': %m", name);
|
||||
|
||||
log_info("Acquired dbus name '%s'.", name);
|
||||
|
||||
for (;;) {
|
||||
r = sd_bus_process(bus, /* ret= */ NULL);
|
||||
if (r < 0)
|
||||
break;
|
||||
if (r > 0)
|
||||
continue;
|
||||
|
||||
if (release_marker && access(release_marker, F_OK) >= 0) {
|
||||
r = sd_bus_release_name(bus, name);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to release dbus name '%s': %m", name);
|
||||
|
||||
log_info("Released dbus name '%s'.", name);
|
||||
release_marker = NULL; /* go back to blocking until we're killed */
|
||||
}
|
||||
|
||||
r = sd_bus_wait(bus, release_marker ? 100 * USEC_PER_MSEC : USEC_INFINITY);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
log_info("Disconnected from bus, reconnecting...");
|
||||
|
||||
while (access(marker, F_OK) < 0) {
|
||||
if (notify_marker && access(notify_marker, F_OK) >= 0) {
|
||||
r = sd_notify(/* unset_environment= */ false, "RELOADING=1\nREADY=1");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to send reload notification: %m");
|
||||
|
||||
log_info("Sent reload notification.");
|
||||
notify_marker = NULL;
|
||||
}
|
||||
|
||||
usleep_safe(50 * USEC_PER_MSEC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
holder=/usr/lib/systemd/tests/unit-tests/manual/test-dbus-name-holder
|
||||
name=systemd.test.DBusReconnect
|
||||
marker=/run/dbus-reconnect-go
|
||||
release=/run/dbus-reconnect-release
|
||||
notify=/run/dbus-reconnect-notify
|
||||
reloaded=/run/dbus-reconnect-reloaded
|
||||
|
||||
at_exit() {
|
||||
rm -f "$marker" "$release" "$notify" "$reloaded"
|
||||
systemctl stop \
|
||||
dbus-reconnect-recover.service \
|
||||
dbus-reconnect-stop.service \
|
||||
dbus-reconnect-exit.service \
|
||||
dbus-reconnect-drop.service \
|
||||
dbus-reconnect-notify.service \
|
||||
dbus-reconnect-notify-target.service \
|
||||
dbus-reconnect-reexec.service 2>/dev/null || :
|
||||
}
|
||||
|
||||
trap at_exit EXIT
|
||||
|
||||
# Poll until property $2 of unit $1 reaches value $3
|
||||
poll() {
|
||||
local unit="${1:?}" prop="${2:?}" want="${3:?}" got i
|
||||
for ((i = 0; i < 150; i++)); do
|
||||
got="$(systemctl show -p "$prop" --value "$unit")"
|
||||
if [[ "$got" == "$want" ]]; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
done
|
||||
echo "poll: timed out waiting for $unit $prop=$want (last: $got)" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
reset_failed() {
|
||||
local unit="${1:?}"
|
||||
|
||||
systemctl reset-failed "$unit" 2>/dev/null || :
|
||||
}
|
||||
|
||||
restart_broker() {
|
||||
systemctl kill --signal=KILL dbus.service
|
||||
timeout 90 bash -c '
|
||||
until busctl --system status >/dev/null 2>&1; do
|
||||
systemctl reset-failed dbus.service dbus.socket 2>/dev/null
|
||||
systemctl start dbus.socket 2>/dev/null
|
||||
sleep 0.2
|
||||
done'
|
||||
}
|
||||
|
||||
enter_revalidation() {
|
||||
local unit="${1:?}"
|
||||
|
||||
shift
|
||||
reset_failed "$unit"
|
||||
rm -f "$marker"
|
||||
systemd-run \
|
||||
--unit="$unit" \
|
||||
-p Type=dbus \
|
||||
-p BusName="$name" \
|
||||
-p Restart=no \
|
||||
"$@" \
|
||||
"$holder" "$name" "$marker"
|
||||
restart_broker
|
||||
poll "$unit" SubState running-revalidating
|
||||
}
|
||||
|
||||
# the name comes back within the grace window
|
||||
unit=dbus-reconnect-recover.service
|
||||
enter_revalidation "$unit"
|
||||
main_pid="$(systemctl show -p MainPID --value "$unit")"
|
||||
assert_neq "$main_pid" "0"
|
||||
touch "$marker"
|
||||
poll "$unit" SubState running
|
||||
assert_eq "$(systemctl show -p MainPID --value "$unit")" "$main_pid"
|
||||
systemctl stop "$unit"
|
||||
|
||||
# the name never comes back
|
||||
unit=dbus-reconnect-stop.service
|
||||
enter_revalidation "$unit"
|
||||
poll "$unit" ActiveState inactive
|
||||
|
||||
# ditto with RemainAfterExit=yes
|
||||
unit=dbus-reconnect-exit.service
|
||||
enter_revalidation "$unit" -p RemainAfterExit=yes
|
||||
poll "$unit" SubState exited
|
||||
systemctl stop "$unit"
|
||||
|
||||
# the name is dropped on a live connection and we action it at once
|
||||
unit=dbus-reconnect-drop.service
|
||||
reset_failed "$unit"
|
||||
rm -f "$release"
|
||||
systemd-run \
|
||||
--unit="$unit" \
|
||||
-p Type=dbus \
|
||||
-p BusName="$name" \
|
||||
-p Restart=no \
|
||||
"$holder" "$name" "$marker" "$release"
|
||||
poll "$unit" SubState running
|
||||
touch "$release"
|
||||
for ((i = 0; i < 150; i++)); do
|
||||
sub_state="$(systemctl show -p SubState --value "$unit")"
|
||||
if [[ "$sub_state" == running-revalidating ]]; then
|
||||
echo "live drop entered the revalidation window" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
active_state="$(systemctl show -p ActiveState --value "$unit")"
|
||||
if [[ "$active_state" == inactive ]]; then
|
||||
break
|
||||
fi
|
||||
|
||||
sleep 0.2
|
||||
done
|
||||
assert_eq "$(systemctl show -p ActiveState --value "$unit")" inactive
|
||||
|
||||
# a reload notification is propagated even while the name is absent (revalidating)
|
||||
target=dbus-reconnect-notify-target.service
|
||||
reset_failed "$target"
|
||||
systemd-run \
|
||||
--unit="$target" \
|
||||
-p Type=oneshot \
|
||||
-p RemainAfterExit=yes \
|
||||
-p "ExecReload=/usr/bin/touch $reloaded" \
|
||||
/usr/bin/true
|
||||
|
||||
unit=dbus-reconnect-notify.service
|
||||
reset_failed "$unit"
|
||||
rm -f "$marker" "$release" "$notify" "$reloaded"
|
||||
systemd-run \
|
||||
--unit="$unit" \
|
||||
-p Type=dbus \
|
||||
-p BusName="$name" \
|
||||
-p Restart=no \
|
||||
-p NotifyAccess=main \
|
||||
-p PropagatesReloadTo="$target" \
|
||||
"$holder" "$name" "$marker" "$release" "$notify"
|
||||
restart_broker
|
||||
poll "$unit" SubState running-revalidating
|
||||
touch "$notify"
|
||||
timeout 30 bash -c "until [[ -e '$reloaded' ]]; do sleep 0.2; done"
|
||||
touch "$marker"
|
||||
poll "$unit" SubState running
|
||||
systemctl stop "$unit" "$target"
|
||||
|
||||
# the revalidation state survives a daemon-reexec and still recovers afterwards
|
||||
unit=dbus-reconnect-reexec.service
|
||||
enter_revalidation "$unit"
|
||||
systemctl daemon-reexec
|
||||
touch "$marker"
|
||||
poll "$unit" SubState running
|
||||
systemctl stop "$unit"
|
||||
Reference in New Issue
Block a user