* 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;
/* 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? */
n++;
}
+ if (m->pending_reload_message_vl) {
+ manager_varlink_send_pending_reload_message(m);
+ n++;
+ }
+
return n;
}
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 */
#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"
#include "version.h"
#include "varlink-common.h"
#include "varlink-manager.h"
+#include "varlink-util.h"
#include "virt.h"
#include "watchdog.h"
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;
+}
#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);
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);
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);
+}
* - 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);
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,
# 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
# 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
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