2 * wdctl(8) - show hardware watchdog status
4 * Copyright (C) 2012 Lennart Poettering
5 * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://gnu.org/licenses/>.
20 #include <sys/ioctl.h>
25 #include <linux/watchdog.h>
26 #include <sys/types.h>
30 #include <libsmartcols.h>
36 #include "closestream.h"
38 #include "pathnames.h"
40 #include "carefulputc.h"
47 #ifndef WDIOC_SETPRETIMEOUT
48 # define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
49 # define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int)
50 # define WDIOC_GETTIMELEFT _IOR(WATCHDOG_IOCTL_BASE, 10, int)
51 # define WDIOF_POWEROVER 0x0040 /* Power over voltage */
52 # define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */
53 # define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */
54 # define WDIOF_PRETIMEOUT 0x0200 /* Pretimeout (in seconds), get/set */
55 # define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */
61 #ifndef WDIOF_ALARMONLY
62 # define WDIOF_ALARMONLY 0x0400 /* Watchdog triggers a management or
63 other external alarm not a reboot */
69 const char *description
;
72 static const struct wdflag wdflags
[] = {
73 { WDIOF_CARDRESET
, "CARDRESET", N_("Card previously reset the CPU") },
74 { WDIOF_EXTERN1
, "EXTERN1", N_("External relay 1") },
75 { WDIOF_EXTERN2
, "EXTERN2", N_("External relay 2") },
76 { WDIOF_FANFAULT
, "FANFAULT", N_("Fan failed") },
77 { WDIOF_KEEPALIVEPING
, "KEEPALIVEPING", N_("Keep alive ping reply") },
78 { WDIOF_MAGICCLOSE
, "MAGICCLOSE", N_("Supports magic close char") },
79 { WDIOF_OVERHEAT
, "OVERHEAT", N_("Reset due to CPU overheat") },
80 { WDIOF_POWEROVER
, "POWEROVER", N_("Power over voltage") },
81 { WDIOF_POWERUNDER
, "POWERUNDER", N_("Power bad/power fault") },
82 { WDIOF_PRETIMEOUT
, "PRETIMEOUT", N_("Pretimeout (in seconds)") },
83 { WDIOF_SETTIMEOUT
, "SETTIMEOUT", N_("Set timeout (in seconds)") },
84 { WDIOF_ALARMONLY
, "ALARMONLY", N_("Not trigger reboot") }
90 const char * const name
; /* header */
91 double whint
; /* width hint (N < 1 is in percent of termwidth) */
92 int flags
; /* SCOLS_FL_* */
96 enum { COL_FLAG
, COL_DESC
, COL_STATUS
, COL_BSTATUS
, COL_DEVICE
};
98 /* columns descriptions */
99 static const struct colinfo infos
[] = {
100 [COL_FLAG
] = { "FLAG", 14, 0, N_("flag name") },
101 [COL_DESC
] = { "DESCRIPTION", 0.1, SCOLS_FL_TRUNC
, N_("flag description") },
102 [COL_STATUS
] = { "STATUS", 1, SCOLS_FL_RIGHT
, N_("flag status") },
103 [COL_BSTATUS
] = { "BOOT-STATUS", 1, SCOLS_FL_RIGHT
, N_("flag boot status") },
104 [COL_DEVICE
] = { "DEVICE", 0.1, 0, N_("watchdog device name") }
108 static int columns
[ARRAY_SIZE(infos
) * 2];
113 struct path_cxt
*sysfs
;
116 char **available_governors
;
126 struct watchdog_info ident
;
128 unsigned int has_identity
: 1,
142 int timeout
; /* --settimeout */
143 int pretimeout
; /* --setpretimeout */
144 const char *governor
; /* --setpregovernor */
145 unsigned int set_timeout
: 1,
149 unsigned int show_oneline
: 1,
157 #define want_set(_ctl) ((_ctl)->set_timeout \
158 || (_ctl)->set_pretimeout \
161 /* converts flag name to flag bit */
162 static long name2bit(const char *name
, size_t namesz
)
166 for (i
= 0; i
< ARRAY_SIZE(wdflags
); i
++) {
167 const char *cn
= wdflags
[i
].name
;
168 if (!c_strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
169 return wdflags
[i
].flag
;
171 warnx(_("unknown flag: %s"), name
);
175 static int column2id(const char *name
, size_t namesz
)
179 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
180 const char *cn
= infos
[i
].name
;
181 if (!c_strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
184 warnx(_("unknown column: %s"), name
);
188 static int get_column_id(int num
)
190 assert(num
< ncolumns
);
191 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
196 static const struct colinfo
*get_column_info(unsigned num
)
198 return &infos
[ get_column_id(num
) ];
201 /* We prefer cdev /dev/watchdog0 as this device has node in
202 * /sys/class/watchdog/. The old miscdev /dev/watchdog is fallback for old
205 static const char *get_default_device(void)
208 static const char *const devs
[] = {
214 for (p
= devs
; *p
; p
++) {
215 if (access(*p
, F_OK
) == 0)
222 static void __attribute__((__noreturn__
)) usage(void)
226 const char *dflt
= get_default_device();
228 fputs(USAGE_HEADER
, out
);
230 _(" %s [options] [<device> ...]\n"), program_invocation_short_name
);
232 fputs(USAGE_SEPARATOR
, out
);
233 fputs(_("Show the status of the hardware watchdog.\n"), out
);
235 fputs(USAGE_OPTIONS
, out
);
236 fputs(_(" -f, --flags <list> print selected flags only\n"
237 " -F, --noflags don't print information about flags\n"
238 " -I, --noident don't print watchdog identity information\n"
239 " -n, --noheadings don't print headings for flags table\n"
240 " -O, --oneline print all information on one line\n"
241 " -o, --output <list> output columns of the flags\n"
242 " -p, --setpretimeout <sec> set watchdog pre-timeout\n"
243 " -g, --setpregovernor <name> set pre-timeout governor\n"
244 " -r, --raw use raw output format for flags table\n"
245 " -T, --notimeouts don't print watchdog timeouts\n"
246 " -s, --settimeout <sec> set watchdog timeout\n"
247 " -x, --flags-only print only flags table (same as -I -T)\n"), out
);
249 fputs(USAGE_SEPARATOR
, out
);
250 fprintf(out
, USAGE_HELP_OPTIONS(24));
251 fputs(USAGE_SEPARATOR
, out
);
254 fprintf(out
, _("The default device is %s.\n"), dflt
);
256 fprintf(out
, _("No default device is available.\n"));
258 fputs(USAGE_COLUMNS
, out
);
259 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
260 fprintf(out
, " %13s %s\n", infos
[i
].name
, _(infos
[i
].help
));
262 fprintf(out
, USAGE_MAN_TAIL("wdctl(8)"));
267 static struct path_cxt
*get_sysfs(struct wd_device
*wd
)
269 struct path_cxt
*sys
;
276 if (stat(wd
->devpath
, &st
) != 0)
279 sys
= ul_new_path(_PATH_SYS_DEVCHAR
"/%u:%u",
280 major(st
.st_rdev
), minor(st
.st_rdev
));
284 if (ul_path_get_dirfd(sys
) < 0)
285 goto nosysfs
; /* device not in /sys */
287 if (ul_path_access(sys
, F_OK
, "identity") != 0)
288 goto nosysfs
; /* no info in /sys (old miscdev?) */
297 static void add_flag_line(struct libscols_table
*table
, struct wd_device
*wd
, const struct wdflag
*fl
)
300 struct libscols_line
*line
;
302 line
= scols_table_new_line(table
, NULL
);
304 warn(_("failed to allocate output line"));
308 for (i
= 0; i
< ncolumns
; i
++) {
309 const char *str
= NULL
;
311 switch (get_column_id(i
)) {
316 str
= fl
->description
;
319 str
= wd
->status
& fl
->flag
? "1" : "0";
322 str
= wd
->bstatus
& fl
->flag
? "1" : "0";
331 if (str
&& scols_line_set_data(line
, i
, str
)) {
332 warn(_("failed to add output data"));
338 static int show_flags(struct wd_control
*ctl
, struct wd_device
*wd
, uint32_t wanted
)
342 struct libscols_table
*table
;
345 /* information about supported bits is probably missing in /sys */
346 if (!wd
->ident
.options
)
351 /* create output table */
352 table
= scols_new_table();
354 warn(_("failed to allocate output table"));
357 scols_table_enable_raw(table
, ctl
->show_raw
);
358 scols_table_enable_noheadings(table
, ctl
->hide_headings
);
361 for (i
= 0; i
< (size_t) ncolumns
; i
++) {
362 const struct colinfo
*col
= get_column_info(i
);
364 if (!scols_table_new_column(table
, col
->name
, col
->whint
, col
->flags
)) {
365 warnx(_("failed to allocate output column"));
370 /* fill-in table with data
371 * -- one line for each supported flag (option) */
372 flags
= wd
->ident
.options
;
374 for (i
= 0; i
< ARRAY_SIZE(wdflags
); i
++) {
375 if (wanted
&& !(wanted
& wdflags
[i
].flag
))
377 else if (flags
& wdflags
[i
].flag
)
378 add_flag_line(table
, wd
, &wdflags
[i
]);
380 flags
&= ~wdflags
[i
].flag
;
384 warnx(_("%s: unknown flags 0x%x\n"), wd
->devpath
, flags
);
386 scols_print_table(table
);
389 scols_unref_table(table
);
394 * Warning: successfully opened watchdog has to be properly closed with magic
395 * close character otherwise the machine will be rebooted!
397 * Don't use err() or exit() here!
399 static int set_watchdog(struct wd_control
*ctl
, struct wd_device
*wd
)
402 sigset_t sigs
, oldsigs
;
409 if (!ctl
->set_timeout
&& !ctl
->set_pretimeout
)
412 sigemptyset(&oldsigs
);
414 sigprocmask(SIG_BLOCK
, &sigs
, &oldsigs
);
416 fd
= open(wd
->devpath
, O_WRONLY
|O_CLOEXEC
);
420 warnx(_("%s: watchdog already in use, terminating."),
422 warn(_("cannot open %s"), wd
->devpath
);
427 /* We just opened this to query the state, not to arm
428 * it hence use the magic close character */
429 static const char v
= 'V';
431 if (write(fd
, &v
, 1) >= 0)
433 if (errno
!= EINTR
) {
434 warn(_("%s: failed to disarm watchdog"), wd
->devpath
);
437 /* Let's try hard, since if we don't get this right
438 * the machine might end up rebooting. */
441 if (ctl
->set_timeout
) {
442 if (ioctl(fd
, WDIOC_SETTIMEOUT
, &ctl
->timeout
) != 0) {
444 warn(_("cannot set timeout for %s"), wd
->devpath
);
446 printf(P_("Timeout has been set to %d second.\n",
447 "Timeout has been set to %d seconds.\n",
448 ctl
->timeout
), ctl
->timeout
);
451 if (ctl
->set_pretimeout
) {
452 if (ioctl(fd
, WDIOC_SETPRETIMEOUT
, &ctl
->pretimeout
) != 0) {
454 warn(_("cannot set pretimeout for %s"), wd
->devpath
);
456 printf(P_("Pre-timeout has been set to %d second.\n",
457 "Pre-timeout has been set to %d seconds.\n",
458 ctl
->pretimeout
), ctl
->pretimeout
);
462 warn(_("write failed"));
464 sigprocmask(SIG_SETMASK
, &oldsigs
, NULL
);
468 struct path_cxt
*sys
= get_sysfs(wd
);
472 ul_path_write_string(sys
, ctl
->governor
,
473 "pretimeout_governor");
475 warn(_("cannot set pre-timeout governor"));
483 * Warning: successfully opened watchdog has to be properly closed with magic
484 * close character otherwise the machine will be rebooted!
486 * Don't use err() or exit() here!
488 static int read_watchdog_from_device(struct wd_device
*wd
)
491 sigset_t sigs
, oldsigs
;
495 sigemptyset(&oldsigs
);
497 sigprocmask(SIG_BLOCK
, &sigs
, &oldsigs
);
499 fd
= open(wd
->devpath
, O_WRONLY
|O_CLOEXEC
);
504 if (ioctl(fd
, WDIOC_GETSUPPORT
, &wd
->ident
) < 0)
505 warn(_("%s: failed to get information about watchdog"), wd
->devpath
);
507 ioctl(fd
, WDIOC_GETSTATUS
, &wd
->status
);
508 ioctl(fd
, WDIOC_GETBOOTSTATUS
, &wd
->bstatus
);
511 * Sometimes supported options like WDIOF_CARDRESET are missing from
512 * ident.options, add anything set in status/bstatus to ident.options.
514 wd
->ident
.options
|= wd
->status
;
515 wd
->ident
.options
|= wd
->bstatus
;
517 if (ioctl(fd
, WDIOC_GETTIMEOUT
, &wd
->timeout
) >= 0)
519 if (ioctl(fd
, WDIOC_GETPRETIMEOUT
, &wd
->pretimeout
) >= 0)
520 wd
->has_pretimeout
= 1;
521 if (ioctl(fd
, WDIOC_GETTIMELEFT
, &wd
->timeleft
) >= 0)
522 wd
->has_timeleft
= 1;
526 /* We just opened this to query the state, not to arm
527 * it hence use the magic close character */
528 static const char v
= 'V';
530 if (write(fd
, &v
, 1) >= 0)
532 if (errno
!= EINTR
) {
533 warn(_("%s: failed to disarm watchdog"), wd
->devpath
);
536 /* Let's try hard, since if we don't get this right
537 * the machine might end up rebooting. */
541 warn(_("write failed"));
542 sigprocmask(SIG_SETMASK
, &oldsigs
, NULL
);
548 /* Returns: <0 on error, 0 on success, 1 for unsupported */
549 static int read_watchdog_from_sysfs(struct wd_device
*wd
)
551 struct path_cxt
*sys
;
557 if (ul_path_read_buffer(sys
, (char *) wd
->ident
.identity
, sizeof(wd
->ident
.identity
), "identity") >= 0)
558 wd
->has_identity
= 1;
559 if (ul_path_read_u32(sys
, &wd
->ident
.firmware_version
, "fw_version") == 0)
560 wd
->has_fw_version
= 1;
561 if (ul_path_scanf(sys
, "options", "%x", &wd
->ident
.options
) == 1)
563 if (ul_path_scanf(sys
, "status", "%x", &wd
->status
) == 1)
565 if (ul_path_read_u32(sys
, &wd
->bstatus
, "bootstatus") == 0)
566 wd
->has_bootstatus
= 1;
567 if (ul_path_read_s32(sys
, &wd
->nowayout
, "nowayout") == 0)
568 wd
->has_nowayout
= 1;
569 if (ul_path_read_s32(sys
, &wd
->timeout
, "timeout") == 0)
571 if (ul_path_read_s32(sys
, &wd
->pretimeout
, "pretimeout") == 0)
572 wd
->has_pretimeout
= 1;
573 if (ul_path_read_s32(sys
, &wd
->timeleft
, "timeleft") == 0)
574 wd
->has_timeleft
= 1;
579 static int read_governors(struct wd_device
*wd
)
581 struct path_cxt
*sys
;
588 f
= ul_path_fopen(sys
, "r", "pretimeout_available_governors");
594 while ((sz
= getline(&line
, &dummy
, f
)) >= 0) {
595 if (rtrim_whitespace((unsigned char *) line
) == 0)
597 ul_strv_consume(&wd
->available_governors
, line
);
605 ul_path_read_string(sys
, &wd
->governor
, "pretimeout_governor");
609 static bool should_read_from_device(struct wd_device
*wd
)
614 if (!wd
->has_nowayout
)
620 return !wd
->has_identity
||
621 !wd
->has_fw_version
||
624 !wd
->has_bootstatus
||
627 // pretimeout attribute may be hidden in sysfs
630 static int read_watchdog(struct wd_device
*wd
)
634 rc
= read_watchdog_from_sysfs(wd
);
636 if (rc
&& should_read_from_device(wd
))
637 rc
= read_watchdog_from_device(wd
);
640 warn(_("cannot read information about %s"), wd
->devpath
);
648 static void show_timeouts(struct wd_device
*wd
)
651 printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd
->timeout
),
652 _("Timeout:"), wd
->timeout
);
653 if (wd
->has_timeleft
)
654 printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd
->timeleft
),
655 _("Timeleft:"), wd
->timeleft
);
656 if (wd
->has_pretimeout
)
657 printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd
->pretimeout
),
658 _("Pre-timeout:"), wd
->pretimeout
);
661 static void show_governors(struct wd_device
*wd
)
664 printf(_("%-14s %s\n"), _("Pre-timeout governor:"), wd
->governor
);
665 if (wd
->available_governors
) {
666 char *tmp
= ul_strv_join(wd
->available_governors
, " ");
669 printf(_("%-14s %s\n"),
670 _("Available pre-timeout governors:"), tmp
);
675 static void print_oneline(struct wd_control
*ctl
, struct wd_device
*wd
, uint32_t wanted
)
677 printf("%s:", wd
->devpath
);
679 if (!ctl
->hide_ident
) {
680 printf(" VERSION=\"%x\"", wd
->ident
.firmware_version
);
682 printf(" IDENTITY=");
683 fputs_quoted((char *) wd
->ident
.identity
, stdout
);
685 if (!ctl
->hide_timeouts
) {
687 printf(" TIMEOUT=\"%i\"", wd
->timeout
);
688 if (wd
->has_pretimeout
)
689 printf(" PRETIMEOUT=\"%i\"", wd
->pretimeout
);
690 if (wd
->has_timeleft
)
691 printf(" TIMELEFT=\"%i\"", wd
->timeleft
);
694 if (!ctl
->hide_flags
) {
696 uint32_t flags
= wd
->ident
.options
;
698 for (i
= 0; i
< ARRAY_SIZE(wdflags
); i
++) {
699 const struct wdflag
*fl
;
701 if ((wanted
&& !(wanted
& wdflags
[i
].flag
)) ||
702 !(flags
& wdflags
[i
].flag
))
707 printf(" %s=\"%s\"", fl
->name
,
708 wd
->status
& fl
->flag
? "1" : "0");
709 printf(" %s_BOOT=\"%s\"", fl
->name
,
710 wd
->bstatus
& fl
->flag
? "1" : "0");
718 static void print_device(struct wd_control
*ctl
, struct wd_device
*wd
, uint32_t wanted
)
720 /* NAME=value one line output */
721 if (ctl
->show_oneline
) {
722 print_oneline(ctl
, wd
, wanted
);
727 if (!ctl
->hide_ident
) {
728 printf("%-15s%s\n", _("Device:"), wd
->devpath
);
729 printf("%-15s%s [%s %x]\n",
733 wd
->ident
.firmware_version
);
735 if (!ctl
->hide_timeouts
)
740 if (!ctl
->hide_flags
)
741 show_flags(ctl
, wd
, wanted
);
744 int main(int argc
, char *argv
[])
747 struct wd_control ctl
= { .hide_headings
= 0 };
748 int c
, res
= EXIT_SUCCESS
, count
= 0;
749 unsigned long wanted
= 0;
750 const char *dflt_device
= NULL
;
752 static const struct option long_opts
[] = {
753 { "flags", required_argument
, NULL
, 'f' },
754 { "flags-only", no_argument
, NULL
, 'x' },
755 { "help", no_argument
, NULL
, 'h' },
756 { "noflags", no_argument
, NULL
, 'F' },
757 { "noheadings", no_argument
, NULL
, 'n' },
758 { "noident", no_argument
, NULL
, 'I' },
759 { "notimeouts", no_argument
, NULL
, 'T' },
760 { "settimeout", required_argument
, NULL
, 's' },
761 { "setpretimeout", required_argument
, NULL
, 'p' },
762 { "setpregovernor", required_argument
, NULL
, 'g' },
763 { "output", required_argument
, NULL
, 'o' },
764 { "oneline", no_argument
, NULL
, 'O' },
765 { "raw", no_argument
, NULL
, 'r' },
766 { "version", no_argument
, NULL
, 'V' },
770 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
771 { 'F','f' }, /* noflags,flags*/
774 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
776 setlocale(LC_ALL
, "");
777 bindtextdomain(PACKAGE
, LOCALEDIR
);
779 close_stdout_atexit();
781 while ((c
= getopt_long(argc
, argv
,
782 "d:f:g:hFnITp:o:s:OrVx", long_opts
, NULL
)) != -1) {
784 err_exclusive_options(c
, long_opts
, excl
, excl_st
);
788 ncolumns
= string_to_idarray(optarg
,
789 columns
, ARRAY_SIZE(columns
),
795 ctl
.timeout
= strtos32_or_err(optarg
, _("invalid timeout"));
799 ctl
.pretimeout
= strtos32_or_err(optarg
, _("invalid pretimeout"));
800 ctl
.set_pretimeout
= 1;
803 if (string_to_bitmask(optarg
, &wanted
, name2bit
) != 0)
810 ctl
.governor
= optarg
;
816 ctl
.hide_timeouts
= 1;
819 ctl
.hide_headings
= 1;
825 ctl
.show_oneline
= 1;
829 ctl
.hide_timeouts
= 1;
835 print_version(EXIT_SUCCESS
);
837 errtryhelp(EXIT_FAILURE
);
842 /* default columns */
843 columns
[ncolumns
++] = COL_FLAG
;
844 columns
[ncolumns
++] = COL_DESC
;
845 columns
[ncolumns
++] = COL_STATUS
;
846 columns
[ncolumns
++] = COL_BSTATUS
;
849 /* Device no specified, use default. */
850 if (optind
== argc
) {
851 dflt_device
= get_default_device();
853 err(EXIT_FAILURE
, _("No default device is available."));
859 memset(&wd
, 0, sizeof(wd
));
860 wd
.devpath
= dflt_device
? dflt_device
: argv
[optind
++];
866 if (want_set(&ctl
)) {
867 rc
= set_watchdog(&ctl
, &wd
);
873 rc
= read_watchdog(&wd
);
879 print_device(&ctl
, &wd
, wanted
);
880 ul_unref_path(wd
.sysfs
);
881 } while (optind
< argc
);