]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/wdctl.c
wdctl: remove printing from main()
[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 <unistd.h>
25 #include <signal.h>
26 #include <assert.h>
27 #include <linux/watchdog.h>
28
29 #include <libsmartcols.h>
30
31 #include "nls.h"
32 #include "c.h"
33 #include "xalloc.h"
34 #include "closestream.h"
35 #include "optutils.h"
36 #include "pathnames.h"
37 #include "strutils.h"
38 #include "carefulputc.h"
39
40 /*
41 * since 2.6.18
42 */
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 */
52 #endif
53
54 /*
55 * since 3.5
56 */
57 #ifndef WDIOF_ALARMONLY
58 # define WDIOF_ALARMONLY 0x0400 /* Watchdog triggers a management or
59 other external alarm not a reboot */
60 #endif
61
62 struct wdflag {
63 uint32_t flag;
64 const char *name;
65 const char *description;
66 };
67
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") }
81 };
82
83
84 /* column names */
85 struct colinfo {
86 const char *name; /* header */
87 double whint; /* width hint (N < 1 is in percent of termwidth) */
88 int flags; /* SCOLS_FL_* */
89 const char *help;
90 };
91
92 enum { COL_FLAG, COL_DESC, COL_STATUS, COL_BSTATUS, COL_DEVICE };
93
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") }
101
102 };
103
104 static int columns[ARRAY_SIZE(infos) * 2];
105 static int ncolumns;
106
107 struct wd_device {
108 char *devpath;
109
110 int timeout;
111 int timeleft;
112 int pretimeout;
113
114 uint32_t status;
115 uint32_t bstatus;
116
117 struct watchdog_info ident;
118
119 unsigned int has_timeout : 1,
120 has_timeleft : 1,
121 has_pretimeout : 1;
122 };
123
124 struct wd_control {
125 unsigned int show_oneline : 1,
126 show_raw : 1,
127 hide_headings : 1,
128 hide_flags : 1,
129 hide_ident : 1,
130 hide_timeouts : 1;
131 };
132
133 /* converts flag name to flag bit */
134 static long name2bit(const char *name, size_t namesz)
135 {
136 size_t i;
137
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;
142 }
143 warnx(_("unknown flag: %s"), name);
144 return -1;
145 }
146
147 static int column2id(const char *name, size_t namesz)
148 {
149 size_t i;
150
151 for (i = 0; i < ARRAY_SIZE(infos); i++) {
152 const char *cn = infos[i].name;
153 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
154 return i;
155 }
156 warnx(_("unknown column: %s"), name);
157 return -1;
158 }
159
160 static int get_column_id(int num)
161 {
162 assert(num < ncolumns);
163 assert(columns[num] < (int) ARRAY_SIZE(infos));
164
165 return columns[num];
166 }
167
168 static struct colinfo *get_column_info(unsigned num)
169 {
170 return &infos[ get_column_id(num) ];
171 }
172
173 static void __attribute__((__noreturn__)) usage(void)
174 {
175 FILE *out = stdout;
176 size_t i;
177
178 fputs(USAGE_HEADER, out);
179 fprintf(out,
180 _(" %s [options] [<device> ...]\n"), program_invocation_short_name);
181
182 fputs(USAGE_SEPARATOR, out);
183 fputs(_("Show the status of the hardware watchdog.\n"), out);
184
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);
196
197 fputs(USAGE_SEPARATOR, out);
198 printf(USAGE_HELP_OPTIONS(24));
199 fputs(USAGE_SEPARATOR, out);
200
201 fprintf(out, _("The default device is %s.\n"), _PATH_WATCHDOG_DEV);
202
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));
206
207 printf(USAGE_MAN_TAIL("wdctl(8)"));
208
209 exit(EXIT_SUCCESS);
210 }
211
212 static void add_flag_line(struct libscols_table *table, struct wd_device *wd, const struct wdflag *fl)
213 {
214 int i;
215 struct libscols_line *line;
216
217 line = scols_table_new_line(table, NULL);
218 if (!line) {
219 warn(_("failed to allocate output line"));
220 return;
221 }
222
223 for (i = 0; i < ncolumns; i++) {
224 const char *str = NULL;
225
226 switch (get_column_id(i)) {
227 case COL_FLAG:
228 str = fl->name;
229 break;
230 case COL_DESC:
231 str = fl->description;
232 break;
233 case COL_STATUS:
234 str = wd->status & fl->flag ? "1" : "0";
235 break;
236 case COL_BSTATUS:
237 str = wd->bstatus & fl->flag ? "1" : "0";
238 break;
239 case COL_DEVICE:
240 str = wd->devpath;
241 break;
242 default:
243 break;
244 }
245
246 if (str && scols_line_set_data(line, i, str)) {
247 warn(_("failed to add output data"));
248 break;
249 }
250 }
251 }
252
253 static int show_flags(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
254 {
255 size_t i;
256 int rc = -1;
257 struct libscols_table *table;
258 uint32_t flags;
259
260 scols_init_debug(0);
261
262 /* create output table */
263 table = scols_new_table();
264 if (!table) {
265 warn(_("failed to allocate output table"));
266 return -1;
267 }
268 scols_table_enable_raw(table, ctl->show_raw);
269 scols_table_enable_noheadings(table, ctl->hide_headings);
270
271 /* define columns */
272 for (i = 0; i < (size_t) ncolumns; i++) {
273 struct colinfo *col = get_column_info(i);
274
275 if (!scols_table_new_column(table, col->name, col->whint, col->flags)) {
276 warnx(_("failed to allocate output column"));
277 goto done;
278 }
279 }
280
281 /* fill-in table with data
282 * -- one line for each supported flag (option) */
283 flags = wd->ident.options;
284
285 for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
286 if (wanted && !(wanted & wdflags[i].flag))
287 ; /* ignore */
288 else if (flags & wdflags[i].flag)
289 add_flag_line(table, wd, &wdflags[i]);
290
291 flags &= ~wdflags[i].flag;
292 }
293
294 if (flags)
295 warnx(_("%s: unknown flags 0x%x\n"), wd->devpath, flags);
296
297 scols_print_table(table);
298 rc = 0;
299 done:
300 scols_unref_table(table);
301 return rc;
302 }
303 /*
304 * Warning: successfully opened watchdog has to be properly closed with magic
305 * close character otherwise the machine will be rebooted!
306 *
307 * Don't use err() or exit() here!
308 */
309 static int set_watchdog(struct wd_device *wd, int timeout)
310 {
311 int fd;
312 sigset_t sigs, oldsigs;
313 int rc = 0;
314
315 assert(wd->devpath);
316
317 sigemptyset(&oldsigs);
318 sigfillset(&sigs);
319 sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
320
321 fd = open(wd->devpath, O_WRONLY|O_CLOEXEC);
322
323 if (fd < 0) {
324 if (errno == EBUSY)
325 warnx(_("%s: watchdog already in use, terminating."),
326 wd->devpath);
327 warn(_("cannot open %s"), wd->devpath);
328 return -1;
329 }
330
331 for (;;) {
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';
335
336 if (write(fd, &v, 1) >= 0)
337 break;
338 if (errno != EINTR) {
339 warn(_("%s: failed to disarm watchdog"), wd->devpath);
340 break;
341 }
342 /* Let's try hard, since if we don't get this right
343 * the machine might end up rebooting. */
344 }
345
346 if (ioctl(fd, WDIOC_SETTIMEOUT, &timeout) != 0) {
347 rc = errno;
348 warn(_("cannot set timeout for %s"), wd->devpath);
349 }
350
351 if (close(fd))
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);
356
357 return rc;
358 }
359
360 /*
361 * Warning: successfully opened watchdog has to be properly closed with magic
362 * close character otherwise the machine will be rebooted!
363 *
364 * Don't use err() or exit() here!
365 */
366 static int read_watchdog(struct wd_device *wd)
367 {
368 int fd;
369 sigset_t sigs, oldsigs;
370
371 assert(wd->devpath);
372
373 sigemptyset(&oldsigs);
374 sigfillset(&sigs);
375 sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
376
377 fd = open(wd->devpath, O_WRONLY|O_CLOEXEC);
378
379 if (fd < 0) {
380 if (errno == EBUSY)
381 warnx(_("%s: watchdog already in use, terminating."),
382 wd->devpath);
383 warn(_("cannot open %s"), wd->devpath);
384 return -1;
385 }
386
387 if (ioctl(fd, WDIOC_GETSUPPORT, &wd->ident) < 0)
388 warn(_("%s: failed to get information about watchdog"), wd->devpath);
389 else {
390 ioctl(fd, WDIOC_GETSTATUS, &wd->status);
391 ioctl(fd, WDIOC_GETBOOTSTATUS, &wd->bstatus);
392
393 if (ioctl(fd, WDIOC_GETTIMEOUT, &wd->timeout) >= 0)
394 wd->has_timeout = 1;
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;
399 }
400
401 for (;;) {
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';
405
406 if (write(fd, &v, 1) >= 0)
407 break;
408 if (errno != EINTR) {
409 warn(_("%s: failed to disarm watchdog"), wd->devpath);
410 break;
411 }
412 /* Let's try hard, since if we don't get this right
413 * the machine might end up rebooting. */
414 }
415
416 if (close(fd))
417 warn(_("write failed"));
418 sigprocmask(SIG_SETMASK, &oldsigs, NULL);
419
420 return 0;
421 }
422
423 static void show_timeouts(struct wd_device *wd)
424 {
425 if (wd->has_timeout)
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);
434 }
435
436 static void print_oneline(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
437 {
438 printf("%s:", wd->devpath);
439
440 if (!ctl->hide_ident) {
441 printf(" VERSION=\"%x\"", wd->ident.firmware_version);
442
443 printf(" IDENTITY=");
444 fputs_quoted((char *) wd->ident.identity, stdout);
445 }
446 if (!ctl->hide_timeouts) {
447 if (wd->has_timeout)
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);
453 }
454
455 if (!ctl->hide_flags) {
456 size_t i;
457 uint32_t flags = wd->ident.options;
458
459 for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
460 const struct wdflag *fl;
461
462 if ((wanted && !(wanted & wdflags[i].flag)) ||
463 !(flags & wdflags[i].flag))
464 continue;
465
466 fl= &wdflags[i];
467
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");
472
473 }
474 }
475
476 fputc('\n', stdout);
477 }
478
479 static void print_device(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
480 {
481 /* NAME=value one line output */
482 if (ctl->show_oneline) {
483 print_oneline(ctl, wd, wanted);
484 return;
485 }
486
487 /* pretty output */
488 if (!ctl->hide_ident) {
489 printf("%-15s%s\n", _("Device:"), wd->devpath);
490 printf("%-15s%s [%s %x]\n",
491 _("Identity:"),
492 wd->ident.identity,
493 _("version"),
494 wd->ident.firmware_version);
495 }
496 if (!ctl->hide_timeouts)
497 show_timeouts(wd);
498
499 if (!ctl->hide_flags)
500 show_flags(ctl, wd, wanted);
501 }
502
503 int main(int argc, char *argv[])
504 {
505 struct wd_device wd;
506 struct wd_control ctl = { .hide_headings = 0 };
507 int c, res = EXIT_SUCCESS, count = 0;
508 uint32_t wanted = 0;
509 int timeout = 0;
510
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' },
524 { NULL, 0, NULL, 0 }
525 };
526
527 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
528 { 'F','f' }, /* noflags,flags*/
529 { 0 }
530 };
531 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
532
533 setlocale(LC_ALL, "");
534 bindtextdomain(PACKAGE, LOCALEDIR);
535 textdomain(PACKAGE);
536 close_stdout_atexit();
537
538 while ((c = getopt_long(argc, argv,
539 "d:f:hFnITo:s:OrVx", long_opts, NULL)) != -1) {
540
541 err_exclusive_options(c, long_opts, excl, excl_st);
542
543 switch(c) {
544 case 'o':
545 ncolumns = string_to_idarray(optarg,
546 columns, ARRAY_SIZE(columns),
547 column2id);
548 if (ncolumns < 0)
549 return EXIT_FAILURE;
550 break;
551 case 's':
552 timeout = strtos32_or_err(optarg, _("invalid timeout argument"));
553 break;
554 case 'f':
555 if (string_to_bitmask(optarg, (unsigned long *) &wanted, name2bit) != 0)
556 return EXIT_FAILURE;
557 break;
558 case 'F':
559 ctl.hide_flags = 1;
560 break;
561 case 'I':
562 ctl.hide_ident = 1;
563 break;
564 case 'T':
565 ctl.hide_timeouts = 1;
566 break;
567 case 'n':
568 ctl.hide_headings = 1;
569 break;
570 case 'r':
571 ctl.show_raw = 1;
572 break;
573 case 'O':
574 ctl.show_oneline = 1;
575 break;
576 case 'x':
577 ctl.hide_ident = 1;
578 ctl.hide_timeouts = 1;
579 break;
580
581 case 'h':
582 usage();
583 case 'V':
584 print_version(EXIT_SUCCESS);
585 default:
586 errtryhelp(EXIT_FAILURE);
587 }
588 }
589
590 if (!ncolumns) {
591 /* default columns */
592 columns[ncolumns++] = COL_FLAG;
593 columns[ncolumns++] = COL_DESC;
594 columns[ncolumns++] = COL_STATUS;
595 columns[ncolumns++] = COL_BSTATUS;
596 }
597
598 do {
599 int rc;
600
601 memset(&wd, 0, sizeof(wd));
602
603 if (optind == argc)
604 wd.devpath = _PATH_WATCHDOG_DEV;
605 else
606 wd.devpath = argv[optind++];
607
608 if (count)
609 fputc('\n', stdout);
610 count++;
611
612 if (timeout) {
613 rc = set_watchdog(&wd, timeout);
614 if (rc) {
615 res = EXIT_FAILURE;
616 }
617 }
618
619 rc = read_watchdog(&wd);
620 if (rc) {
621 res = EXIT_FAILURE;
622 continue;
623 }
624
625 print_device(&ctl, &wd, wanted);
626
627 } while (optind < argc);
628
629 return res;
630 }