From b3dd29d1b878865102c5cb366dd3e814ce043cb9 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 6 Jun 2019 11:17:13 +0200 Subject: wdctl; read from /sys if necessary 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 --- include/pathnames.h | 1 + sys-utils/wdctl.8 | 4 +++ sys-utils/wdctl.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/include/pathnames.h b/include/pathnames.h index 6f091d5f0..2e1f19355 100644 --- a/include/pathnames.h +++ b/include/pathnames.h @@ -104,6 +104,7 @@ #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" diff --git a/sys-utils/wdctl.8 b/sys-utils/wdctl.8 index 7edf80866..efd8b5917 100644 --- a/sys-utils/wdctl.8 +++ b/sys-utils/wdctl.8 @@ -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 diff --git a/sys-utils/wdctl.c b/sys-utils/wdctl.c index 772e6b968..0bb245bae 100644 --- a/sys-utils/wdctl.c +++ b/sys-utils/wdctl.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include @@ -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) -- cgit v1.2.3-55-g7522