From ac908152b3b43a49f793d225c075423422cd3e33 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 8 May 2024 10:38:11 +0200 Subject: [PATCH 1/4] hostnamed: don't allow hostnamed to exit on idle if varlink connections are still ongoing And while we are at it, ongoing PK authorizations are also a reason to block exit on idle. --- src/hostname/hostnamed.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 82d08803fa..fe1216fc1c 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -1682,6 +1682,13 @@ static int connect_varlink(Context *c) { return 0; } +static bool context_check_idle(void *userdata) { + Context *c = ASSERT_PTR(userdata); + + return varlink_server_current_connections(c->varlink_server) == 0 && + hashmap_isempty(c->polkit_registry); +} + static int run(int argc, char *argv[]) { _cleanup_(context_destroy) Context context = { .hostname_source = _HOSTNAME_INVALID, /* appropriate value will be set later */ @@ -1731,8 +1738,8 @@ static int run(int argc, char *argv[]) { context.bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, - /* check_idle= */ NULL, - /* userdata= */ NULL); + context_check_idle, + &context); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); From ec8bbd8adb9463471803aadd9a8ba2a326d7a49d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 May 2024 09:46:04 +0200 Subject: [PATCH 2/4] tree-wide: never consider service idle if polkit authentication is still pending Let's be correct on this. --- src/import/importd.c | 5 +++-- src/locale/localed.c | 14 +++++++++++++- src/machine/machined.c | 5 ++++- src/portable/portabled.c | 5 +++-- src/timedate/timedated.c | 14 +++++++++++++- 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/import/importd.c b/src/import/importd.c index 3bfa3cdd75..2ecffdec4e 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -1728,9 +1728,10 @@ static int manager_add_bus_objects(Manager *m) { } static bool manager_check_idle(void *userdata) { - Manager *m = userdata; + Manager *m = ASSERT_PTR(userdata); - return hashmap_isempty(m->transfers); + return hashmap_isempty(m->transfers) && + hashmap_isempty(m->polkit_registry); } static void manager_parse_env(Manager *m) { diff --git a/src/locale/localed.c b/src/locale/localed.c index c0d104578d..062744519d 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -622,6 +622,12 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { return 0; } +static bool context_check_idle(void *userdata) { + Context *c = ASSERT_PTR(userdata); + + return hashmap_isempty(c->polkit_registry); +} + static int run(int argc, char *argv[]) { _cleanup_(context_clear) Context context = {}; _cleanup_(sd_event_unrefp) sd_event *event = NULL; @@ -662,7 +668,13 @@ static int run(int argc, char *argv[]) { if (r < 0) log_warning_errno(r, "Failed to send readiness notification, ignoring: %m"); - r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL); + r = bus_event_loop_with_idle( + event, + bus, + "org.freedesktop.locale1", + DEFAULT_EXIT_USEC, + context_check_idle, + &context); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); diff --git a/src/machine/machined.c b/src/machine/machined.c index d7087e4672..398375bc5e 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -311,7 +311,7 @@ static int manager_startup(Manager *m) { } static bool check_idle(void *userdata) { - Manager *m = userdata; + Manager *m = ASSERT_PTR(userdata); if (m->operations) return false; @@ -322,6 +322,9 @@ static bool check_idle(void *userdata) { if (varlink_server_current_connections(m->varlink_machine_server) > 0) return false; + if (!hashmap_isempty(m->polkit_registry)) + return false; + manager_gc(m, true); return hashmap_isempty(m->machines); diff --git a/src/portable/portabled.c b/src/portable/portabled.c index d46ac018a3..0286bf59e7 100644 --- a/src/portable/portabled.c +++ b/src/portable/portabled.c @@ -117,9 +117,10 @@ static int manager_startup(Manager *m) { } static bool check_idle(void *userdata) { - Manager *m = userdata; + Manager *m = ASSERT_PTR(userdata); - return !m->operations; + return !m->operations && + hashmap_isempty(m->polkit_registry); } static int run(int argc, char *argv[]) { diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index e3b4367ec0..b4cc5f9dd5 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -1118,6 +1118,12 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { return 0; } +static bool context_check_idle(void *userdata) { + Context *c = ASSERT_PTR(userdata); + + return hashmap_isempty(c->polkit_registry); +} + static int run(int argc, char *argv[]) { _cleanup_(context_clear) Context context = {}; _cleanup_(sd_event_unrefp) sd_event *event = NULL; @@ -1164,7 +1170,13 @@ static int run(int argc, char *argv[]) { if (r < 0) log_warning_errno(r, "Failed to send readiness notification, ignoring: %m"); - r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL); + r = bus_event_loop_with_idle( + event, + bus, + "org.freedesktop.timedate1", + DEFAULT_EXIT_USEC, + context_check_idle, + &context); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); From 48ce0824dc67fa3e6743dca02e15fc1b91c0c55c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 8 May 2024 13:05:40 +0200 Subject: [PATCH 3/4] sd-bus: add new sd_bus_pending_method_calls() call --- man/rules/meson.build | 1 + man/sd_bus_pending_method_calls.xml | 88 +++++++++++++++++++++++++++++ src/libsystemd/libsystemd.sym | 5 ++ src/libsystemd/sd-bus/sd-bus.c | 18 ++++++ src/systemd/sd-bus.h | 2 + 5 files changed, 114 insertions(+) create mode 100644 man/sd_bus_pending_method_calls.xml diff --git a/man/rules/meson.build b/man/rules/meson.build index a9a2a25c0d..c3e1eefd8a 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -418,6 +418,7 @@ manpages = [ '3', ['sd_bus_path_decode', 'sd_bus_path_decode_many', 'sd_bus_path_encode_many'], ''], + ['sd_bus_pending_method_calls', '3', [], ''], ['sd_bus_process', '3', [], ''], ['sd_bus_query_sender_creds', '3', ['sd_bus_query_sender_privilege'], ''], ['sd_bus_reply_method_error', diff --git a/man/sd_bus_pending_method_calls.xml b/man/sd_bus_pending_method_calls.xml new file mode 100644 index 0000000000..063009cf94 --- /dev/null +++ b/man/sd_bus_pending_method_calls.xml @@ -0,0 +1,88 @@ + + + + + + + + sd_bus_pending_method_calls + systemd + + + + sd_bus_pending_method_calls + 3 + + + + sd_bus_pending_method_calls + + Return the number of currently pending, outgoing method calls + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_pending_method_calls + sd_bus *bus + + + + + + + Description + + sd_bus_pending_method_calls() returns the number of currently pending outgoing + method calls, i.e. method calls enqueued with + sd_bus_call_async3 for + which no reply has been received yet, and which have not reached a time-out yet. + + The bus argument may be NULL, in which case zero is + returned. + + + + + Return Value + + This function returns 0 if there are no pending method calls, or a NULL bus + object was specified. On failure, a negative errno-style error code is returned. + + + Errors + + Returned errors may indicate the following problems: + + + + -ECHILD + + The bus connection has been created in a different process, library or module instance. + + + + + + + + + History + sd_bus_pending_method_calls() was added in version 257. + + + + See Also + + + systemd1 + sd-bus3 + sd_bus_call_async3 + + + + diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 78b4453462..6874240b8a 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -843,3 +843,8 @@ global: sd_journal_stream_fd_with_namespace; sd_event_source_get_inotify_path; } LIBSYSTEMD_255; + +LIBSYSTEMD_257 { +global: + sd_bus_pending_method_calls; +} LIBSYSTEMD_256; diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 1a642cb197..738afe0e94 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -4471,3 +4471,21 @@ _public_ int sd_bus_enqueue_for_read(sd_bus *bus, sd_bus_message *m) { bus->rqueue[bus->rqueue_size++] = bus_message_ref_queued(m, bus); return 0; } + +_public_ int sd_bus_pending_method_calls(sd_bus *bus) { + + /* Returns the number of currently pending asynchronous method calls. This is graceful, i.e. an + * unallocated (i.e. NULL) bus connection has no method calls pending. */ + + if (!bus) + return 0; + + assert_return(bus = bus_resolve(bus), -ENOPKG); + + if (!BUS_IS_OPEN(bus->state)) + return 0; + + size_t n = ordered_hashmap_size(bus->reply_callbacks); + + return n > INT_MAX ? INT_MAX : (int) n; /* paranoid overflow check */ +} diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index f1a3311992..2d8efe5c7e 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -238,6 +238,8 @@ int sd_bus_add_fallback_vtable(sd_bus *bus, sd_bus_slot **slot, const char *pref int sd_bus_add_node_enumerator(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_node_enumerator_t callback, void *userdata); int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path); +int sd_bus_pending_method_calls(sd_bus *bus); + /* Slot object */ sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot); From 75fb956f70513669a3f09bab93c3eb76d72dbe4f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 8 May 2024 13:08:38 +0200 Subject: [PATCH 4/4] bus-util: check sd_bus_pending_method_calls() when determining whether a service is idle --- src/shared/bus-util.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 30f9602b1e..216c16f039 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -122,7 +122,9 @@ int bus_event_loop_with_idle( if (r == SD_EVENT_FINISHED) break; - if (check_idle) + if (sd_bus_pending_method_calls(bus) > 0) + idle = false; + else if (check_idle) idle = check_idle(userdata); else idle = true;