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=
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;
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);
+}