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