From e4c98db335673dcbd7cff3add516db92861302b2 Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Fri, 8 Dec 2017 11:26:30 -0600 Subject: [PATCH 1/3] watchdog: allow a device path to be specified Currently systemd hardcodes the use of /dev/watchdog. This is a legacy chardev that points to watchdog0 in the system. Modify the watchdog API to allow a different device path to be passed and stored. Opening the watchdog defaults to /dev/watchdog, maintaining existing behavior. --- src/shared/watchdog.c | 9 ++++++++- src/shared/watchdog.h | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c index a6a356dbd0..b0a422da84 100644 --- a/src/shared/watchdog.c +++ b/src/shared/watchdog.c @@ -27,10 +27,12 @@ #include "fd-util.h" #include "log.h" +#include "string-util.h" #include "time-util.h" #include "watchdog.h" static int watchdog_fd = -1; +static char *watchdog_device = NULL; static usec_t watchdog_timeout = USEC_INFINITY; static int update_timeout(void) { @@ -84,7 +86,8 @@ static int open_watchdog(void) { if (watchdog_fd >= 0) return 0; - watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC); + watchdog_fd = open(watchdog_device ?: "/dev/watchdog", + O_WRONLY|O_CLOEXEC); if (watchdog_fd < 0) return -errno; @@ -96,6 +99,10 @@ static int open_watchdog(void) { return update_timeout(); } +int watchdog_set_device(char *path) { + return free_and_strdup(&watchdog_device, path); +} + int watchdog_set_timeout(usec_t *usec) { int r; diff --git a/src/shared/watchdog.h b/src/shared/watchdog.h index 8c17e7e1dc..5694338db3 100644 --- a/src/shared/watchdog.h +++ b/src/shared/watchdog.h @@ -25,6 +25,11 @@ #include "time-util.h" #include "util.h" +int watchdog_set_device(char *path); int watchdog_set_timeout(usec_t *usec); int watchdog_ping(void); void watchdog_close(bool disarm); + +static inline void watchdog_free_device(void) { + (void) watchdog_set_device(NULL); +} From 8a2c1fbfc377b20032ebc4cc287dda848554d61b Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Fri, 8 Dec 2017 11:26:44 -0600 Subject: [PATCH 2/3] core: Add WatchdogDevice config option and implement it This option allows a device path to be specified for the systemd watchdog (both runtime and shutdown). If a system requires a watchdog other than /dev/watchdog (pointing to /dev/watchdog0) to be used to reboot the system, this setting should be changed to the relevant watchdog device path (e.g. /dev/watchdog1). --- src/core/main.c | 32 ++++++++++++++++++++++++++++++-- src/core/shutdown.c | 11 +++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index 08d9188385..4995446c51 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -120,6 +120,7 @@ static usec_t arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL; static unsigned arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST; static usec_t arg_runtime_watchdog = 0; static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE; +static char *arg_watchdog_device = NULL; static char **arg_default_environment = NULL; static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {}; static uint64_t arg_capability_bounding_set = CAP_ALL; @@ -461,6 +462,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (arg_default_timeout_start_usec <= 0) arg_default_timeout_start_usec = USEC_INFINITY; + } else if (proc_cmdline_key_streq(key, "systemd.watchdog_device")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + parse_path_argument_and_warn(value, false, &arg_watchdog_device); + } else if (streq(key, "quiet") && !value) { if (arg_show_status == _SHOW_STATUS_UNSET) @@ -751,6 +759,7 @@ static int parse_config_file(void) { { "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers }, { "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog }, { "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog }, + { "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device }, { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set }, #if HAVE_SECCOMP { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs }, @@ -1521,7 +1530,11 @@ static int become_shutdown( /* Tell the binary how often to ping, ignore failure */ if (asprintf(&e, "WATCHDOG_USEC="USEC_FMT, arg_shutdown_watchdog) > 0) - (void) strv_push(&env_block, e); + (void) strv_consume(&env_block, e); + + if (arg_watchdog_device && + asprintf(&e, "WATCHDOG_DEVICE=%s", arg_watchdog_device) > 0) + (void) strv_consume(&env_block, e); } else watchdog_close(true); @@ -1914,6 +1927,13 @@ static int initialize_runtime( write_container_id(); } + if (arg_system && arg_watchdog_device) { + r = watchdog_set_device(arg_watchdog_device); + if (r < 0) + log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", + arg_watchdog_device); + } + if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY) watchdog_set_timeout(&arg_runtime_watchdog); @@ -2450,8 +2470,13 @@ finish: * here explicitly. valgrind will only generate nice output on * exit(), not on exec(), hence let's do the former not the * latter here. */ - if (getpid_cached() == 1 && RUNNING_ON_VALGRIND) + if (getpid_cached() == 1 && RUNNING_ON_VALGRIND) { + /* Cleanup watchdog_device strings for valgrind. We need them + * in become_shutdown() so normally we cannot free them yet. */ + watchdog_free_device(); + arg_watchdog_device = mfree(arg_watchdog_device); return 0; + } #endif if (shutdown_verb) { @@ -2461,6 +2486,9 @@ finish: error_message = "Failed to execute shutdown binary"; } + watchdog_free_device(); + arg_watchdog_device = mfree(arg_watchdog_device); + if (getpid_cached() == 1) { if (error_message) manager_status_printf(NULL, STATUS_TYPE_EMERGENCY, diff --git a/src/core/shutdown.c b/src/core/shutdown.c index d54661b698..d09ef190a2 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -167,6 +167,7 @@ int main(int argc, char *argv[]) { unsigned retries; int cmd, r; static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL}; + char *watchdog_device; log_parse_environment(); r = parse_argv(argc, argv); @@ -207,6 +208,13 @@ int main(int argc, char *argv[]) { in_container = detect_container() > 0; use_watchdog = !!getenv("WATCHDOG_USEC"); + watchdog_device = getenv("WATCHDOG_DEVICE"); + if (watchdog_device) { + r = watchdog_set_device(watchdog_device); + if (r < 0) + log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", + watchdog_device); + } /* Lock us into memory */ mlockall(MCL_CURRENT|MCL_FUTURE); @@ -320,6 +328,9 @@ int main(int argc, char *argv[]) { initrd_jump: + /* We're done with the watchdog. */ + watchdog_free_device(); + arguments[0] = NULL; arguments[1] = arg_verb; arguments[2] = NULL; From c75396c30bd99413c68aad7d3872054d3c61a68c Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Fri, 8 Dec 2017 11:27:01 -0600 Subject: [PATCH 3/3] documentation: add description for watchdog device path Document the command line parameter and the system configuration file setting. --- man/kernel-command-line.xml | 9 +++++++++ man/systemd-system.conf.xml | 15 +++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 1864895ee2..422c0607da 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -382,6 +382,15 @@ + + systemd.watchdog_device= + + + Overwrites the watchdog device path WatchdogDevice=. For details, see + systemd-system.conf5. + + + modules_load= rd.modules_load= diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml index 43715813ed..08f6c91901 100644 --- a/man/systemd-system.conf.xml +++ b/man/systemd-system.conf.xml @@ -161,8 +161,10 @@ d, w). If RuntimeWatchdogSec= is set to a non-zero value, the watchdog hardware - (/dev/watchdog) will be programmed to - automatically reboot the system if it is not contacted within + (/dev/watchdog or the path specified with + WatchdogDevice= or the kernel option + systemd.watchdog-device=) will be programmed + to automatically reboot the system if it is not contacted within the specified timeout interval. The system manager will ensure to contact it at least once in half the specified timeout interval. This feature requires a hardware watchdog device to @@ -179,6 +181,15 @@ available. + + WatchdogDevice= + + Configure the hardware watchdog device that the + runtime and shutdown watchdog timers will open and use. Defaults + to /dev/watchdog. This setting has no + effect if a hardware watchdog is not available. + + CapabilityBoundingSet=