From 986935cf6adccf47f5a8a543885fb1d36f5dfb61 Mon Sep 17 00:00:00 2001 From: Franck Bui Date: Wed, 22 Apr 2020 16:16:47 +0200 Subject: [PATCH] pid1: update manager settings on reload too Most complexity of this patch is due to the fact that some manager settings (basically the watchdog properties) can be set at runtime and in this case the runtime values must be retained over daemon-reload or daemon-reexec. For consistency sake, all watchdog properties behaves now the same way, that is: - Values defined by config files can be overridden by writing the new value through their respective D-BUS properties. In this case, these values are preserved over reload/reexec until the special value '0' or USEC_INFINITY is written, which will then restore the last values loaded from the config files. If the restored value is '0' or 'USEC_INFINITY', the watchdogs will be disabled and the corresponding device will be closed. - Reading the properties from a user instance will return the USEC_INFINITY value as these properties are only meaningful for PID1. - Writing to one of the watchdog properties of a user instance's will be a NOP. Fixes: #15453 --- src/core/dbus-manager.c | 114 +++++++++++++++++++++++++++++++++++----- src/core/main.c | 28 +++++----- src/core/manager.c | 101 +++++++++++++++++++++++++++++++++-- src/core/manager.h | 16 ++++-- 4 files changed, 226 insertions(+), 33 deletions(-) diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index f8a13bd637..51254b92da 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -244,6 +244,76 @@ static int property_get_show_status( return sd_bus_message_append_basic(reply, 'b', &b); } +static int property_get_runtime_watchdog( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(m); + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_RUNTIME)); +} + +static int property_get_reboot_watchdog( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(m); + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_REBOOT)); +} + +static int property_get_kexec_watchdog( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(m); + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_KEXEC)); +} + +static int property_set_watchdog(Manager *m, WatchdogType type, sd_bus_message *value) { + usec_t timeout; + int r; + + assert(m); + assert(value); + + assert_cc(sizeof(usec_t) == sizeof(uint64_t)); + + r = sd_bus_message_read(value, "t", &timeout); + if (r < 0) + return r; + + return manager_set_watchdog_overridden(m, type, timeout); +} + static int property_set_runtime_watchdog( sd_bus *bus, const char *path, @@ -253,19 +323,37 @@ static int property_set_runtime_watchdog( void *userdata, sd_bus_error *error) { - usec_t *t = userdata; - int r; + return property_set_watchdog(userdata, WATCHDOG_RUNTIME, value); +} +static int property_set_reboot_watchdog( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + return property_set_watchdog(userdata, WATCHDOG_REBOOT, value); +} + +static int property_set_kexec_watchdog( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(m); assert(bus); assert(value); - assert_cc(sizeof(usec_t) == sizeof(uint64_t)); - - r = sd_bus_message_read(value, "t", t); - if (r < 0) - return r; - - return watchdog_set_timeout(t); + return property_set_watchdog(userdata, WATCHDOG_KEXEC, value); } static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char *name, Unit **ret_unit, sd_bus_error *error) { @@ -2404,11 +2492,11 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0), - SD_BUS_WRITABLE_PROPERTY("RebootWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, reboot_watchdog), 0), + SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", property_get_runtime_watchdog, property_set_runtime_watchdog, 0, 0), + SD_BUS_WRITABLE_PROPERTY("RebootWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, 0), /* The following item is an obsolete alias */ - SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, reboot_watchdog), SD_BUS_VTABLE_HIDDEN), - SD_BUS_WRITABLE_PROPERTY("KExecWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, kexec_watchdog), 0), + SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_WRITABLE_PROPERTY("KExecWatchdogUSec", "t", property_get_kexec_watchdog, property_set_kexec_watchdog, 0, 0), SD_BUS_WRITABLE_PROPERTY("ServiceWatchdogs", "b", bus_property_get_bool, bus_property_set_bool, offsetof(Manager, service_watchdogs), 0), SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0), SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0), diff --git a/src/core/main.c b/src/core/main.c index c9eaf70bd6..4cb61af745 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -703,16 +703,18 @@ static void set_manager_settings(Manager *m) { assert(m); - /* Propagates the various manager settings into the manager object, i.e. properties that effect the manager - * itself (as opposed to just being inherited into newly allocated units, see set_manager_defaults() above). */ + /* Propagates the various manager settings into the manager object, i.e. properties that + * effect the manager itself (as opposed to just being inherited into newly allocated + * units, see set_manager_defaults() above). */ m->confirm_spawn = arg_confirm_spawn; m->service_watchdogs = arg_service_watchdogs; - m->runtime_watchdog = arg_runtime_watchdog; - m->reboot_watchdog = arg_reboot_watchdog; - m->kexec_watchdog = arg_kexec_watchdog; m->cad_burst_action = arg_cad_burst_action; + manager_set_watchdog(m, WATCHDOG_RUNTIME, arg_runtime_watchdog); + manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog); + manager_set_watchdog(m, WATCHDOG_KEXEC, arg_kexec_watchdog); + manager_set_show_status(m, arg_show_status, "commandline"); m->status_unit_format = arg_status_unit_format; } @@ -1784,6 +1786,7 @@ static int invoke_main_loop( (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock); set_manager_defaults(m); + set_manager_settings(m); update_cpu_affinity(false); update_numa_policy(false); @@ -1974,9 +1977,6 @@ static int initialize_runtime( if (r < 0) log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", arg_watchdog_device); } - - if (timestamp_is_set(arg_runtime_watchdog)) - watchdog_set_timeout(&arg_runtime_watchdog); } if (arg_timer_slack_nsec != NSEC_INFINITY) @@ -2250,6 +2250,10 @@ static int parse_configuration(const struct rlimit *saved_rlimit_nofile, /* Note that this also parses bits from the kernel command line, including "debug". */ log_parse_environment(); + /* Initialize the show status setting if it hasn't been set explicitly yet */ + if (arg_show_status == _SHOW_STATUS_INVALID) + arg_show_status = SHOW_STATUS_YES; + return 0; } @@ -2273,10 +2277,6 @@ static int load_configuration( return r; } - /* Initialize the show status setting if it hasn't been set explicitly yet */ - if (arg_show_status == _SHOW_STATUS_INVALID) - arg_show_status = SHOW_STATUS_YES; - return 0; } @@ -2753,8 +2753,8 @@ finish: pager_close(); if (m) { - arg_reboot_watchdog = m->reboot_watchdog; - arg_kexec_watchdog = m->kexec_watchdog; + arg_reboot_watchdog = manager_get_watchdog(m, WATCHDOG_REBOOT); + arg_kexec_watchdog = manager_get_watchdog(m, WATCHDOG_KEXEC); m = manager_free(m); } diff --git a/src/core/manager.c b/src/core/manager.c index 1beab6baed..c57ad1490d 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -779,6 +779,10 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager .original_log_level = -1, .original_log_target = _LOG_TARGET_INVALID, + .watchdog_overridden[WATCHDOG_RUNTIME] = USEC_INFINITY, + .watchdog_overridden[WATCHDOG_REBOOT] = USEC_INFINITY, + .watchdog_overridden[WATCHDOG_KEXEC] = USEC_INFINITY, + .notify_fd = -1, .cgroups_agent_fd = -1, .signal_fd = -1, @@ -2922,9 +2926,10 @@ int manager_loop(Manager *m) { return log_error_errno(r, "Failed to enable SIGCHLD event source: %m"); while (m->objective == MANAGER_OK) { - usec_t wait_usec; + usec_t wait_usec, watchdog_usec; - if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m)) + watchdog_usec = manager_get_watchdog(m, WATCHDOG_RUNTIME); + if (timestamp_is_set(watchdog_usec)) watchdog_ping(); if (!ratelimit_below(&rl)) { @@ -2955,7 +2960,7 @@ int manager_loop(Manager *m) { continue; /* Sleep for watchdog runtime wait time */ - if (MANAGER_IS_SYSTEM(m)) + if (timestamp_is_set(watchdog_usec)) wait_usec = watchdog_runtime_wait(); else wait_usec = USEC_INFINITY; @@ -3196,6 +3201,10 @@ int manager_serialize( if (m->log_target_overridden) (void) serialize_item(f, "log-target-override", log_target_to_string(log_get_target())); + (void) serialize_usec(f, "runtime-watchdog-overridden", m->watchdog_overridden[WATCHDOG_RUNTIME]); + (void) serialize_usec(f, "reboot-watchdog-overridden", m->watchdog_overridden[WATCHDOG_REBOOT]); + (void) serialize_usec(f, "kexec-watchdog-overridden", m->watchdog_overridden[WATCHDOG_KEXEC]); + for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) { _cleanup_free_ char *joined = NULL; @@ -3328,6 +3337,68 @@ static int manager_deserialize_units(Manager *m, FILE *f, FDSet *fds) { return 0; } +usec_t manager_get_watchdog(Manager *m, WatchdogType t) { + assert(m); + + if (MANAGER_IS_USER(m)) + return USEC_INFINITY; + + if (timestamp_is_set(m->watchdog_overridden[t])) + return m->watchdog_overridden[t]; + + return m->watchdog[t]; +} + +void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout) { + int r = 0; + + assert(m); + + if (MANAGER_IS_USER(m)) + return; + + if (m->watchdog[t] == timeout) + return; + + if (t == WATCHDOG_RUNTIME) + if (!timestamp_is_set(m->watchdog_overridden[WATCHDOG_RUNTIME])) { + if (timestamp_is_set(timeout)) + r = watchdog_set_timeout(&timeout); + else + watchdog_close(true); + } + + if (r >= 0) + m->watchdog[t] = timeout; +} + +int manager_set_watchdog_overridden(Manager *m, WatchdogType t, usec_t timeout) { + int r = 0; + + assert(m); + + if (MANAGER_IS_USER(m)) + return 0; + + if (m->watchdog_overridden[t] == timeout) + return 0; + + if (t == WATCHDOG_RUNTIME) { + usec_t *p; + + p = timestamp_is_set(timeout) ? &timeout : &m->watchdog[t]; + if (timestamp_is_set(*p)) + r = watchdog_set_timeout(p); + else + watchdog_close(true); + } + + if (r >= 0) + m->watchdog_overridden[t] = timeout; + + return 0; +} + int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { int r = 0; @@ -3451,6 +3522,30 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else manager_override_log_target(m, target); + } else if ((val = startswith(l, "runtime-watchdog-overridden="))) { + usec_t t; + + if (deserialize_usec(val, &t) < 0) + log_notice("Failed to parse runtime-watchdog-overridden value '%s', ignoring.", val); + else + manager_set_watchdog_overridden(m, WATCHDOG_RUNTIME, t); + + } else if ((val = startswith(l, "reboot-watchdog-overridden="))) { + usec_t t; + + if (deserialize_usec(val, &t) < 0) + log_notice("Failed to parse reboot-watchdog-overridden value '%s', ignoring.", val); + else + manager_set_watchdog_overridden(m, WATCHDOG_REBOOT, t); + + } else if ((val = startswith(l, "kexec-watchdog-overridden="))) { + usec_t t; + + if (deserialize_usec(val, &t) < 0) + log_notice("Failed to parse kexec-watchdog-overridden value '%s', ignoring.", val); + else + manager_set_watchdog_overridden(m, WATCHDOG_KEXEC, t); + } else if (startswith(l, "env=")) { r = deserialize_environment(l + 4, &m->client_environment); if (r < 0) diff --git a/src/core/manager.h b/src/core/manager.h index 10c34f9543..89ae3d09fd 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -114,6 +114,13 @@ typedef enum ManagerTimestamp { _MANAGER_TIMESTAMP_INVALID = -1, } ManagerTimestamp; +typedef enum WatchdogType { + WATCHDOG_RUNTIME, + WATCHDOG_REBOOT, + WATCHDOG_KEXEC, + _WATCHDOG_TYPE_MAX, +} WatchdogType; + #include "execute.h" #include "job.h" #include "path-lookup.h" @@ -231,9 +238,8 @@ struct Manager { char **transient_environment; /* The environment, as determined from config files, kernel cmdline and environment generators */ char **client_environment; /* Environment variables created by clients through the bus API */ - usec_t runtime_watchdog; - usec_t reboot_watchdog; - usec_t kexec_watchdog; + usec_t watchdog[_WATCHDOG_TYPE_MAX]; + usec_t watchdog_overridden[_WATCHDOG_TYPE_MAX]; dual_timestamp timestamps[_MANAGER_TIMESTAMP_MAX]; @@ -555,5 +561,9 @@ const char *manager_timestamp_to_string(ManagerTimestamp m) _const_; ManagerTimestamp manager_timestamp_from_string(const char *s) _pure_; ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s); +usec_t manager_get_watchdog(Manager *m, WatchdogType t); +void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout); +int manager_set_watchdog_overridden(Manager *m, WatchdogType t, usec_t timeout); + const char* oom_policy_to_string(OOMPolicy i) _const_; OOMPolicy oom_policy_from_string(const char *s) _pure_;