]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: add varlink Reload/Reexecute methods 39561/head
authorLuca Boccassi <luca.boccassi@gmail.com>
Mon, 3 Nov 2025 00:44:57 +0000 (00:44 +0000)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 13 Nov 2025 00:07:23 +0000 (00:07 +0000)
Same as the D-Bus ones

src/core/dbus-manager.c
src/core/manager.c
src/core/manager.h
src/core/varlink-manager.c
src/core/varlink-manager.h
src/core/varlink.c
src/core/varlink.h
src/shared/varlink-io.systemd.Manager.c
test/units/TEST-59-RELOADING-RESTART.sh
test/units/TEST-74-AUX-UTILS.varlinkctl.sh

index 3f3d0f2d62fba2b0941a99c6ace1e148f817524e..ea4b1f3767947914d8b3670e93a5ce1af545bf0e 100644 (file)
@@ -1556,6 +1556,7 @@ static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *
          * finished. */
 
         assert(!m->pending_reload_message_dbus);
+        assert(!m->pending_reload_message_vl);
         r = sd_bus_message_new_method_return(message, &m->pending_reload_message_dbus);
         if (r < 0)
                 return r;
index f8b748a4acd65f400805268fa34723a90eaf4490..1dfacadc29203b612d05a93a53b663510dae94c9 100644 (file)
@@ -2570,7 +2570,7 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) {
 
         /* When we are reloading, let's not wait with generating signals, since we need to exit the manager as quickly
          * as we can. There's no point in throttling generation of signals in that case. */
-        if (MANAGER_IS_RELOADING(m) || m->send_reloading_done || m->pending_reload_message_dbus)
+        if (MANAGER_IS_RELOADING(m) || m->send_reloading_done || m->pending_reload_message_dbus || m->pending_reload_message_vl)
                 budget = UINT_MAX; /* infinite budget in this case */
         else {
                 /* Anything to do at all? */
@@ -2628,6 +2628,11 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) {
                 n++;
         }
 
+        if (m->pending_reload_message_vl) {
+                manager_varlink_send_pending_reload_message(m);
+                n++;
+        }
+
         return n;
 }
 
index 8c57c1d9a258f0ce4598778a6f6fd6327f985276..2de287e1ca087503b559b155d5ec686996757aa7 100644 (file)
@@ -316,8 +316,10 @@ typedef struct Manager {
         sd_id128_t bus_id, deserialized_bus_id;
 
         /* This is used during reloading: before the reload we queue
-         * the reply message here, and afterwards we send it */
+         * the reply message here, and afterwards we send it.
+         * It can be either a D-Bus message or a Varlink message, but not both. */
         sd_bus_message *pending_reload_message_dbus;
+        sd_varlink *pending_reload_message_vl;
 
         Hashmap *watch_bus;  /* D-Bus names => Unit object n:1 */
 
index 3414ccb9edc6f2e792efdb629e09894f9327b762..35c752f81ec7ad9f11b64c1e04a808a738f41439 100644 (file)
@@ -7,9 +7,13 @@
 #include "alloc-util.h"
 #include "architecture.h"
 #include "build.h"
+#include "bus-polkit.h"
 #include "confidential-virt.h"
 #include "json-util.h"
 #include "manager.h"
+#include "pidref.h"
+#include "process-util.h"
+#include "selinux-access.h"
 #include "set.h"
 #include "strv.h"
 #include "syslog-util.h"
@@ -17,6 +21,7 @@
 #include "version.h"
 #include "varlink-common.h"
 #include "varlink-manager.h"
+#include "varlink-util.h"
 #include "virt.h"
 #include "watchdog.h"
 
@@ -181,3 +186,98 @@ int vl_method_describe_manager(sd_varlink *link, sd_json_variant *parameters, sd
 
         return sd_varlink_reply(link, v);
 }
+
+int vl_method_reload_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        Manager *manager = ASSERT_PTR(userdata);
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL);
+        if (r != 0)
+                return r;
+
+        r = mac_selinux_access_check_varlink(link, "reload");
+        if (r < 0)
+                return r;
+
+        r = varlink_verify_polkit_async(
+                        link,
+                        manager->system_bus,
+                        "org.freedesktop.systemd1.reload-daemon",
+                        /* details= */ NULL,
+                        &manager->polkit_registry);
+        if (r <= 0)
+                return r;
+
+        /* We need at least the pidref, otherwise there's nothing to log about. */
+        r = varlink_get_peer_pidref(link, &pidref);
+        if (r < 0)
+                log_debug_errno(r, "Failed to get peer pidref, ignoring: %m");
+        else
+                manager_log_caller(manager, &pidref, "Reload");
+
+        /* Check the rate limit after the authorization succeeds, to avoid denial-of-service issues. */
+        if (!ratelimit_below(&manager->reload_reexec_ratelimit)) {
+                log_warning("Reloading request rejected due to rate limit.");
+                return sd_varlink_error(link, VARLINK_ERROR_MANAGER_RATE_LIMIT_REACHED, NULL);
+        }
+
+        /* Instead of sending the reply back right away, we just remember that we need to and then send it
+         * after the reload is finished. That way the caller knows when the reload finished. */
+
+        assert(!manager->pending_reload_message_vl);
+        assert(!manager->pending_reload_message_dbus);
+        manager->pending_reload_message_vl = sd_varlink_ref(link);
+
+        manager->objective = MANAGER_RELOAD;
+
+        return 1;
+}
+
+int vl_method_reexecute_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        Manager *manager = ASSERT_PTR(userdata);
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL);
+        if (r != 0)
+                return r;
+
+        r = mac_selinux_access_check_varlink(link, "reload");
+        if (r < 0)
+                return r;
+
+        r = varlink_verify_polkit_async(
+                        link,
+                        manager->system_bus,
+                        "org.freedesktop.systemd1.reload-daemon",
+                        /* details= */ NULL,
+                        &manager->polkit_registry);
+        if (r <= 0)
+                return r;
+
+        /* We need at least the pidref, otherwise there's nothing to log about. */
+        r = varlink_get_peer_pidref(link, &pidref);
+        if (r < 0)
+                log_debug_errno(r, "Failed to get peer pidref, ignoring: %m");
+        else
+                manager_log_caller(manager, &pidref, "Reexecute");
+
+        /* Check the rate limit after the authorization succeeds, to avoid denial-of-service issues. */
+        if (!ratelimit_below(&manager->reload_reexec_ratelimit)) {
+                log_warning("Reexecution request rejected due to rate limit.");
+                return sd_varlink_error(link, VARLINK_ERROR_MANAGER_RATE_LIMIT_REACHED, NULL);
+        }
+
+        /* We don't send a reply back here, the client should just wait for us disconnecting. */
+
+        manager->objective = MANAGER_REEXECUTE;
+
+        return 1;
+}
index fde4b6137680ba8e434cb920ed962901f8ad3379..bff621d536fa92aef85c72489bab44f96da21e41 100644 (file)
@@ -3,4 +3,8 @@
 
 #include "core-forward.h"
 
+#define VARLINK_ERROR_MANAGER_RATE_LIMIT_REACHED "io.systemd.Manager.RateLimitReached"
+
 int vl_method_describe_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
+int vl_method_reexecute_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
+int vl_method_reload_manager(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
index 90e023cc163eca6b0f54f3689400a92b22191fd8..99f12c59e562f956c136ac58bd9d6986e20b8430 100644 (file)
@@ -384,6 +384,8 @@ int manager_setup_varlink_server(Manager *m) {
         r = sd_varlink_server_bind_method_many(
                         s,
                         "io.systemd.Manager.Describe", vl_method_describe_manager,
+                        "io.systemd.Manager.Reexecute", vl_method_reexecute_manager,
+                        "io.systemd.Manager.Reload", vl_method_reload_manager,
                         "io.systemd.Unit.List", vl_method_list_units,
                         "io.systemd.service.Ping", varlink_method_ping,
                         "io.systemd.service.GetEnvironment", varlink_method_get_environment);
@@ -495,3 +497,18 @@ void manager_varlink_done(Manager *m) {
         m->varlink_server = sd_varlink_server_unref(m->varlink_server);
         m->managed_oom_varlink = sd_varlink_close_unref(m->managed_oom_varlink);
 }
+
+void manager_varlink_send_pending_reload_message(Manager *m) {
+        int r;
+
+        assert(m);
+
+        if (!m->pending_reload_message_vl)
+                return;
+
+        r = sd_varlink_reply(m->pending_reload_message_vl, /* parameters= */ NULL);
+        if (r < 0)
+                log_warning_errno(r, "Failed to send queued reload message, ignoring: %m");
+
+        m->pending_reload_message_vl = sd_varlink_unref(m->pending_reload_message_vl);
+}
index 72e6a2b130d5fe502f42041145668f52d6281b81..f959cf8a670e109329484a427876580e8d9de3b9 100644 (file)
@@ -12,3 +12,5 @@ void manager_varlink_done(Manager *m);
  * - The value of ManagedOOM*= properties change
  * - A unit with ManagedOOM*= properties changes unit active state */
 int manager_varlink_send_managed_oom_update(Unit *u);
+
+void manager_varlink_send_pending_reload_message(Manager *m);
index cdcaa53b71682d8322f0b95b95df232a05da850b..dff8127bb5b95e9c2bedbc7b7e774d74cf5f344f 100644 (file)
@@ -173,10 +173,23 @@ static SD_VARLINK_DEFINE_METHOD(
                 SD_VARLINK_FIELD_COMMENT("Runtime information of the manager"),
                 SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(runtime, ManagerRuntime, 0));
 
+static SD_VARLINK_DEFINE_METHOD(
+                Reexecute);
+
+static SD_VARLINK_DEFINE_METHOD(
+                Reload);
+
+static SD_VARLINK_DEFINE_ERROR(RateLimitReached);
+
 SD_VARLINK_DEFINE_INTERFACE(
                 io_systemd_Manager,
                 "io.systemd.Manager",
                 &vl_method_Describe,
+                SD_VARLINK_SYMBOL_COMMENT("Reexecute the main manager process"),
+                &vl_method_Reexecute,
+                SD_VARLINK_SYMBOL_COMMENT("Reload the manager configuration"),
+                &vl_method_Reload,
+                &vl_error_RateLimitReached,
                 &vl_type_ManagerContext,
                 &vl_type_ManagerRuntime,
                 &vl_type_Timestamp,
index 6600bd054dc075fb20efa1180cfbfe169b53371b..361d091a60d5613efc6ea2daecffcac82f516e61 100755 (executable)
@@ -89,6 +89,9 @@ systemctl daemon-reload
 # The timeout will hit (and the test will fail) if the reloads are not rate-limited
 timeout 15 bash -c 'while systemctl daemon-reload --no-block; do true; done'
 
+# Same for varlink, rate limiting is shared
+timeout 15 bash -c 'while varlinkctl call --timeout=1 /run/systemd/io.systemd.Manager io.systemd.Manager.Reload '{}'; do true; done'
+
 # Rate limit should reset after 9s
 sleep 10
 
@@ -96,6 +99,7 @@ systemctl daemon-reload
 
 # Same test for reexec, but we wait here
 timeout 15 bash -c 'while systemctl daemon-reexec; do true; done'
+timeout 15 bash -c 'while varlinkctl call --timeout=infinity /run/systemd/io.systemd.Manager io.systemd.Manager.Reload '{}'; do true; done'
 
 # Rate limit should reset after 9s
 sleep 10
index a35097ffa7a4e69fb6b2217a2d1b26ee7c7ae001..6168fc65eb0066dbe98273dbc6d6f249e091cc40 100755 (executable)
@@ -184,6 +184,9 @@ rm /tmp/describe1.json /tmp/describe2.json
 varlinkctl info /run/systemd/io.systemd.Manager
 varlinkctl introspect /run/systemd/io.systemd.Manager io.systemd.Manager
 varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.Describe '{}'
+varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.Reload '{}'
+# This will disconnect and fail, as the manager reexec and drops connections
+varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.Reexecute '{}' ||:
 
 # test io.systemd.Network
 varlinkctl info /run/systemd/netif/io.systemd.Network