]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/wdctl.c
Merge branch 'PR/libmount-exec-errors' of github.com:karelzak/util-linux-work
[thirdparty/util-linux.git] / sys-utils / wdctl.c
CommitLineData
8fb6a2de
LP
1/*
2 * wdctl(8) - show hardware watchdog status
3 *
4 * Copyright (C) 2012 Lennart Poettering
09f9a393 5 * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
8fb6a2de
LP
6 *
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
10 * later version.
11 *
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.
16 *
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.
20 */
8fb6a2de
LP
21#include <sys/ioctl.h>
22#include <getopt.h>
23#include <stdio.h>
09f9a393
KZ
24#include <signal.h>
25#include <assert.h>
7560aebe 26#include <linux/watchdog.h>
b3dd29d1
KZ
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <unistd.h>
8fb6a2de 30
fe7af530
OO
31#include <libsmartcols.h>
32
8fb6a2de
LP
33#include "nls.h"
34#include "c.h"
2ffddb6a 35#include "xalloc.h"
33a0de92 36#include "closestream.h"
e6dbcc4a 37#include "optutils.h"
33a0de92 38#include "pathnames.h"
09f9a393 39#include "strutils.h"
fe7af530 40#include "carefulputc.h"
b3dd29d1 41#include "path.h"
86ffb5a7 42#include "strv.h"
8fb6a2de 43
2eb5ba0b
KZ
44/*
45 * since 2.6.18
46 */
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 */
56#endif
57
58/*
59 * since 3.5
60 */
61#ifndef WDIOF_ALARMONLY
62# define WDIOF_ALARMONLY 0x0400 /* Watchdog triggers a management or
63 other external alarm not a reboot */
64#endif
65
09f9a393
KZ
66struct wdflag {
67 uint32_t flag;
68 const char *name;
69 const char *description;
8fb6a2de
LP
70};
71
09f9a393
KZ
72static 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)") },
2eb5ba0b
KZ
83 { WDIOF_SETTIMEOUT, "SETTIMEOUT", N_("Set timeout (in seconds)") },
84 { WDIOF_ALARMONLY, "ALARMONLY", N_("Not trigger reboot") }
09f9a393
KZ
85};
86
87
88/* column names */
89struct colinfo {
37b2b3fa
TW
90 const char * const name; /* header */
91 double whint; /* width hint (N < 1 is in percent of termwidth) */
92 int flags; /* SCOLS_FL_* */
93 const char *help;
09f9a393
KZ
94};
95
96747e75 96enum { COL_FLAG, COL_DESC, COL_STATUS, COL_BSTATUS, COL_DEVICE };
09f9a393
KZ
97
98/* columns descriptions */
37b2b3fa 99static const struct colinfo infos[] = {
09f9a393 100 [COL_FLAG] = { "FLAG", 14, 0, N_("flag name") },
fe7af530
OO
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") },
96747e75
KZ
104 [COL_DEVICE] = { "DEVICE", 0.1, 0, N_("watchdog device name") }
105
09f9a393
KZ
106};
107
059a91f8
KZ
108static int columns[ARRAY_SIZE(infos) * 2];
109static int ncolumns;
09f9a393 110
e4d511d4 111struct wd_device {
8c8df421 112 const char *devpath;
45303291 113 struct path_cxt *sysfs;
09f9a393 114
86ffb5a7
KZ
115 char *governor;
116 char **available_governors;
117
09f9a393
KZ
118 int timeout;
119 int timeleft;
120 int pretimeout;
121
122 uint32_t status;
123 uint32_t bstatus;
b3dd29d1 124 int nowayout;
09f9a393
KZ
125
126 struct watchdog_info ident;
127
6de3def8
TW
128 unsigned int has_identity : 1,
129 has_fw_version : 1,
130 has_options : 1,
131 has_status : 1,
132 has_bootstatus : 1,
133 has_timeout : 1,
09f9a393 134 has_timeleft : 1,
b3dd29d1 135 has_pretimeout : 1,
45303291
KZ
136 has_nowayout : 1,
137 no_sysfs : 1;
09f9a393
KZ
138};
139
a599d137 140struct wd_control {
7d64a2b5
KZ
141 /* set */
142 int timeout; /* --settimeout */
143 int pretimeout; /* --setpretimeout */
c0762da1 144 const char *governor; /* --setpregovernor */
7d64a2b5
KZ
145 unsigned int set_timeout : 1,
146 set_pretimeout : 1;
147
148 /* output */
a599d137
KZ
149 unsigned int show_oneline : 1,
150 show_raw : 1,
151 hide_headings : 1,
152 hide_flags : 1,
153 hide_ident : 1,
154 hide_timeouts : 1;
155};
156
c0762da1
KZ
157#define want_set(_ctl) ((_ctl)->set_timeout \
158 || (_ctl)->set_pretimeout \
159 || (_ctl)->governor)
7d64a2b5 160
09f9a393
KZ
161/* converts flag name to flag bit */
162static long name2bit(const char *name, size_t namesz)
163{
164 size_t i;
165
166 for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
167 const char *cn = wdflags[i].name;
168 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
169 return wdflags[i].flag;
170 }
171 warnx(_("unknown flag: %s"), name);
172 return -1;
173}
174
175static int column2id(const char *name, size_t namesz)
176{
177 size_t i;
178
059a91f8 179 for (i = 0; i < ARRAY_SIZE(infos); i++) {
09f9a393
KZ
180 const char *cn = infos[i].name;
181 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
182 return i;
183 }
184 warnx(_("unknown column: %s"), name);
185 return -1;
186}
187
188static int get_column_id(int num)
189{
09f9a393 190 assert(num < ncolumns);
059a91f8 191 assert(columns[num] < (int) ARRAY_SIZE(infos));
09f9a393
KZ
192
193 return columns[num];
194}
195
37b2b3fa 196static const struct colinfo *get_column_info(unsigned num)
09f9a393
KZ
197{
198 return &infos[ get_column_id(num) ];
199}
200
8c8df421
KZ
201/* We preffer cdev /dev/watchdog0 as this device has node in
202 * /sys/class/watchdog/. The old miscdev /dev/watchdog is fallback for old
203 * systemds only.
204 */
205static const char *get_default_device(void)
206{
207 const char **p;
208 static const char *devs[] = {
209 "/dev/watchdog0",
210 "/dev/watchdog",
211 NULL
212 };
213
214 for (p = devs; *p; p++) {
215 if (access(*p, F_OK) == 0)
216 return *p;
217 }
218
219 return NULL;
220}
221
86be6a32 222static void __attribute__((__noreturn__)) usage(void)
8fb6a2de 223{
86be6a32 224 FILE *out = stdout;
09f9a393 225 size_t i;
8c8df421 226 const char *dflt = get_default_device();
8fb6a2de
LP
227
228 fputs(USAGE_HEADER, out);
229 fprintf(out,
f56338b4 230 _(" %s [options] [<device> ...]\n"), program_invocation_short_name);
8fb6a2de 231
451dbcfa
BS
232 fputs(USAGE_SEPARATOR, out);
233 fputs(_("Show the status of the hardware watchdog.\n"), out);
8fb6a2de 234
451dbcfa 235 fputs(USAGE_OPTIONS, out);
fb8b62df
HH
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"
152b47d2 242 " -p, --setpretimeout <sec> set watchdog pre-timeout\n"
c0762da1 243 " -g, --setpregovernor <name> set pre-timeout governor\n"
fb8b62df
HH
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);
09f9a393 248
8fb6a2de 249 fputs(USAGE_SEPARATOR, out);
bad4c729 250 fprintf(out, USAGE_HELP_OPTIONS(24));
09f9a393
KZ
251 fputs(USAGE_SEPARATOR, out);
252
8c8df421
KZ
253 if (dflt)
254 fprintf(out, _("The default device is %s.\n"), dflt);
255 else
256 fprintf(out, _("No default device is available.\n"));
f56338b4 257
6e2d5a44 258 fputs(USAGE_COLUMNS, out);
09f9a393
KZ
259 for (i = 0; i < ARRAY_SIZE(infos); i++)
260 fprintf(out, " %13s %s\n", infos[i].name, _(infos[i].help));
261
bad4c729 262 fprintf(out, USAGE_MAN_TAIL("wdctl(8)"));
8fb6a2de 263
86be6a32 264 exit(EXIT_SUCCESS);
8fb6a2de
LP
265}
266
7d64a2b5
KZ
267static struct path_cxt *get_sysfs(struct wd_device *wd)
268{
269 struct path_cxt *sys;
270 struct stat st;
271
272 if (wd->no_sysfs)
273 return NULL;
274 if (wd->sysfs)
275 return wd->sysfs;
276 if (stat(wd->devpath, &st) != 0)
277 goto nosysfs;
278
279 sys = ul_new_path(_PATH_SYS_DEVCHAR "/%u:%u",
280 major(st.st_rdev), minor(st.st_rdev));
281 if (!sys)
282 return NULL;
283
284 if (ul_path_get_dirfd(sys) < 0)
285 goto nosysfs; /* device not in /sys */
286
287 if (ul_path_access(sys, F_OK, "identity") != 0)
288 goto nosysfs; /* no info in /sys (old miscdev?) */
289
290 wd->sysfs = sys;
291 return sys;
292nosysfs:
293 wd->no_sysfs = 1;
294 return NULL;
295}
296
e4d511d4 297static void add_flag_line(struct libscols_table *table, struct wd_device *wd, const struct wdflag *fl)
8fb6a2de 298{
09f9a393 299 int i;
fe7af530 300 struct libscols_line *line;
8fb6a2de 301
fe7af530 302 line = scols_table_new_line(table, NULL);
09f9a393 303 if (!line) {
780ce22c 304 warn(_("failed to allocate output line"));
8fb6a2de
LP
305 return;
306 }
307
09f9a393
KZ
308 for (i = 0; i < ncolumns; i++) {
309 const char *str = NULL;
310
311 switch (get_column_id(i)) {
312 case COL_FLAG:
313 str = fl->name;
314 break;
315 case COL_DESC:
316 str = fl->description;
317 break;
318 case COL_STATUS:
319 str = wd->status & fl->flag ? "1" : "0";
320 break;
321 case COL_BSTATUS:
322 str = wd->bstatus & fl->flag ? "1" : "0";
323 break;
96747e75 324 case COL_DEVICE:
e4d511d4 325 str = wd->devpath;
96747e75 326 break;
09f9a393
KZ
327 default:
328 break;
329 }
8fb6a2de 330
780ce22c
KZ
331 if (str && scols_line_set_data(line, i, str)) {
332 warn(_("failed to add output data"));
333 break;
334 }
09f9a393 335 }
8fb6a2de
LP
336}
337
a599d137 338static int show_flags(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
8fb6a2de 339{
09f9a393
KZ
340 size_t i;
341 int rc = -1;
fe7af530 342 struct libscols_table *table;
09f9a393
KZ
343 uint32_t flags;
344
b3dd29d1
KZ
345 /* information about supported bits is probably missing in /sys */
346 if (!wd->ident.options)
347 return 0;
348
710ed55d
KZ
349 scols_init_debug(0);
350
09f9a393 351 /* create output table */
0925a9dd 352 table = scols_new_table();
fe7af530 353 if (!table) {
780ce22c 354 warn(_("failed to allocate output table"));
09f9a393
KZ
355 return -1;
356 }
a599d137
KZ
357 scols_table_enable_raw(table, ctl->show_raw);
358 scols_table_enable_noheadings(table, ctl->hide_headings);
8fb6a2de 359
09f9a393
KZ
360 /* define columns */
361 for (i = 0; i < (size_t) ncolumns; i++) {
37b2b3fa 362 const struct colinfo *col = get_column_info(i);
8fb6a2de 363
fe7af530 364 if (!scols_table_new_column(table, col->name, col->whint, col->flags)) {
780ce22c 365 warnx(_("failed to allocate output column"));
09f9a393 366 goto done;
8fb6a2de
LP
367 }
368 }
369
09f9a393
KZ
370 /* fill-in table with data
371 * -- one line for each supported flag (option) */
372 flags = wd->ident.options;
8fb6a2de 373
09f9a393
KZ
374 for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
375 if (wanted && !(wanted & wdflags[i].flag))
376 ; /* ignore */
377 else if (flags & wdflags[i].flag)
fe7af530 378 add_flag_line(table, wd, &wdflags[i]);
8fb6a2de 379
09f9a393 380 flags &= ~wdflags[i].flag;
8fb6a2de
LP
381 }
382
09f9a393 383 if (flags)
e4d511d4 384 warnx(_("%s: unknown flags 0x%x\n"), wd->devpath, flags);
8fb6a2de 385
fe7af530 386 scols_print_table(table);
09f9a393
KZ
387 rc = 0;
388done:
fe7af530 389 scols_unref_table(table);
09f9a393
KZ
390 return rc;
391}
152b47d2 392
fb8b62df
HH
393/*
394 * Warning: successfully opened watchdog has to be properly closed with magic
395 * close character otherwise the machine will be rebooted!
396 *
397 * Don't use err() or exit() here!
398 */
7d64a2b5 399static int set_watchdog(struct wd_control *ctl, struct wd_device *wd)
fb8b62df
HH
400{
401 int fd;
402 sigset_t sigs, oldsigs;
403 int rc = 0;
404
7d64a2b5 405 assert(wd);
e4d511d4 406 assert(wd->devpath);
7d64a2b5 407 assert(ctl);
fb8b62df 408
fe7f7399 409 if (!ctl->set_timeout && !ctl->set_pretimeout)
c0762da1
KZ
410 goto sysfs_only;
411
fb8b62df
HH
412 sigemptyset(&oldsigs);
413 sigfillset(&sigs);
414 sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
415
e4d511d4 416 fd = open(wd->devpath, O_WRONLY|O_CLOEXEC);
fb8b62df
HH
417
418 if (fd < 0) {
419 if (errno == EBUSY)
420 warnx(_("%s: watchdog already in use, terminating."),
e4d511d4
KZ
421 wd->devpath);
422 warn(_("cannot open %s"), wd->devpath);
fb8b62df
HH
423 return -1;
424 }
425
426 for (;;) {
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';
430
431 if (write(fd, &v, 1) >= 0)
432 break;
433 if (errno != EINTR) {
e4d511d4 434 warn(_("%s: failed to disarm watchdog"), wd->devpath);
fb8b62df
HH
435 break;
436 }
437 /* Let's try hard, since if we don't get this right
438 * the machine might end up rebooting. */
439 }
440
7d64a2b5
KZ
441 if (ctl->set_timeout) {
442 if (ioctl(fd, WDIOC_SETTIMEOUT, &ctl->timeout) != 0) {
443 rc += errno;
444 warn(_("cannot set timeout for %s"), wd->devpath);
445 } else
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);
fb8b62df
HH
449 }
450
7d64a2b5
KZ
451 if (ctl->set_pretimeout) {
452 if (ioctl(fd, WDIOC_SETPRETIMEOUT, &ctl->pretimeout) != 0) {
453 rc += errno;
454 warn(_("cannot set pretimeout for %s"), wd->devpath);
455 } else
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);
152b47d2
KZ
459 }
460
4aaabaa9 461 if (close(fd))
3757e57f 462 warn(_("write failed"));
fb8b62df 463
7d64a2b5 464 sigprocmask(SIG_SETMASK, &oldsigs, NULL);
c0762da1
KZ
465
466sysfs_only:
467 if (ctl->governor) {
468 struct path_cxt *sys = get_sysfs(wd);
469 int xrc;
470
471 xrc = !sys ? errno :
472 ul_path_write_string(sys, ctl->governor,
473 "pretimeout_governor");
474 if (xrc)
475 warn(_("cannot set pre-timeout governor"));
476 rc += xrc;
477 }
478
fb8b62df
HH
479 return rc;
480}
8fb6a2de 481
09f9a393
KZ
482/*
483 * Warning: successfully opened watchdog has to be properly closed with magic
484 * close character otherwise the machine will be rebooted!
485 *
486 * Don't use err() or exit() here!
487 */
b3dd29d1 488static int read_watchdog_from_device(struct wd_device *wd)
09f9a393
KZ
489{
490 int fd;
491 sigset_t sigs, oldsigs;
492
e4d511d4 493 assert(wd->devpath);
09f9a393
KZ
494
495 sigemptyset(&oldsigs);
496 sigfillset(&sigs);
497 sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
8fb6a2de 498
e4d511d4 499 fd = open(wd->devpath, O_WRONLY|O_CLOEXEC);
8fb6a2de 500
b3dd29d1
KZ
501 if (fd < 0)
502 return -errno;
8fb6a2de 503
09f9a393 504 if (ioctl(fd, WDIOC_GETSUPPORT, &wd->ident) < 0)
e4d511d4 505 warn(_("%s: failed to get information about watchdog"), wd->devpath);
09f9a393
KZ
506 else {
507 ioctl(fd, WDIOC_GETSTATUS, &wd->status);
508 ioctl(fd, WDIOC_GETBOOTSTATUS, &wd->bstatus);
509
b1b0259f
HG
510 /*
511 * Sometimes supported options like WDIOF_CARDRESET are missing from
512 * ident.options, add anything set in status/bstatus to ident.options.
513 */
514 wd->ident.options |= wd->status;
515 wd->ident.options |= wd->bstatus;
516
09f9a393
KZ
517 if (ioctl(fd, WDIOC_GETTIMEOUT, &wd->timeout) >= 0)
518 wd->has_timeout = 1;
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;
523 }
8fb6a2de
LP
524
525 for (;;) {
526 /* We just opened this to query the state, not to arm
527 * it hence use the magic close character */
8fb6a2de
LP
528 static const char v = 'V';
529
530 if (write(fd, &v, 1) >= 0)
531 break;
8fb6a2de 532 if (errno != EINTR) {
e4d511d4 533 warn(_("%s: failed to disarm watchdog"), wd->devpath);
8fb6a2de
LP
534 break;
535 }
8fb6a2de
LP
536 /* Let's try hard, since if we don't get this right
537 * the machine might end up rebooting. */
538 }
539
4aaabaa9 540 if (close(fd))
3757e57f 541 warn(_("write failed"));
09f9a393
KZ
542 sigprocmask(SIG_SETMASK, &oldsigs, NULL);
543
544 return 0;
545}
546
45303291
KZ
547
548/* Returns: <0 error, 0 success, 1 unssuported */
549static int read_watchdog_from_sysfs(struct wd_device *wd)
550{
551 struct path_cxt *sys;
552
553 sys = get_sysfs(wd);
554 if (!sys)
555 return 1;
556
6de3def8
TW
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)
562 wd->has_options = 1;
563 if (ul_path_scanf(sys, "status", "%x", &wd->status) == 1)
564 wd->has_status = 1;
565 if (ul_path_read_u32(sys, &wd->bstatus, "bootstatus") == 0)
566 wd->has_bootstatus = 1;
b3dd29d1
KZ
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)
570 wd->has_timeout = 1;
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;
575
b3dd29d1 576 return 0;
b3dd29d1
KZ
577}
578
86ffb5a7
KZ
579static int read_governors(struct wd_device *wd)
580{
581 struct path_cxt *sys;
582 FILE *f;
583
584 sys = get_sysfs(wd);
585 if (!sys)
586 return 1;
587
588 f = ul_path_fopen(sys, "r", "pretimeout_available_governors");
589 if (f) {
590 char *line = NULL;
591 size_t dummy = 0;
592 ssize_t sz;
593
594 while ((sz = getline(&line, &dummy, f)) >= 0) {
595 if (rtrim_whitespace((unsigned char *) line) == 0)
596 continue;
597 strv_consume(&wd->available_governors, line);
598 dummy = 0;
599 line = NULL;
600 }
601 free(line);
602 fclose(f);
603 }
604
605 ul_path_read_string(sys, &wd->governor, "pretimeout_governor");
606 return 0;
607}
608
6de3def8
TW
609static bool should_read_from_device(struct wd_device *wd)
610{
611 if (!wd->has_nowayout)
612 return false;
613
614 if (wd->nowayout)
615 return false;
616
617 return !wd->has_identity ||
618 !wd->has_fw_version ||
619 !wd->has_options ||
620 !wd->has_status ||
621 !wd->has_bootstatus ||
622 !wd->has_timeout ||
623 !wd->has_timeleft;
624 // pretimeout attribute may be hidden in sysfs
625}
626
b3dd29d1
KZ
627static int read_watchdog(struct wd_device *wd)
628{
6de3def8
TW
629 int rc;
630
631 rc = read_watchdog_from_sysfs(wd);
b3dd29d1 632
6de3def8
TW
633 if (rc && should_read_from_device(wd))
634 rc = read_watchdog_from_device(wd);
b3dd29d1
KZ
635
636 if (rc) {
637 warn(_("cannot read information about %s"), wd->devpath);
638 return -1;
639 }
640
86ffb5a7 641 read_governors(wd);
b3dd29d1
KZ
642 return 0;
643}
644
5d628f37
KZ
645static void show_timeouts(struct wd_device *wd)
646{
647 if (wd->has_timeout)
648 printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->timeout),
649 _("Timeout:"), wd->timeout);
5d628f37
KZ
650 if (wd->has_timeleft)
651 printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->timeleft),
652 _("Timeleft:"), wd->timeleft);
86ffb5a7
KZ
653 if (wd->has_pretimeout)
654 printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->pretimeout),
655 _("Pre-timeout:"), wd->pretimeout);
656}
657
658static void show_governors(struct wd_device *wd)
659{
660 if (wd->governor)
661 printf(_("%-14s %s\n"), _("Pre-timeout governor:"), wd->governor);
662 if (wd->available_governors) {
663 char *tmp = strv_join(wd->available_governors, " ");
664
665 if (tmp)
666 printf(_("%-14s %s\n"),
667 _("Available pre-timeout governors:"), tmp);
668 free(tmp);
669 }
5d628f37
KZ
670}
671
a599d137 672static void print_oneline(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
9561d1af 673{
e4d511d4 674 printf("%s:", wd->devpath);
9561d1af 675
a599d137 676 if (!ctl->hide_ident) {
9561d1af
KZ
677 printf(" VERSION=\"%x\"", wd->ident.firmware_version);
678
679 printf(" IDENTITY=");
fe7af530 680 fputs_quoted((char *) wd->ident.identity, stdout);
9561d1af 681 }
a599d137 682 if (!ctl->hide_timeouts) {
9561d1af
KZ
683 if (wd->has_timeout)
684 printf(" TIMEOUT=\"%i\"", wd->timeout);
685 if (wd->has_pretimeout)
686 printf(" PRETIMEOUT=\"%i\"", wd->pretimeout);
687 if (wd->has_timeleft)
688 printf(" TIMELEFT=\"%i\"", wd->timeleft);
689 }
690
a599d137 691 if (!ctl->hide_flags) {
9561d1af
KZ
692 size_t i;
693 uint32_t flags = wd->ident.options;
694
695 for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
696 const struct wdflag *fl;
697
698 if ((wanted && !(wanted & wdflags[i].flag)) ||
699 !(flags & wdflags[i].flag))
700 continue;
701
702 fl= &wdflags[i];
703
704 printf(" %s=\"%s\"", fl->name,
705 wd->status & fl->flag ? "1" : "0");
706 printf(" %s_BOOT=\"%s\"", fl->name,
707 wd->bstatus & fl->flag ? "1" : "0");
708
709 }
710 }
711
712 fputc('\n', stdout);
713}
714
5d628f37 715static void print_device(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
09f9a393 716{
5d628f37
KZ
717 /* NAME=value one line output */
718 if (ctl->show_oneline) {
719 print_oneline(ctl, wd, wanted);
720 return;
721 }
722
723 /* pretty output */
724 if (!ctl->hide_ident) {
725 printf("%-15s%s\n", _("Device:"), wd->devpath);
726 printf("%-15s%s [%s %x]\n",
727 _("Identity:"),
728 wd->ident.identity,
729 _("version"),
730 wd->ident.firmware_version);
731 }
732 if (!ctl->hide_timeouts)
733 show_timeouts(wd);
734
86ffb5a7
KZ
735 show_governors(wd);
736
5d628f37
KZ
737 if (!ctl->hide_flags)
738 show_flags(ctl, wd, wanted);
09f9a393 739}
8fb6a2de 740
09f9a393
KZ
741int main(int argc, char *argv[])
742{
e4d511d4 743 struct wd_device wd;
a599d137 744 struct wd_control ctl = { .hide_headings = 0 };
fe7af530 745 int c, res = EXIT_SUCCESS, count = 0;
f36f3251 746 unsigned long wanted = 0;
8c8df421 747 const char *dflt_device = NULL;
09f9a393
KZ
748
749 static const struct option long_opts[] = {
09f9a393 750 { "flags", required_argument, NULL, 'f' },
96747e75 751 { "flags-only", no_argument, NULL, 'x' },
09f9a393
KZ
752 { "help", no_argument, NULL, 'h' },
753 { "noflags", no_argument, NULL, 'F' },
754 { "noheadings", no_argument, NULL, 'n' },
755 { "noident", no_argument, NULL, 'I' },
756 { "notimeouts", no_argument, NULL, 'T' },
fb8b62df 757 { "settimeout", required_argument, NULL, 's' },
152b47d2 758 { "setpretimeout", required_argument, NULL, 'p' },
c0762da1 759 { "setpregovernor", required_argument, NULL, 'g' },
09f9a393 760 { "output", required_argument, NULL, 'o' },
9561d1af 761 { "oneline", no_argument, NULL, 'O' },
09f9a393
KZ
762 { "raw", no_argument, NULL, 'r' },
763 { "version", no_argument, NULL, 'V' },
764 { NULL, 0, NULL, 0 }
765 };
766
a7349ee3 767 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
1c608be1
KZ
768 { 'F','f' }, /* noflags,flags*/
769 { 0 }
770 };
771 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
772
09f9a393
KZ
773 setlocale(LC_ALL, "");
774 bindtextdomain(PACKAGE, LOCALEDIR);
775 textdomain(PACKAGE);
2c308875 776 close_stdout_atexit();
09f9a393
KZ
777
778 while ((c = getopt_long(argc, argv,
c0762da1 779 "d:f:g:hFnITp:o:s:OrVx", long_opts, NULL)) != -1) {
1c608be1
KZ
780
781 err_exclusive_options(c, long_opts, excl, excl_st);
782
09f9a393 783 switch(c) {
09f9a393
KZ
784 case 'o':
785 ncolumns = string_to_idarray(optarg,
786 columns, ARRAY_SIZE(columns),
787 column2id);
788 if (ncolumns < 0)
789 return EXIT_FAILURE;
790 break;
fb8b62df 791 case 's':
7d64a2b5
KZ
792 ctl.timeout = strtos32_or_err(optarg, _("invalid timeout argument"));
793 ctl.set_timeout = 1;
152b47d2
KZ
794 break;
795 case 'p':
7d64a2b5
KZ
796 ctl.pretimeout = strtos32_or_err(optarg, _("invalid pretimeout argument"));
797 ctl.set_pretimeout = 1;
fb8b62df 798 break;
09f9a393 799 case 'f':
f36f3251 800 if (string_to_bitmask(optarg, &wanted, name2bit) != 0)
09f9a393
KZ
801 return EXIT_FAILURE;
802 break;
09f9a393 803 case 'F':
a599d137 804 ctl.hide_flags = 1;
09f9a393 805 break;
c0762da1
KZ
806 case 'g':
807 ctl.governor = optarg;
808 break;
09f9a393 809 case 'I':
a599d137 810 ctl.hide_ident = 1;
09f9a393
KZ
811 break;
812 case 'T':
a599d137 813 ctl.hide_timeouts = 1;
09f9a393
KZ
814 break;
815 case 'n':
a599d137 816 ctl.hide_headings = 1;
09f9a393
KZ
817 break;
818 case 'r':
a599d137 819 ctl.show_raw = 1;
09f9a393 820 break;
9561d1af 821 case 'O':
a599d137 822 ctl.show_oneline = 1;
09f9a393 823 break;
96747e75 824 case 'x':
a599d137
KZ
825 ctl.hide_ident = 1;
826 ctl.hide_timeouts = 1;
96747e75 827 break;
2c308875
KZ
828
829 case 'h':
830 usage();
831 case 'V':
832 print_version(EXIT_SUCCESS);
09f9a393 833 default:
677ec86c 834 errtryhelp(EXIT_FAILURE);
09f9a393
KZ
835 }
836 }
837
09f9a393
KZ
838 if (!ncolumns) {
839 /* default columns */
840 columns[ncolumns++] = COL_FLAG;
841 columns[ncolumns++] = COL_DESC;
842 columns[ncolumns++] = COL_STATUS;
843 columns[ncolumns++] = COL_BSTATUS;
844 }
845
8c8df421
KZ
846 /* Device no specified, use default. */
847 if (optind == argc) {
848 dflt_device = get_default_device();
849 if (!dflt_device)
850 err(EXIT_FAILURE, _("No default device is available."));
851 }
852
f56338b4
KZ
853 do {
854 int rc;
09f9a393 855
f56338b4 856 memset(&wd, 0, sizeof(wd));
8c8df421 857 wd.devpath = dflt_device ? dflt_device : argv[optind++];
f56338b4
KZ
858
859 if (count)
860 fputc('\n', stdout);
861 count++;
862
7d64a2b5
KZ
863 if (want_set(&ctl)) {
864 rc = set_watchdog(&ctl, &wd);
fb8b62df
HH
865 if (rc) {
866 res = EXIT_FAILURE;
867 }
868 }
869
f56338b4
KZ
870 rc = read_watchdog(&wd);
871 if (rc) {
872 res = EXIT_FAILURE;
873 continue;
874 }
875
5d628f37 876 print_device(&ctl, &wd, wanted);
7d64a2b5 877 ul_unref_path(wd.sysfs);
f56338b4
KZ
878 } while (optind < argc);
879
880 return res;
8fb6a2de 881}