watchdog: always close fd on watchdog stop
authorDragan Milenkovic <strpbrk@tesla.rcub.bg.ac.rs>
Mon, 1 May 2023 11:11:02 +0000 (13:11 +0200)
committerDaniel Golle <daniel@makrotopia.org>
Sat, 30 Mar 2024 12:55:27 +0000 (12:55 +0000)
The user may asks for procd watchdog handler to be stopped
with or without disabling it, by specifying the magicclose flag.
If the flag is set, the watchdog will be disabled and the fd closed,
allowing the user to take control over the watchdog.

There is a race in this scenario. If the system fails before
the user re-enables the watchdog, the system might hang
without a proper reset.

To prevent this, the user should ask the procd handler to be stopped
without disabling the watchdog, by specifying magicclose as false.
However, in this case, the procd will only stop refreshing the watchdog,
but will leave the fd open. At least on Raspberry Pi, this prevents
anyone else from opening the watchdog device, resulting in EBUSY.

With this patch, watchdog fd will always be closed, regardless
of the magicclose flag, allowing for the described safe use-case.

For user that previously stopped the watchdog handler
with the magicclose flag, the functionality remains unchanged.

Signed-off-by: Dragan Milenkovic <strpbrk@tesla.rcub.bg.ac.rs>
watchdog.c

index 39ae9ff9aa5823ab05c9b1af3e650a11e93cf7d3..9b50d909d170ab6e91b1b1b9bf1e0e8d0b21b6a8 100644 (file)
@@ -71,13 +71,15 @@ static int watchdog_open(bool cloexec)
        return wdt_fd;
 }
 
-static void watchdog_close(void)
+static void watchdog_close(bool with_release)
 {
        if (wdt_fd < 0)
                return;
 
-       if (write(wdt_fd, "V", 1) < 0)
-               ERROR("WDT failed to write release: %m\n");
+       if (with_release) {
+               if (write(wdt_fd, "V", 1) < 0)
+                       ERROR("WDT failed to write release: %m\n");
+       }
 
        if (close(wdt_fd) == -1)
                ERROR("WDT failed to close watchdog: %m\n");
@@ -137,8 +139,7 @@ void watchdog_set_stopped(bool val)
        if (val) {
                uloop_timeout_cancel(&wdt_timeout);
 
-               if (wdt_magicclose)
-                       watchdog_close();
+               watchdog_close(wdt_magicclose);
        }
        else {
                watchdog_open(true);