summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarel Zak2019-06-17 15:48:28 +0200
committerKarel Zak2019-06-17 15:48:28 +0200
commitbddcf5b81673b4cc362b36bac3caaeabb1738fcc (patch)
treeeb375002908b685c325282469a47bdbc1ff86681
parentinclude/closestream: avoid close more than once (diff)
parentwdctl; read from /sys if necessary (diff)
downloadkernel-qcow2-util-linux-bddcf5b81673b4cc362b36bac3caaeabb1738fcc.tar.gz
kernel-qcow2-util-linux-bddcf5b81673b4cc362b36bac3caaeabb1738fcc.tar.xz
kernel-qcow2-util-linux-bddcf5b81673b4cc362b36bac3caaeabb1738fcc.zip
Merge branch 'topic/wdctl'
-rw-r--r--include/pathnames.h4
-rw-r--r--lib/path.c5
-rw-r--r--sys-utils/wdctl.84
-rw-r--r--sys-utils/wdctl.c266
4 files changed, 193 insertions, 86 deletions
diff --git a/include/pathnames.h b/include/pathnames.h
index ee470ee4b..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"
@@ -167,9 +168,6 @@
/* deprecated */
#define _PATH_RAWDEVCTL_OLD "/dev/rawctl"
-/* wdctl path */
-#define _PATH_WATCHDOG_DEV "/dev/watchdog"
-
/* ipc paths */
#define _PATH_PROC_SYSV_MSG "/proc/sysvipc/msg"
#define _PATH_PROC_SYSV_SEM "/proc/sysvipc/sem"
diff --git a/lib/path.c b/lib/path.c
index f7fd19592..8dce1da40 100644
--- a/lib/path.c
+++ b/lib/path.c
@@ -610,9 +610,10 @@ int ul_path_read_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char
/* Remove tailing newline (usual in sysfs) */
if (rc > 0 && *(buf + rc - 1) == '\n')
- --rc;
+ buf[--rc] = '\0';
+ else
+ buf[rc - 1] = '\0';
- buf[rc] = '\0';
return rc;
}
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 4574e10da..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
@@ -59,10 +63,6 @@
other external alarm not a reboot */
#endif
-/* basic output flags */
-static int no_headings;
-static int raw;
-
struct wdflag {
uint32_t flag;
const char *name;
@@ -108,8 +108,8 @@ static struct colinfo infos[] = {
static int columns[ARRAY_SIZE(infos) * 2];
static int ncolumns;
-struct wdinfo {
- char *device;
+struct wd_device {
+ const char *devpath;
int timeout;
int timeleft;
@@ -117,12 +117,23 @@ struct wdinfo {
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 {
+ unsigned int show_oneline : 1,
+ show_raw : 1,
+ hide_headings : 1,
+ hide_flags : 1,
+ hide_ident : 1,
+ hide_timeouts : 1;
};
/* converts flag name to flag bit */
@@ -165,10 +176,32 @@ static struct colinfo *get_column_info(unsigned num)
return &infos[ get_column_id(num) ];
}
+/* We preffer cdev /dev/watchdog0 as this device has node in
+ * /sys/class/watchdog/. The old miscdev /dev/watchdog is fallback for old
+ * systemds only.
+ */
+static const char *get_default_device(void)
+{
+ const char **p;
+ static const char *devs[] = {
+ "/dev/watchdog0",
+ "/dev/watchdog",
+ NULL
+ };
+
+ for (p = devs; *p; p++) {
+ if (access(*p, F_OK) == 0)
+ return *p;
+ }
+
+ return NULL;
+}
+
static void __attribute__((__noreturn__)) usage(void)
{
FILE *out = stdout;
size_t i;
+ const char *dflt = get_default_device();
fputs(USAGE_HEADER, out);
fprintf(out,
@@ -193,7 +226,10 @@ static void __attribute__((__noreturn__)) usage(void)
printf(USAGE_HELP_OPTIONS(24));
fputs(USAGE_SEPARATOR, out);
- fprintf(out, _("The default device is %s.\n"), _PATH_WATCHDOG_DEV);
+ if (dflt)
+ fprintf(out, _("The default device is %s.\n"), dflt);
+ else
+ fprintf(out, _("No default device is available.\n"));
fputs(USAGE_COLUMNS, out);
for (i = 0; i < ARRAY_SIZE(infos); i++)
@@ -204,7 +240,7 @@ static void __attribute__((__noreturn__)) usage(void)
exit(EXIT_SUCCESS);
}
-static void add_flag_line(struct libscols_table *table, struct wdinfo *wd, const struct wdflag *fl)
+static void add_flag_line(struct libscols_table *table, struct wd_device *wd, const struct wdflag *fl)
{
int i;
struct libscols_line *line;
@@ -232,7 +268,7 @@ static void add_flag_line(struct libscols_table *table, struct wdinfo *wd, const
str = wd->bstatus & fl->flag ? "1" : "0";
break;
case COL_DEVICE:
- str = wd->device;
+ str = wd->devpath;
break;
default:
break;
@@ -245,13 +281,17 @@ static void add_flag_line(struct libscols_table *table, struct wdinfo *wd, const
}
}
-static int show_flags(struct wdinfo *wd, uint32_t wanted)
+static int show_flags(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
{
size_t i;
int rc = -1;
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 */
@@ -260,8 +300,8 @@ static int show_flags(struct wdinfo *wd, uint32_t wanted)
warn(_("failed to allocate output table"));
return -1;
}
- scols_table_enable_raw(table, raw);
- scols_table_enable_noheadings(table, no_headings);
+ scols_table_enable_raw(table, ctl->show_raw);
+ scols_table_enable_noheadings(table, ctl->hide_headings);
/* define columns */
for (i = 0; i < (size_t) ncolumns; i++) {
@@ -287,7 +327,7 @@ static int show_flags(struct wdinfo *wd, uint32_t wanted)
}
if (flags)
- warnx(_("%s: unknown flags 0x%x\n"), wd->device, flags);
+ warnx(_("%s: unknown flags 0x%x\n"), wd->devpath, flags);
scols_print_table(table);
rc = 0;
@@ -301,25 +341,25 @@ done:
*
* Don't use err() or exit() here!
*/
-static int set_watchdog(struct wdinfo *wd, int timeout)
+static int set_watchdog(struct wd_device *wd, int timeout)
{
int fd;
sigset_t sigs, oldsigs;
int rc = 0;
- assert(wd->device);
+ assert(wd->devpath);
sigemptyset(&oldsigs);
sigfillset(&sigs);
sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
- fd = open(wd->device, O_WRONLY|O_CLOEXEC);
+ fd = open(wd->devpath, O_WRONLY|O_CLOEXEC);
if (fd < 0) {
if (errno == EBUSY)
warnx(_("%s: watchdog already in use, terminating."),
- wd->device);
- warn(_("cannot open %s"), wd->device);
+ wd->devpath);
+ warn(_("cannot open %s"), wd->devpath);
return -1;
}
@@ -331,7 +371,7 @@ static int set_watchdog(struct wdinfo *wd, int timeout)
if (write(fd, &v, 1) >= 0)
break;
if (errno != EINTR) {
- warn(_("%s: failed to disarm watchdog"), wd->device);
+ warn(_("%s: failed to disarm watchdog"), wd->devpath);
break;
}
/* Let's try hard, since if we don't get this right
@@ -340,7 +380,7 @@ static int set_watchdog(struct wdinfo *wd, int timeout)
if (ioctl(fd, WDIOC_SETTIMEOUT, &timeout) != 0) {
rc = errno;
- warn(_("cannot set timeout for %s"), wd->device);
+ warn(_("cannot set timeout for %s"), wd->devpath);
}
if (close(fd))
@@ -358,29 +398,24 @@ static int set_watchdog(struct wdinfo *wd, int timeout)
*
* Don't use err() or exit() here!
*/
-static int read_watchdog(struct wdinfo *wd)
+static int read_watchdog_from_device(struct wd_device *wd)
{
int fd;
sigset_t sigs, oldsigs;
- assert(wd->device);
+ assert(wd->devpath);
sigemptyset(&oldsigs);
sigfillset(&sigs);
sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
- fd = open(wd->device, O_WRONLY|O_CLOEXEC);
+ fd = open(wd->devpath, O_WRONLY|O_CLOEXEC);
- if (fd < 0) {
- if (errno == EBUSY)
- warnx(_("%s: watchdog already in use, terminating."),
- wd->device);
- warn(_("cannot open %s"), wd->device);
- return -1;
- }
+ if (fd < 0)
+ return -errno;
if (ioctl(fd, WDIOC_GETSUPPORT, &wd->ident) < 0)
- warn(_("%s: failed to get information about watchdog"), wd->device);
+ warn(_("%s: failed to get information about watchdog"), wd->devpath);
else {
ioctl(fd, WDIOC_GETSTATUS, &wd->status);
ioctl(fd, WDIOC_GETBOOTSTATUS, &wd->bstatus);
@@ -401,7 +436,7 @@ static int read_watchdog(struct wdinfo *wd)
if (write(fd, &v, 1) >= 0)
break;
if (errno != EINTR) {
- warn(_("%s: failed to disarm watchdog"), wd->device);
+ warn(_("%s: failed to disarm watchdog"), wd->devpath);
break;
}
/* Let's try hard, since if we don't get this right
@@ -415,18 +450,88 @@ static int read_watchdog(struct wdinfo *wd)
return 0;
}
-static void print_oneline(struct wdinfo *wd, uint32_t wanted,
- int noident, int notimeouts, int noflags)
+/* Returns: <0 error, 0 success, 1 unssuported */
+static int read_watchdog_from_sysfs(struct wd_device *wd)
{
- printf("%s:", wd->device);
+ struct path_cxt *sys;
+ struct stat st;
+ int rc;
- if (!noident) {
+ 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)
+ printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->timeout),
+ _("Timeout:"), wd->timeout);
+ if (wd->has_pretimeout)
+ printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->pretimeout),
+ _("Pre-timeout:"), wd->pretimeout);
+ if (wd->has_timeleft)
+ printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->timeleft),
+ _("Timeleft:"), wd->timeleft);
+}
+
+static void print_oneline(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
+{
+ printf("%s:", wd->devpath);
+
+ if (!ctl->hide_ident) {
printf(" VERSION=\"%x\"", wd->ident.firmware_version);
printf(" IDENTITY=");
fputs_quoted((char *) wd->ident.identity, stdout);
}
- if (!notimeouts) {
+ if (!ctl->hide_timeouts) {
if (wd->has_timeout)
printf(" TIMEOUT=\"%i\"", wd->timeout);
if (wd->has_pretimeout)
@@ -435,7 +540,7 @@ static void print_oneline(struct wdinfo *wd, uint32_t wanted,
printf(" TIMELEFT=\"%i\"", wd->timeleft);
}
- if (!noflags) {
+ if (!ctl->hide_flags) {
size_t i;
uint32_t flags = wd->ident.options;
@@ -459,26 +564,38 @@ static void print_oneline(struct wdinfo *wd, uint32_t wanted,
fputc('\n', stdout);
}
-static void show_timeouts(struct wdinfo *wd)
+static void print_device(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
{
- if (wd->has_timeout)
- printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->timeout),
- _("Timeout:"), wd->timeout);
- if (wd->has_pretimeout)
- printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->pretimeout),
- _("Pre-timeout:"), wd->pretimeout);
- if (wd->has_timeleft)
- printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->timeleft),
- _("Timeleft:"), wd->timeleft);
+ /* NAME=value one line output */
+ if (ctl->show_oneline) {
+ print_oneline(ctl, wd, wanted);
+ return;
+ }
+
+ /* pretty output */
+ if (!ctl->hide_ident) {
+ printf("%-15s%s\n", _("Device:"), wd->devpath);
+ printf("%-15s%s [%s %x]\n",
+ _("Identity:"),
+ wd->ident.identity,
+ _("version"),
+ wd->ident.firmware_version);
+ }
+ if (!ctl->hide_timeouts)
+ show_timeouts(wd);
+
+ if (!ctl->hide_flags)
+ show_flags(ctl, wd, wanted);
}
int main(int argc, char *argv[])
{
- struct wdinfo wd;
+ struct wd_device wd;
+ struct wd_control ctl = { .hide_headings = 0 };
int c, res = EXIT_SUCCESS, count = 0;
- char noflags = 0, noident = 0, notimeouts = 0, oneline = 0;
uint32_t wanted = 0;
int timeout = 0;
+ const char *dflt_device = NULL;
static const struct option long_opts[] = {
{ "flags", required_argument, NULL, 'f' },
@@ -528,26 +645,26 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
break;
case 'F':
- noflags = 1;
+ ctl.hide_flags = 1;
break;
case 'I':
- noident = 1;
+ ctl.hide_ident = 1;
break;
case 'T':
- notimeouts = 1;
+ ctl.hide_timeouts = 1;
break;
case 'n':
- no_headings = 1;
+ ctl.hide_headings = 1;
break;
case 'r':
- raw = 1;
+ ctl.show_raw = 1;
break;
case 'O':
- oneline = 1;
+ ctl.show_oneline = 1;
break;
case 'x':
- noident = 1;
- notimeouts = 1;
+ ctl.hide_ident = 1;
+ ctl.hide_timeouts = 1;
break;
case 'h':
@@ -567,15 +684,18 @@ int main(int argc, char *argv[])
columns[ncolumns++] = COL_BSTATUS;
}
+ /* Device no specified, use default. */
+ if (optind == argc) {
+ dflt_device = get_default_device();
+ if (!dflt_device)
+ err(EXIT_FAILURE, _("No default device is available."));
+ }
+
do {
int rc;
memset(&wd, 0, sizeof(wd));
-
- if (optind == argc)
- wd.device = _PATH_WATCHDOG_DEV;
- else
- wd.device = argv[optind++];
+ wd.devpath = dflt_device ? dflt_device : argv[optind++];
if (count)
fputc('\n', stdout);
@@ -594,24 +714,8 @@ int main(int argc, char *argv[])
continue;
}
- if (oneline) {
- print_oneline(&wd, wanted, noident, notimeouts, noflags);
- continue;
- }
+ print_device(&ctl, &wd, wanted);
- /* pretty output */
- if (!noident) {
- printf("%-15s%s\n", _("Device:"), wd.device);
- printf("%-15s%s [%s %x]\n",
- _("Identity:"),
- wd.ident.identity,
- _("version"),
- wd.ident.firmware_version);
- }
- if (!notimeouts)
- show_timeouts(&wd);
- if (!noflags)
- show_flags(&wd, wanted);
} while (optind < argc);
return res;