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>
31 #include "closestream.h"
33 #include "pathnames.h"
40 const char *description
;
43 static const struct wdflag wdflags
[] = {
44 { WDIOF_CARDRESET
, "CARDRESET", N_("Card previously reset the CPU") },
45 { WDIOF_EXTERN1
, "EXTERN1", N_("External relay 1") },
46 { WDIOF_EXTERN2
, "EXTERN2", N_("External relay 2") },
47 { WDIOF_FANFAULT
, "FANFAULT", N_("Fan failed") },
48 { WDIOF_KEEPALIVEPING
, "KEEPALIVEPING", N_("Keep alive ping reply") },
49 { WDIOF_MAGICCLOSE
, "MAGICCLOSE", N_("Supports magic close char") },
50 { WDIOF_OVERHEAT
, "OVERHEAT", N_("Reset due to CPU overheat") },
51 { WDIOF_POWEROVER
, "POWEROVER", N_("Power over voltage") },
52 { WDIOF_POWERUNDER
, "POWERUNDER", N_("Power bad/power fault") },
53 { WDIOF_PRETIMEOUT
, "PRETIMEOUT", N_("Pretimeout (in seconds)") },
54 { WDIOF_SETTIMEOUT
, "SETTIMEOUT", N_("Set timeout (in seconds)") }
60 const char *name
; /* header */
61 double whint
; /* width hint (N < 1 is in percent of termwidth) */
62 int flags
; /* TT_FL_* */
66 enum { COL_FLAG
, COL_DESC
, COL_STATUS
, COL_BSTATUS
, COL_DEVICE
};
68 /* columns descriptions */
69 static struct colinfo infos
[] = {
70 [COL_FLAG
] = { "FLAG", 14, 0, N_("flag name") },
71 [COL_DESC
] = { "DESCRIPTION", 0.1, TT_FL_TRUNC
, N_("flag description") },
72 [COL_STATUS
] = { "STATUS", 1, TT_FL_RIGHT
, N_("flag status") },
73 [COL_BSTATUS
] = { "BOOT-STATUS", 1, TT_FL_RIGHT
, N_("flag boot status") },
74 [COL_DEVICE
] = { "DEVICE", 0.1, 0, N_("watchdog device name") }
78 #define NCOLS ARRAY_SIZE(infos)
79 static int columns
[NCOLS
], ncolumns
;
91 struct watchdog_info ident
;
93 unsigned int has_timeout
: 1,
98 /* converts flag name to flag bit */
99 static long name2bit(const char *name
, size_t namesz
)
103 for (i
= 0; i
< ARRAY_SIZE(wdflags
); i
++) {
104 const char *cn
= wdflags
[i
].name
;
105 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
106 return wdflags
[i
].flag
;
108 warnx(_("unknown flag: %s"), name
);
112 static int column2id(const char *name
, size_t namesz
)
116 for (i
= 0; i
< NCOLS
; i
++) {
117 const char *cn
= infos
[i
].name
;
118 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
121 warnx(_("unknown column: %s"), name
);
125 static int get_column_id(int num
)
127 assert(ARRAY_SIZE(columns
) == NCOLS
);
128 assert(num
< ncolumns
);
129 assert(columns
[num
] < (int) NCOLS
);
134 static struct colinfo
*get_column_info(unsigned num
)
136 return &infos
[ get_column_id(num
) ];
139 static void usage(FILE *out
)
143 fputs(USAGE_HEADER
, out
);
145 _(" %s [options] [<device> ...]\n"), program_invocation_short_name
);
147 fputs(USAGE_OPTIONS
, out
);
149 fputs(_(" -f, --flags <list> print selected flags only\n"
150 " -F, --noflags don't print information about flags\n"
151 " -I, --noident don't print watchdog identity information\n"
152 " -n, --noheadings don't print headings for flags table\n"
153 " -O, --oneline print all information on one line\n"
154 " -o, --output <list> output columns of the flags\n"
155 " -r, --raw use raw output format for flags table\n"
156 " -T, --notimeouts don't print watchdog timeouts\n"
157 " -s, --settimeout <sec> set watchdog timeout\n"
158 " -x, --flags-only print only flags table (same as -I -T)\n"), out
);
160 fputs(USAGE_SEPARATOR
, out
);
161 fputs(USAGE_HELP
, out
);
162 fputs(USAGE_VERSION
, out
);
163 fputs(USAGE_SEPARATOR
, out
);
165 fprintf(out
, _("The default device is %s.\n"), _PATH_WATCHDOG_DEV
);
166 fputs(USAGE_SEPARATOR
, out
);
168 fputs(_("Available columns:\n"), out
);
169 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
170 fprintf(out
, " %13s %s\n", infos
[i
].name
, _(infos
[i
].help
));
172 fprintf(out
, USAGE_MAN_TAIL("wdctl(8)"));
174 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);
177 static void add_flag_line(struct tt
*tt
, struct wdinfo
*wd
, const struct wdflag
*fl
)
180 struct tt_line
*line
;
182 line
= tt_add_line(tt
, NULL
);
184 warn(_("failed to add line to output"));
188 for (i
= 0; i
< ncolumns
; i
++) {
189 const char *str
= NULL
;
191 switch (get_column_id(i
)) {
196 str
= fl
->description
;
199 str
= wd
->status
& fl
->flag
? "1" : "0";
202 str
= wd
->bstatus
& fl
->flag
? "1" : "0";
212 tt_line_set_data(line
, i
, str
);
216 static int show_flags(struct wdinfo
*wd
, int tt_flags
, uint32_t wanted
)
223 /* create output table */
224 tt
= tt_new_table(tt_flags
);
226 warn(_("failed to initialize output table"));
231 for (i
= 0; i
< (size_t) ncolumns
; i
++) {
232 struct colinfo
*col
= get_column_info(i
);
234 if (!tt_define_column(tt
, col
->name
, col
->whint
, col
->flags
)) {
235 warnx(_("failed to initialize output column"));
240 /* fill-in table with data
241 * -- one line for each supported flag (option) */
242 flags
= wd
->ident
.options
;
244 for (i
= 0; i
< ARRAY_SIZE(wdflags
); i
++) {
245 if (wanted
&& !(wanted
& wdflags
[i
].flag
))
247 else if (flags
& wdflags
[i
].flag
)
248 add_flag_line(tt
, wd
, &wdflags
[i
]);
250 flags
&= ~wdflags
[i
].flag
;
254 warnx(_("%s: unknown flags 0x%x\n"), wd
->device
, flags
);
263 * Warning: successfully opened watchdog has to be properly closed with magic
264 * close character otherwise the machine will be rebooted!
266 * Don't use err() or exit() here!
268 static int set_watchdog(struct wdinfo
*wd
, int timeout
)
271 sigset_t sigs
, oldsigs
;
276 sigemptyset(&oldsigs
);
278 sigprocmask(SIG_BLOCK
, &sigs
, &oldsigs
);
280 fd
= open(wd
->device
, O_WRONLY
|O_CLOEXEC
);
284 warnx(_("%s: watchdog already in use, terminating."),
286 warn(_("cannot open %s"), wd
->device
);
291 /* We just opened this to query the state, not to arm
292 * it hence use the magic close character */
293 static const char v
= 'V';
295 if (write(fd
, &v
, 1) >= 0)
297 if (errno
!= EINTR
) {
298 warn(_("%s: failed to disarm watchdog"), wd
->device
);
301 /* Let's try hard, since if we don't get this right
302 * the machine might end up rebooting. */
305 if (ioctl(fd
, WDIOC_SETTIMEOUT
, &timeout
) != 0) {
307 warn(_("cannot set timeout for %s"), wd
->device
);
311 sigprocmask(SIG_SETMASK
, &oldsigs
, NULL
);
312 printf("Set timeout to %d seconds\n", timeout
);
318 * Warning: successfully opened watchdog has to be properly closed with magic
319 * close character otherwise the machine will be rebooted!
321 * Don't use err() or exit() here!
323 static int read_watchdog(struct wdinfo
*wd
)
326 sigset_t sigs
, oldsigs
;
330 sigemptyset(&oldsigs
);
332 sigprocmask(SIG_BLOCK
, &sigs
, &oldsigs
);
334 fd
= open(wd
->device
, O_WRONLY
|O_CLOEXEC
);
338 warnx(_("%s: watchdog already in use, terminating."),
340 warn(_("cannot open %s"), wd
->device
);
344 if (ioctl(fd
, WDIOC_GETSUPPORT
, &wd
->ident
) < 0)
345 warn(_("%s: failed to get information about watchdog"), wd
->device
);
347 ioctl(fd
, WDIOC_GETSTATUS
, &wd
->status
);
348 ioctl(fd
, WDIOC_GETBOOTSTATUS
, &wd
->bstatus
);
350 if (ioctl(fd
, WDIOC_GETTIMEOUT
, &wd
->timeout
) >= 0)
352 if (ioctl(fd
, WDIOC_GETPRETIMEOUT
, &wd
->pretimeout
) >= 0)
353 wd
->has_pretimeout
= 1;
354 if (ioctl(fd
, WDIOC_GETTIMELEFT
, &wd
->timeleft
) >= 0)
355 wd
->has_timeleft
= 1;
359 /* We just opened this to query the state, not to arm
360 * it hence use the magic close character */
361 static const char v
= 'V';
363 if (write(fd
, &v
, 1) >= 0)
365 if (errno
!= EINTR
) {
366 warn(_("%s: failed to disarm watchdog"), wd
->device
);
369 /* Let's try hard, since if we don't get this right
370 * the machine might end up rebooting. */
374 sigprocmask(SIG_SETMASK
, &oldsigs
, NULL
);
379 static void print_oneline(struct wdinfo
*wd
, uint32_t wanted
,
380 int noident
, int notimeouts
, int noflags
)
382 printf("%s:", wd
->device
);
385 printf(" VERSION=\"%x\"", wd
->ident
.firmware_version
);
387 printf(" IDENTITY=");
388 tt_fputs_quoted((char *) wd
->ident
.identity
, stdout
);
392 printf(" TIMEOUT=\"%i\"", wd
->timeout
);
393 if (wd
->has_pretimeout
)
394 printf(" PRETIMEOUT=\"%i\"", wd
->pretimeout
);
395 if (wd
->has_timeleft
)
396 printf(" TIMELEFT=\"%i\"", wd
->timeleft
);
401 uint32_t flags
= wd
->ident
.options
;
403 for (i
= 0; i
< ARRAY_SIZE(wdflags
); i
++) {
404 const struct wdflag
*fl
;
406 if ((wanted
&& !(wanted
& wdflags
[i
].flag
)) ||
407 !(flags
& wdflags
[i
].flag
))
412 printf(" %s=\"%s\"", fl
->name
,
413 wd
->status
& fl
->flag
? "1" : "0");
414 printf(" %s_BOOT=\"%s\"", fl
->name
,
415 wd
->bstatus
& fl
->flag
? "1" : "0");
423 static void show_timeouts(struct wdinfo
*wd
)
426 printf(_("%-15s%2i seconds\n"), _("Timeout:"), wd
->timeout
);
427 if (wd
->has_pretimeout
)
428 printf(_("%-15s%2i seconds\n"), _("Pre-timeout:"), wd
->pretimeout
);
429 if (wd
->has_timeleft
)
430 printf(_("%-15s%2i seconds\n"), _("Timeleft:"), wd
->timeleft
);
433 int main(int argc
, char *argv
[])
436 int c
, tt_flags
= 0, res
= EXIT_SUCCESS
, count
= 0;
437 char noflags
= 0, noident
= 0, notimeouts
= 0, oneline
= 0;
441 static const struct option long_opts
[] = {
442 { "flags", required_argument
, NULL
, 'f' },
443 { "flags-only", no_argument
, NULL
, 'x' },
444 { "help", no_argument
, NULL
, 'h' },
445 { "noflags", no_argument
, NULL
, 'F' },
446 { "noheadings", no_argument
, NULL
, 'n' },
447 { "noident", no_argument
, NULL
, 'I' },
448 { "notimeouts", no_argument
, NULL
, 'T' },
449 { "settimeout", required_argument
, NULL
, 's' },
450 { "output", required_argument
, NULL
, 'o' },
451 { "oneline", no_argument
, NULL
, 'O' },
452 { "raw", no_argument
, NULL
, 'r' },
453 { "version", no_argument
, NULL
, 'V' },
457 static const ul_excl_t excl
[] = { /* rows and cols in in ASCII order */
458 { 'F','f' }, /* noflags,flags*/
461 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
463 setlocale(LC_ALL
, "");
464 bindtextdomain(PACKAGE
, LOCALEDIR
);
466 atexit(close_stdout
);
468 while ((c
= getopt_long(argc
, argv
,
469 "d:f:hFnITo:s:OrVx", long_opts
, NULL
)) != -1) {
471 err_exclusive_options(c
, long_opts
, excl
, excl_st
);
475 ncolumns
= string_to_idarray(optarg
,
476 columns
, ARRAY_SIZE(columns
),
482 timeout
= strtos32_or_err(optarg
, _("invalid timeout argument"));
485 if (string_to_bitmask(optarg
, (unsigned long *) &wanted
, name2bit
) != 0)
489 printf(UTIL_LINUX_VERSION
);
503 tt_flags
|= TT_FL_NOHEADINGS
;
506 tt_flags
|= TT_FL_RAW
;
523 /* default columns */
524 columns
[ncolumns
++] = COL_FLAG
;
525 columns
[ncolumns
++] = COL_DESC
;
526 columns
[ncolumns
++] = COL_STATUS
;
527 columns
[ncolumns
++] = COL_BSTATUS
;
533 memset(&wd
, 0, sizeof(wd
));
536 wd
.device
= _PATH_WATCHDOG_DEV
;
538 wd
.device
= argv
[optind
++];
545 rc
= set_watchdog(&wd
, timeout
);
551 rc
= read_watchdog(&wd
);
558 print_oneline(&wd
, wanted
, noident
, notimeouts
, noflags
);
564 printf("%-15s%s\n", _("Device:"), wd
.device
);
565 printf(_("%-15s%s [version %x]\n"),
568 wd
.ident
.firmware_version
);
573 show_flags(&wd
, tt_flags
, wanted
);
574 } while (optind
< argc
);