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 along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include <sys/ioctl.h>
27 #include <linux/watchdog.h>
29 #include <libsmartcols.h>
34 #include "closestream.h"
36 #include "pathnames.h"
38 #include "carefulputc.h"
43 #ifndef WDIOC_SETPRETIMEOUT
44 # define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
45 # define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int)
46 # define WDIOC_GETTIMELEFT _IOR(WATCHDOG_IOCTL_BASE, 10, int)
47 # define WDIOF_POWEROVER 0x0040 /* Power over voltage */
48 # define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */
49 # define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */
50 # define WDIOF_PRETIMEOUT 0x0200 /* Pretimeout (in seconds), get/set */
51 # define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */
57 #ifndef WDIOF_ALARMONLY
58 # define WDIOF_ALARMONLY 0x0400 /* Watchdog triggers a management or
59 other external alarm not a reboot */
65 const char *description
;
68 static const struct wdflag wdflags
[] = {
69 { WDIOF_CARDRESET
, "CARDRESET", N_("Card previously reset the CPU") },
70 { WDIOF_EXTERN1
, "EXTERN1", N_("External relay 1") },
71 { WDIOF_EXTERN2
, "EXTERN2", N_("External relay 2") },
72 { WDIOF_FANFAULT
, "FANFAULT", N_("Fan failed") },
73 { WDIOF_KEEPALIVEPING
, "KEEPALIVEPING", N_("Keep alive ping reply") },
74 { WDIOF_MAGICCLOSE
, "MAGICCLOSE", N_("Supports magic close char") },
75 { WDIOF_OVERHEAT
, "OVERHEAT", N_("Reset due to CPU overheat") },
76 { WDIOF_POWEROVER
, "POWEROVER", N_("Power over voltage") },
77 { WDIOF_POWERUNDER
, "POWERUNDER", N_("Power bad/power fault") },
78 { WDIOF_PRETIMEOUT
, "PRETIMEOUT", N_("Pretimeout (in seconds)") },
79 { WDIOF_SETTIMEOUT
, "SETTIMEOUT", N_("Set timeout (in seconds)") },
80 { WDIOF_ALARMONLY
, "ALARMONLY", N_("Not trigger reboot") }
86 const char *name
; /* header */
87 double whint
; /* width hint (N < 1 is in percent of termwidth) */
88 int flags
; /* SCOLS_FL_* */
92 enum { COL_FLAG
, COL_DESC
, COL_STATUS
, COL_BSTATUS
, COL_DEVICE
};
94 /* columns descriptions */
95 static struct colinfo infos
[] = {
96 [COL_FLAG
] = { "FLAG", 14, 0, N_("flag name") },
97 [COL_DESC
] = { "DESCRIPTION", 0.1, SCOLS_FL_TRUNC
, N_("flag description") },
98 [COL_STATUS
] = { "STATUS", 1, SCOLS_FL_RIGHT
, N_("flag status") },
99 [COL_BSTATUS
] = { "BOOT-STATUS", 1, SCOLS_FL_RIGHT
, N_("flag boot status") },
100 [COL_DEVICE
] = { "DEVICE", 0.1, 0, N_("watchdog device name") }
104 static int columns
[ARRAY_SIZE(infos
) * 2];
117 struct watchdog_info ident
;
119 unsigned int has_timeout
: 1,
125 unsigned int show_oneline
: 1,
133 /* converts flag name to flag bit */
134 static long name2bit(const char *name
, size_t namesz
)
138 for (i
= 0; i
< ARRAY_SIZE(wdflags
); i
++) {
139 const char *cn
= wdflags
[i
].name
;
140 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
141 return wdflags
[i
].flag
;
143 warnx(_("unknown flag: %s"), name
);
147 static int column2id(const char *name
, size_t namesz
)
151 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
152 const char *cn
= infos
[i
].name
;
153 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
156 warnx(_("unknown column: %s"), name
);
160 static int get_column_id(int num
)
162 assert(num
< ncolumns
);
163 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
168 static struct colinfo
*get_column_info(unsigned num
)
170 return &infos
[ get_column_id(num
) ];
173 static void __attribute__((__noreturn__
)) usage(void)
178 fputs(USAGE_HEADER
, out
);
180 _(" %s [options] [<device> ...]\n"), program_invocation_short_name
);
182 fputs(USAGE_SEPARATOR
, out
);
183 fputs(_("Show the status of the hardware watchdog.\n"), out
);
185 fputs(USAGE_OPTIONS
, out
);
186 fputs(_(" -f, --flags <list> print selected flags only\n"
187 " -F, --noflags don't print information about flags\n"
188 " -I, --noident don't print watchdog identity information\n"
189 " -n, --noheadings don't print headings for flags table\n"
190 " -O, --oneline print all information on one line\n"
191 " -o, --output <list> output columns of the flags\n"
192 " -r, --raw use raw output format for flags table\n"
193 " -T, --notimeouts don't print watchdog timeouts\n"
194 " -s, --settimeout <sec> set watchdog timeout\n"
195 " -x, --flags-only print only flags table (same as -I -T)\n"), out
);
197 fputs(USAGE_SEPARATOR
, out
);
198 printf(USAGE_HELP_OPTIONS(24));
199 fputs(USAGE_SEPARATOR
, out
);
201 fprintf(out
, _("The default device is %s.\n"), _PATH_WATCHDOG_DEV
);
203 fputs(USAGE_COLUMNS
, out
);
204 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
205 fprintf(out
, " %13s %s\n", infos
[i
].name
, _(infos
[i
].help
));
207 printf(USAGE_MAN_TAIL("wdctl(8)"));
212 static void add_flag_line(struct libscols_table
*table
, struct wd_device
*wd
, const struct wdflag
*fl
)
215 struct libscols_line
*line
;
217 line
= scols_table_new_line(table
, NULL
);
219 warn(_("failed to allocate output line"));
223 for (i
= 0; i
< ncolumns
; i
++) {
224 const char *str
= NULL
;
226 switch (get_column_id(i
)) {
231 str
= fl
->description
;
234 str
= wd
->status
& fl
->flag
? "1" : "0";
237 str
= wd
->bstatus
& fl
->flag
? "1" : "0";
246 if (str
&& scols_line_set_data(line
, i
, str
)) {
247 warn(_("failed to add output data"));
253 static int show_flags(struct wd_control
*ctl
, struct wd_device
*wd
, uint32_t wanted
)
257 struct libscols_table
*table
;
262 /* create output table */
263 table
= scols_new_table();
265 warn(_("failed to allocate output table"));
268 scols_table_enable_raw(table
, ctl
->show_raw
);
269 scols_table_enable_noheadings(table
, ctl
->hide_headings
);
272 for (i
= 0; i
< (size_t) ncolumns
; i
++) {
273 struct colinfo
*col
= get_column_info(i
);
275 if (!scols_table_new_column(table
, col
->name
, col
->whint
, col
->flags
)) {
276 warnx(_("failed to allocate output column"));
281 /* fill-in table with data
282 * -- one line for each supported flag (option) */
283 flags
= wd
->ident
.options
;
285 for (i
= 0; i
< ARRAY_SIZE(wdflags
); i
++) {
286 if (wanted
&& !(wanted
& wdflags
[i
].flag
))
288 else if (flags
& wdflags
[i
].flag
)
289 add_flag_line(table
, wd
, &wdflags
[i
]);
291 flags
&= ~wdflags
[i
].flag
;
295 warnx(_("%s: unknown flags 0x%x\n"), wd
->devpath
, flags
);
297 scols_print_table(table
);
300 scols_unref_table(table
);
304 * Warning: successfully opened watchdog has to be properly closed with magic
305 * close character otherwise the machine will be rebooted!
307 * Don't use err() or exit() here!
309 static int set_watchdog(struct wd_device
*wd
, int timeout
)
312 sigset_t sigs
, oldsigs
;
317 sigemptyset(&oldsigs
);
319 sigprocmask(SIG_BLOCK
, &sigs
, &oldsigs
);
321 fd
= open(wd
->devpath
, O_WRONLY
|O_CLOEXEC
);
325 warnx(_("%s: watchdog already in use, terminating."),
327 warn(_("cannot open %s"), wd
->devpath
);
332 /* We just opened this to query the state, not to arm
333 * it hence use the magic close character */
334 static const char v
= 'V';
336 if (write(fd
, &v
, 1) >= 0)
338 if (errno
!= EINTR
) {
339 warn(_("%s: failed to disarm watchdog"), wd
->devpath
);
342 /* Let's try hard, since if we don't get this right
343 * the machine might end up rebooting. */
346 if (ioctl(fd
, WDIOC_SETTIMEOUT
, &timeout
) != 0) {
348 warn(_("cannot set timeout for %s"), wd
->devpath
);
352 warn(_("write failed"));
353 sigprocmask(SIG_SETMASK
, &oldsigs
, NULL
);
354 printf(P_("Timeout has been set to %d second.\n",
355 "Timeout has been set to %d seconds.\n", timeout
), timeout
);
361 * Warning: successfully opened watchdog has to be properly closed with magic
362 * close character otherwise the machine will be rebooted!
364 * Don't use err() or exit() here!
366 static int read_watchdog(struct wd_device
*wd
)
369 sigset_t sigs
, oldsigs
;
373 sigemptyset(&oldsigs
);
375 sigprocmask(SIG_BLOCK
, &sigs
, &oldsigs
);
377 fd
= open(wd
->devpath
, O_WRONLY
|O_CLOEXEC
);
381 warnx(_("%s: watchdog already in use, terminating."),
383 warn(_("cannot open %s"), wd
->devpath
);
387 if (ioctl(fd
, WDIOC_GETSUPPORT
, &wd
->ident
) < 0)
388 warn(_("%s: failed to get information about watchdog"), wd
->devpath
);
390 ioctl(fd
, WDIOC_GETSTATUS
, &wd
->status
);
391 ioctl(fd
, WDIOC_GETBOOTSTATUS
, &wd
->bstatus
);
393 if (ioctl(fd
, WDIOC_GETTIMEOUT
, &wd
->timeout
) >= 0)
395 if (ioctl(fd
, WDIOC_GETPRETIMEOUT
, &wd
->pretimeout
) >= 0)
396 wd
->has_pretimeout
= 1;
397 if (ioctl(fd
, WDIOC_GETTIMELEFT
, &wd
->timeleft
) >= 0)
398 wd
->has_timeleft
= 1;
402 /* We just opened this to query the state, not to arm
403 * it hence use the magic close character */
404 static const char v
= 'V';
406 if (write(fd
, &v
, 1) >= 0)
408 if (errno
!= EINTR
) {
409 warn(_("%s: failed to disarm watchdog"), wd
->devpath
);
412 /* Let's try hard, since if we don't get this right
413 * the machine might end up rebooting. */
417 warn(_("write failed"));
418 sigprocmask(SIG_SETMASK
, &oldsigs
, NULL
);
423 static void show_timeouts(struct wd_device
*wd
)
426 printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd
->timeout
),
427 _("Timeout:"), wd
->timeout
);
428 if (wd
->has_pretimeout
)
429 printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd
->pretimeout
),
430 _("Pre-timeout:"), wd
->pretimeout
);
431 if (wd
->has_timeleft
)
432 printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd
->timeleft
),
433 _("Timeleft:"), wd
->timeleft
);
436 static void print_oneline(struct wd_control
*ctl
, struct wd_device
*wd
, uint32_t wanted
)
438 printf("%s:", wd
->devpath
);
440 if (!ctl
->hide_ident
) {
441 printf(" VERSION=\"%x\"", wd
->ident
.firmware_version
);
443 printf(" IDENTITY=");
444 fputs_quoted((char *) wd
->ident
.identity
, stdout
);
446 if (!ctl
->hide_timeouts
) {
448 printf(" TIMEOUT=\"%i\"", wd
->timeout
);
449 if (wd
->has_pretimeout
)
450 printf(" PRETIMEOUT=\"%i\"", wd
->pretimeout
);
451 if (wd
->has_timeleft
)
452 printf(" TIMELEFT=\"%i\"", wd
->timeleft
);
455 if (!ctl
->hide_flags
) {
457 uint32_t flags
= wd
->ident
.options
;
459 for (i
= 0; i
< ARRAY_SIZE(wdflags
); i
++) {
460 const struct wdflag
*fl
;
462 if ((wanted
&& !(wanted
& wdflags
[i
].flag
)) ||
463 !(flags
& wdflags
[i
].flag
))
468 printf(" %s=\"%s\"", fl
->name
,
469 wd
->status
& fl
->flag
? "1" : "0");
470 printf(" %s_BOOT=\"%s\"", fl
->name
,
471 wd
->bstatus
& fl
->flag
? "1" : "0");
479 static void print_device(struct wd_control
*ctl
, struct wd_device
*wd
, uint32_t wanted
)
481 /* NAME=value one line output */
482 if (ctl
->show_oneline
) {
483 print_oneline(ctl
, wd
, wanted
);
488 if (!ctl
->hide_ident
) {
489 printf("%-15s%s\n", _("Device:"), wd
->devpath
);
490 printf("%-15s%s [%s %x]\n",
494 wd
->ident
.firmware_version
);
496 if (!ctl
->hide_timeouts
)
499 if (!ctl
->hide_flags
)
500 show_flags(ctl
, wd
, wanted
);
503 int main(int argc
, char *argv
[])
506 struct wd_control ctl
= { .hide_headings
= 0 };
507 int c
, res
= EXIT_SUCCESS
, count
= 0;
511 static const struct option long_opts
[] = {
512 { "flags", required_argument
, NULL
, 'f' },
513 { "flags-only", no_argument
, NULL
, 'x' },
514 { "help", no_argument
, NULL
, 'h' },
515 { "noflags", no_argument
, NULL
, 'F' },
516 { "noheadings", no_argument
, NULL
, 'n' },
517 { "noident", no_argument
, NULL
, 'I' },
518 { "notimeouts", no_argument
, NULL
, 'T' },
519 { "settimeout", required_argument
, NULL
, 's' },
520 { "output", required_argument
, NULL
, 'o' },
521 { "oneline", no_argument
, NULL
, 'O' },
522 { "raw", no_argument
, NULL
, 'r' },
523 { "version", no_argument
, NULL
, 'V' },
527 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
528 { 'F','f' }, /* noflags,flags*/
531 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
533 setlocale(LC_ALL
, "");
534 bindtextdomain(PACKAGE
, LOCALEDIR
);
536 close_stdout_atexit();
538 while ((c
= getopt_long(argc
, argv
,
539 "d:f:hFnITo:s:OrVx", long_opts
, NULL
)) != -1) {
541 err_exclusive_options(c
, long_opts
, excl
, excl_st
);
545 ncolumns
= string_to_idarray(optarg
,
546 columns
, ARRAY_SIZE(columns
),
552 timeout
= strtos32_or_err(optarg
, _("invalid timeout argument"));
555 if (string_to_bitmask(optarg
, (unsigned long *) &wanted
, name2bit
) != 0)
565 ctl
.hide_timeouts
= 1;
568 ctl
.hide_headings
= 1;
574 ctl
.show_oneline
= 1;
578 ctl
.hide_timeouts
= 1;
584 print_version(EXIT_SUCCESS
);
586 errtryhelp(EXIT_FAILURE
);
591 /* default columns */
592 columns
[ncolumns
++] = COL_FLAG
;
593 columns
[ncolumns
++] = COL_DESC
;
594 columns
[ncolumns
++] = COL_STATUS
;
595 columns
[ncolumns
++] = COL_BSTATUS
;
601 memset(&wd
, 0, sizeof(wd
));
604 wd
.devpath
= _PATH_WATCHDOG_DEV
;
606 wd
.devpath
= argv
[optind
++];
613 rc
= set_watchdog(&wd
, timeout
);
619 rc
= read_watchdog(&wd
);
625 print_device(&ctl
, &wd
, wanted
);
627 } while (optind
< argc
);