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