]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
wdctl; read from /sys if necessary
authorKarel Zak <kzak@redhat.com>
Thu, 6 Jun 2019 09:17:13 +0000 (11:17 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 17 Jun 2019 13:47:39 +0000 (15:47 +0200)
The device can be inaccessible for non-root user or busy (already used
by another process). In this case it seems better to read information
from /sys.

Note that /sys does not provide struct watchdog_info.options, so we
cannot print list of supported watchdog features.

Addresses: https://github.com/karelzak/util-linux/issues/804
Signed-off-by: Karel Zak <kzak@redhat.com>
include/pathnames.h
sys-utils/wdctl.8
sys-utils/wdctl.c

index 6f091d5f061cac302a10bd28eadc8ded150e65d6..2e1f19355c82761370d49e85fbf58a7149f684d6 100644 (file)
 
 #define _PATH_SYS_BLOCK                "/sys/block"
 #define _PATH_SYS_DEVBLOCK     "/sys/dev/block"
+#define _PATH_SYS_DEVCHAR      "/sys/dev/char"
 #define _PATH_SYS_CLASS                "/sys/class"
 #define _PATH_SYS_SCSI         "/sys/bus/scsi"
 
index 7edf80866f5551eed4a82a5342a3db11ce55c2ba..efd8b5917c14c3af20b25b92cbbea23522feddb8 100644 (file)
@@ -14,6 +14,10 @@ Show hardware watchdog status.  The default device is
 If more than one device is specified then the output is separated by
 one blank line.
 .PP
+If the device is already used or user has no permissions to read from the device than
+.B wdctl
+reads data from sysfs.  In this case information about supported features (flags) might be missing.
+.PP
 Note that the number of supported watchdog features is hardware specific.
 .SH OPTIONS
 .TP
index 772e6b9685d1e389cc3227627075d78124879461..0bb245bae5b6e1c7df376a2cedd59a466587f314 100644 (file)
@@ -25,6 +25,9 @@
 #include <signal.h>
 #include <assert.h>
 #include <linux/watchdog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #include <libsmartcols.h>
 
@@ -36,6 +39,7 @@
 #include "pathnames.h"
 #include "strutils.h"
 #include "carefulputc.h"
+#include "path.h"
 
 /*
  * since 2.6.18
@@ -113,12 +117,14 @@ struct wd_device {
 
        uint32_t        status;
        uint32_t        bstatus;
+       int             nowayout;
 
        struct watchdog_info ident;
 
        unsigned int    has_timeout : 1,
                        has_timeleft : 1,
-                       has_pretimeout : 1;
+                       has_pretimeout : 1,
+                       has_nowayout : 1;
 };
 
 struct wd_control {
@@ -282,6 +288,10 @@ static int show_flags(struct wd_control *ctl, struct wd_device *wd, uint32_t wan
        struct libscols_table *table;
        uint32_t flags;
 
+       /* information about supported bits is probably missing in /sys */
+       if (!wd->ident.options)
+               return 0;
+
        scols_init_debug(0);
 
        /* create output table */
@@ -388,7 +398,7 @@ static int set_watchdog(struct wd_device *wd, int timeout)
  *
  * Don't use err() or exit() here!
  */
-static int read_watchdog(struct wd_device *wd)
+static int read_watchdog_from_device(struct wd_device *wd)
 {
        int fd;
        sigset_t sigs, oldsigs;
@@ -401,13 +411,8 @@ static int read_watchdog(struct wd_device *wd)
 
        fd = open(wd->devpath, O_WRONLY|O_CLOEXEC);
 
-       if (fd < 0) {
-               if (errno == EBUSY)
-                       warnx(_("%s: watchdog already in use, terminating."),
-                                       wd->devpath);
-               warn(_("cannot open %s"), wd->devpath);
-               return -1;
-       }
+       if (fd < 0)
+               return -errno;
 
        if (ioctl(fd, WDIOC_GETSUPPORT, &wd->ident) < 0)
                warn(_("%s: failed to get information about watchdog"), wd->devpath);
@@ -445,6 +450,64 @@ static int read_watchdog(struct wd_device *wd)
        return 0;
 }
 
+/* Returns: <0 error, 0 success, 1 unssuported */
+static int read_watchdog_from_sysfs(struct wd_device *wd)
+{
+       struct path_cxt *sys;
+       struct stat st;
+       int rc;
+
+       rc = stat(wd->devpath, &st);
+       if (rc != 0)
+               return rc;
+
+       sys = ul_new_path(_PATH_SYS_DEVCHAR "/%u:%u",
+                       major(st.st_rdev), minor(st.st_rdev));
+       if (!sys)
+               return -ENOMEM;
+
+       if (ul_path_get_dirfd(sys) < 0)
+               goto nosysfs;           /* device not in /sys */
+
+       if (ul_path_access(sys, F_OK, "identity") != 0)
+               goto nosysfs;           /* no info in /sys (old miscdev?) */
+
+       ul_path_read_buffer(sys, (char *) wd->ident.identity, sizeof(wd->ident.identity), "identity");
+
+       ul_path_scanf(sys, "status", "%x", &wd->status);
+       ul_path_read_u32(sys, &wd->bstatus, "bootstatus");
+
+       if (ul_path_read_s32(sys, &wd->nowayout, "nowayout") == 0)
+               wd->has_nowayout = 1;
+       if (ul_path_read_s32(sys, &wd->timeout, "timeout") == 0)
+               wd->has_timeout = 1;
+       if (ul_path_read_s32(sys, &wd->pretimeout, "pretimeout") == 0)
+               wd->has_pretimeout = 1;
+       if (ul_path_read_s32(sys, &wd->timeleft, "timeleft") == 0)
+               wd->has_timeleft = 1;
+
+       ul_unref_path(sys);
+       return 0;
+nosysfs:
+       ul_unref_path(sys);
+       return 1;
+}
+
+static int read_watchdog(struct wd_device *wd)
+{
+       int rc = read_watchdog_from_device(wd);
+
+       if (rc == -EBUSY || rc == -EACCES || rc == -EPERM)
+               rc = read_watchdog_from_sysfs(wd);
+
+       if (rc) {
+               warn(_("cannot read information about %s"), wd->devpath);
+               return -1;
+       }
+
+       return 0;
+}
+
 static void show_timeouts(struct wd_device *wd)
 {
        if (wd->has_timeout)