systemd: patch in watchdog device configuration change

upstream: https://github.com/systemd/systemd/pull/7352

Change-Id: I62df65eeec786890c0a7b923fd0455f7869f38b9
Signed-off-by: Edward A. James <eajames@us.ibm.com>
diff --git a/common/recipes-core/systemd/systemd/0001-watchdog-allow-a-device-path-to-be-specified.patch b/common/recipes-core/systemd/systemd/0001-watchdog-allow-a-device-path-to-be-specified.patch
new file mode 100644
index 0000000..7dddd61
--- /dev/null
+++ b/common/recipes-core/systemd/systemd/0001-watchdog-allow-a-device-path-to-be-specified.patch
@@ -0,0 +1,73 @@
+From 9d1f2858cd2ba49066ca319164b4e3e2769fc0fb Mon Sep 17 00:00:00 2001
+From: "Edward A. James" <eajames@us.ibm.com>
+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 4f3e012..8068e95 100644
+--- a/src/shared/watchdog.c
++++ b/src/shared/watchdog.c
+@@ -26,10 +26,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) {
+@@ -83,7 +85,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;
+ 
+@@ -95,6 +98,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 f6ec178..90a075a 100644
+--- a/src/shared/watchdog.h
++++ b/src/shared/watchdog.h
+@@ -24,6 +24,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);
++}
+-- 
+1.8.3.1
+
diff --git a/common/recipes-core/systemd/systemd/0002-core-Add-WatchdogDevice-config-option-and-implement-.patch b/common/recipes-core/systemd/systemd/0002-core-Add-WatchdogDevice-config-option-and-implement-.patch
new file mode 100644
index 0000000..4e10065
--- /dev/null
+++ b/common/recipes-core/systemd/systemd/0002-core-Add-WatchdogDevice-config-option-and-implement-.patch
@@ -0,0 +1,141 @@
+From 9a7b66daa24ed5c6a994323b308c3c3a0a10bf6a Mon Sep 17 00:00:00 2001
+From: Eddie James <eajames@us.ibm.com>
+Date: Thu, 5 Apr 2018 12:31:15 -0500
+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 07e3e97..d4673bf 100644
+--- a/src/core/main.c
++++ b/src/core/main.c
+@@ -119,6 +119,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;
+@@ -459,6 +460,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)
+@@ -715,6 +723,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           },
+ #ifdef HAVE_SECCOMP
+                 { "Manager", "SystemCallArchitectures",   config_parse_syscall_archs,    0, &arg_syscall_archs                     },
+@@ -1776,6 +1785,13 @@ int main(int argc, char *argv[]) {
+                         test_usr();
+                 }
+ 
++                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);
+ 
+@@ -2147,8 +2163,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() == 1 && RUNNING_ON_VALGRIND)
++        if (getpid() == 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) {
+@@ -2211,7 +2232,11 @@ finish:
+ 
+                         /* 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);
+ 
+@@ -2226,6 +2251,9 @@ finish:
+                           getpid() == 1 ? "freezing" : "quitting");
+         }
+ 
++        watchdog_free_device();
++        arg_watchdog_device = mfree(arg_watchdog_device);
++
+         if (getpid() == 1) {
+                 if (error_message)
+                         manager_status_printf(NULL, STATUS_TYPE_EMERGENCY,
+diff --git a/src/core/shutdown.c b/src/core/shutdown.c
+index a7d5e57..5f0d961 100644
+--- a/src/core/shutdown.c
++++ b/src/core/shutdown.c
+@@ -166,6 +166,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);
+@@ -206,6 +207,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);
+@@ -319,6 +327,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;
+-- 
+1.8.3.1
+
diff --git a/common/recipes-core/systemd/systemd_%.bbappend b/common/recipes-core/systemd/systemd_%.bbappend
index a157bfe..0fd7c7a 100644
--- a/common/recipes-core/systemd/systemd_%.bbappend
+++ b/common/recipes-core/systemd/systemd_%.bbappend
@@ -25,6 +25,9 @@
 SRC_URI += "file://0007-journal-Add-Synchronize-dbus-method.patch"
 SRC_URI_append_df-obmc-ubi-fs = " file://software.conf"
 
+SRC_URI += "file://0001-watchdog-allow-a-device-path-to-be-specified.patch"
+SRC_URI += "file://0002-core-Add-WatchdogDevice-config-option-and-implement-.patch"
+
 RRECOMMENDS_${PN} += "obmc-targets"
 FILES_${PN} += "${libdir}/systemd/network/default.network"
 FILES_${PN} += "${libdir}/systemd/system.conf.d/service-restart-policy.conf"