summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarel Zak2019-06-06 11:17:13 +0200
committerKarel Zak2019-06-17 15:47:39 +0200
commitb3dd29d1b878865102c5cb366dd3e814ce043cb9 (patch)
tree46d32f2850fd0b0b14a3f23cdfc12c642a29d2dd
parentlib/path: make sure ul_path_read_buffer() derminate result (diff)
downloadkernel-qcow2-util-linux-b3dd29d1b878865102c5cb366dd3e814ce043cb9.tar.gz
kernel-qcow2-util-linux-b3dd29d1b878865102c5cb366dd3e814ce043cb9.tar.xz
kernel-qcow2-util-linux-b3dd29d1b878865102c5cb366dd3e814ce043cb9.zip
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 <kzak@redhat.com>
-rw-r--r--include/pathnames.h1
-rw-r--r--sys-utils/wdctl.84
-rw-r--r--sys-utils/wdctl.c81
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 <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)