]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/wdctl.c
Merge branch 'master' of https://github.com/jhuntwork/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 <linux/watchdog.h>
22 #include <sys/ioctl.h>
23 #include <getopt.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <signal.h>
27 #include <assert.h>
28
29 #include "nls.h"
30 #include "c.h"
31 #include "closestream.h"
32 #include "pathnames.h"
33 #include "strutils.h"
34 #include "tt.h"
35
36 struct wdflag {
37 uint32_t flag;
38 const char *name;
39 const char *description;
40 };
41
42 static const struct wdflag wdflags[] = {
43 { WDIOF_CARDRESET, "CARDRESET", N_("Card previously reset the CPU") },
44 { WDIOF_EXTERN1, "EXTERN1", N_("External relay 1") },
45 { WDIOF_EXTERN2, "EXTERN2", N_("External relay 2") },
46 { WDIOF_FANFAULT, "FANFAULT", N_("Fan failed") },
47 { WDIOF_KEEPALIVEPING, "KEEPALIVEPING", N_("Keep alive ping reply") },
48 { WDIOF_MAGICCLOSE, "MAGICCLOSE", N_("Supports magic close char") },
49 { WDIOF_OVERHEAT, "OVERHEAT", N_("Reset due to CPU overheat") },
50 { WDIOF_POWEROVER, "POWEROVER", N_("Power over voltage") },
51 { WDIOF_POWERUNDER, "POWERUNDER", N_("Power bad/power fault") },
52 { WDIOF_PRETIMEOUT, "PRETIMEOUT", N_("Pretimeout (in seconds)") },
53 { WDIOF_SETTIMEOUT, "SETTIMEOUT", N_("Set timeout (in seconds)") }
54 };
55
56
57 /* column names */
58 struct colinfo {
59 const char *name; /* header */
60 double whint; /* width hint (N < 1 is in percent of termwidth) */
61 int flags; /* TT_FL_* */
62 const char *help;
63 };
64
65 enum { COL_FLAG, COL_DESC, COL_STATUS, COL_BSTATUS, COL_DEVICE };
66
67 /* columns descriptions */
68 static struct colinfo infos[] = {
69 [COL_FLAG] = { "FLAG", 14, 0, N_("flag name") },
70 [COL_DESC] = { "DESCRIPTION", 0.1, TT_FL_TRUNC, N_("flag description") },
71 [COL_STATUS] = { "STATUS", 1, TT_FL_RIGHT, N_("flag status") },
72 [COL_BSTATUS] = { "BOOT-STATUS", 1, TT_FL_RIGHT, N_("flag boot status") },
73 [COL_DEVICE] = { "DEVICE", 0.1, 0, N_("watchdog device name") }
74
75 };
76
77 #define NCOLS ARRAY_SIZE(infos)
78 static int columns[NCOLS], ncolumns;
79
80 struct wdinfo {
81 char *device;
82
83 int timeout;
84 int timeleft;
85 int pretimeout;
86
87 uint32_t status;
88 uint32_t bstatus;
89
90 struct watchdog_info ident;
91
92 unsigned int has_timeout : 1,
93 has_timeleft : 1,
94 has_pretimeout : 1;
95 };
96
97 /* converts flag name to flag bit */
98 static long name2bit(const char *name, size_t namesz)
99 {
100 size_t i;
101
102 for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
103 const char *cn = wdflags[i].name;
104 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
105 return wdflags[i].flag;
106 }
107 warnx(_("unknown flag: %s"), name);
108 return -1;
109 }
110
111 static int column2id(const char *name, size_t namesz)
112 {
113 size_t i;
114
115 for (i = 0; i < NCOLS; i++) {
116 const char *cn = infos[i].name;
117 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
118 return i;
119 }
120 warnx(_("unknown column: %s"), name);
121 return -1;
122 }
123
124 static int get_column_id(int num)
125 {
126 assert(ARRAY_SIZE(columns) == NCOLS);
127 assert(num < ncolumns);
128 assert(columns[num] < (int) NCOLS);
129
130 return columns[num];
131 }
132
133 static struct colinfo *get_column_info(unsigned num)
134 {
135 return &infos[ get_column_id(num) ];
136 }
137
138 static void usage(FILE *out)
139 {
140 size_t i;
141
142 fputs(USAGE_HEADER, out);
143 fprintf(out,
144 _(" %s [options] [<device> ...]\n"), program_invocation_short_name);
145
146 fputs(USAGE_OPTIONS, out);
147
148 fputs(_(" -f, --flags <list> print selected flags only\n"
149 " -F, --noflags don't print information about flags\n"
150 " -I, --noident don't print watchdog identity information\n"
151 " -n, --noheadings don't print headings for flags table\n"
152 " -O, --oneline print all information on one line\n"
153 " -o, --output <list> output columns of the flags\n"
154 " -r, --raw use raw output format for flags table\n"
155 " -T, --notimeouts don't print watchdog timeouts\n"
156 " -x, --flags-only print only flags table (same as -I -T)\n"), out);
157
158 fputs(USAGE_SEPARATOR, out);
159 fputs(USAGE_HELP, out);
160 fputs(USAGE_VERSION, out);
161 fputs(USAGE_SEPARATOR, out);
162
163 fprintf(out, _("The default device is %s.\n"), _PATH_WATCHDOG_DEV);
164 fputs(USAGE_SEPARATOR, out);
165
166 fputs(_("Available columns:\n"), out);
167 for (i = 0; i < ARRAY_SIZE(infos); i++)
168 fprintf(out, " %13s %s\n", infos[i].name, _(infos[i].help));
169
170 fprintf(out, USAGE_MAN_TAIL("wdctl(1)"));
171
172 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
173 }
174
175 static void add_flag_line(struct tt *tt, struct wdinfo *wd, const struct wdflag *fl)
176 {
177 int i;
178 struct tt_line *line;
179
180 line = tt_add_line(tt, NULL);
181 if (!line) {
182 warn(_("failed to add line to output"));
183 return;
184 }
185
186 for (i = 0; i < ncolumns; i++) {
187 const char *str = NULL;
188
189 switch (get_column_id(i)) {
190 case COL_FLAG:
191 str = fl->name;
192 break;
193 case COL_DESC:
194 str = fl->description;
195 break;
196 case COL_STATUS:
197 str = wd->status & fl->flag ? "1" : "0";
198 break;
199 case COL_BSTATUS:
200 str = wd->bstatus & fl->flag ? "1" : "0";
201 break;
202 case COL_DEVICE:
203 str = wd->device;
204 break;
205 default:
206 break;
207 }
208
209 if (str)
210 tt_line_set_data(line, i, str);
211 }
212 }
213
214 static int show_flags(struct wdinfo *wd, int tt_flags, uint32_t wanted)
215 {
216 size_t i;
217 int rc = -1;
218 struct tt *tt;
219 uint32_t flags;
220
221 /* create output table */
222 tt = tt_new_table(tt_flags);
223 if (!tt) {
224 warn(_("failed to initialize output table"));
225 return -1;
226 }
227
228 /* define columns */
229 for (i = 0; i < (size_t) ncolumns; i++) {
230 struct colinfo *col = get_column_info(i);
231
232 if (!tt_define_column(tt, col->name, col->whint, col->flags)) {
233 warnx(_("failed to initialize output column"));
234 goto done;
235 }
236 }
237
238 /* fill-in table with data
239 * -- one line for each supported flag (option) */
240 flags = wd->ident.options;
241
242 for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
243 if (wanted && !(wanted & wdflags[i].flag))
244 ; /* ignore */
245 else if (flags & wdflags[i].flag)
246 add_flag_line(tt, wd, &wdflags[i]);
247
248 flags &= ~wdflags[i].flag;
249 }
250
251 if (flags)
252 warnx(_("%s: unknown flags 0x%x\n"), wd->device, flags);
253
254 tt_print_table(tt);
255 rc = 0;
256 done:
257 tt_free_table(tt);
258 return rc;
259 }
260
261 /*
262 * Warning: successfully opened watchdog has to be properly closed with magic
263 * close character otherwise the machine will be rebooted!
264 *
265 * Don't use err() or exit() here!
266 */
267 static int read_watchdog(struct wdinfo *wd)
268 {
269 int fd;
270 sigset_t sigs, oldsigs;
271
272 assert(wd->device);
273
274 sigemptyset(&oldsigs);
275 sigfillset(&sigs);
276 sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
277
278 fd = open(wd->device, O_WRONLY|O_CLOEXEC);
279
280 if (fd < 0) {
281 if (errno == EBUSY)
282 warnx(_("%s: watchdog already in use, terminating."),
283 wd->device);
284 warn(_("%s: open failed"), wd->device);
285 return -1;
286 }
287
288 if (ioctl(fd, WDIOC_GETSUPPORT, &wd->ident) < 0)
289 warn(_("%s: failed to get information about watchdog"), wd->device);
290 else {
291 ioctl(fd, WDIOC_GETSTATUS, &wd->status);
292 ioctl(fd, WDIOC_GETBOOTSTATUS, &wd->bstatus);
293
294 if (ioctl(fd, WDIOC_GETTIMEOUT, &wd->timeout) >= 0)
295 wd->has_timeout = 1;
296 if (ioctl(fd, WDIOC_GETPRETIMEOUT, &wd->pretimeout) >= 0)
297 wd->has_pretimeout = 1;
298 if (ioctl(fd, WDIOC_GETTIMELEFT, &wd->timeleft) >= 0)
299 wd->has_timeleft = 1;
300 }
301
302 for (;;) {
303 /* We just opened this to query the state, not to arm
304 * it hence use the magic close character */
305 static const char v = 'V';
306
307 if (write(fd, &v, 1) >= 0)
308 break;
309 if (errno != EINTR) {
310 warn(_("%s: failed to disarm watchdog"), wd->device);
311 break;
312 }
313 /* Let's try hard, since if we don't get this right
314 * the machine might end up rebooting. */
315 }
316
317 close(fd);
318 sigprocmask(SIG_SETMASK, &oldsigs, NULL);
319
320 return 0;
321 }
322
323 static void print_oneline(struct wdinfo *wd, uint32_t wanted,
324 int noident, int notimeouts, int noflags)
325 {
326 printf("%s:", wd->device);
327
328 if (!noident) {
329 printf(" VERSION=\"%x\"", wd->ident.firmware_version);
330
331 printf(" IDENTITY=");
332 tt_fputs_quoted((char *) wd->ident.identity, stdout);
333 }
334 if (!notimeouts) {
335 if (wd->has_timeout)
336 printf(" TIMEOUT=\"%i\"", wd->timeout);
337 if (wd->has_pretimeout)
338 printf(" PRETIMEOUT=\"%i\"", wd->pretimeout);
339 if (wd->has_timeleft)
340 printf(" TIMELEFT=\"%i\"", wd->timeleft);
341 }
342
343 if (!noflags) {
344 size_t i;
345 uint32_t flags = wd->ident.options;
346
347 for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
348 const struct wdflag *fl;
349
350 if ((wanted && !(wanted & wdflags[i].flag)) ||
351 !(flags & wdflags[i].flag))
352 continue;
353
354 fl= &wdflags[i];
355
356 printf(" %s=\"%s\"", fl->name,
357 wd->status & fl->flag ? "1" : "0");
358 printf(" %s_BOOT=\"%s\"", fl->name,
359 wd->bstatus & fl->flag ? "1" : "0");
360
361 }
362 }
363
364 fputc('\n', stdout);
365 }
366
367 static void show_timeouts(struct wdinfo *wd)
368 {
369 if (wd->has_timeout)
370 printf(_("%-15s%2i seconds\n"), _("Timeout:"), wd->timeout);
371 if (wd->has_pretimeout)
372 printf(_("%-15s%2i seconds\n"), _("Pre-timeout:"), wd->pretimeout);
373 if (wd->has_timeleft)
374 printf(_("%-15s%2i seconds\n"), _("Timeleft:"), wd->timeleft);
375 }
376
377 int main(int argc, char *argv[])
378 {
379 struct wdinfo wd;
380 int c, tt_flags = 0, res = EXIT_SUCCESS, count = 0;
381 char noflags = 0, noident = 0, notimeouts = 0, oneline = 0;
382 uint32_t wanted = 0;
383
384 static const struct option long_opts[] = {
385 { "flags", required_argument, NULL, 'f' },
386 { "flags-only", no_argument, NULL, 'x' },
387 { "help", no_argument, NULL, 'h' },
388 { "noflags", no_argument, NULL, 'F' },
389 { "noheadings", no_argument, NULL, 'n' },
390 { "noident", no_argument, NULL, 'I' },
391 { "notimeouts", no_argument, NULL, 'T' },
392 { "output", required_argument, NULL, 'o' },
393 { "oneline", no_argument, NULL, 'O' },
394 { "raw", no_argument, NULL, 'r' },
395 { "version", no_argument, NULL, 'V' },
396 { NULL, 0, NULL, 0 }
397 };
398
399 setlocale(LC_ALL, "");
400 bindtextdomain(PACKAGE, LOCALEDIR);
401 textdomain(PACKAGE);
402 atexit(close_stdout);
403
404 while ((c = getopt_long(argc, argv,
405 "d:f:hFnITo:OrVx", long_opts, NULL)) != -1) {
406 switch(c) {
407 case 'o':
408 ncolumns = string_to_idarray(optarg,
409 columns, ARRAY_SIZE(columns),
410 column2id);
411 if (ncolumns < 0)
412 return EXIT_FAILURE;
413 break;
414 case 'f':
415 if (string_to_bitmask(optarg, (unsigned long *) &wanted, name2bit) != 0)
416 return EXIT_FAILURE;
417 break;
418 case 'V':
419 printf(UTIL_LINUX_VERSION);
420 return EXIT_SUCCESS;
421 case 'h':
422 usage(stdout);
423 case 'F':
424 noflags = 1;
425 break;
426 case 'I':
427 noident = 1;
428 break;
429 case 'T':
430 notimeouts = 1;
431 break;
432 case 'n':
433 tt_flags |= TT_FL_NOHEADINGS;
434 break;
435 case 'r':
436 tt_flags |= TT_FL_RAW;
437 break;
438 case 'O':
439 oneline = 1;
440 break;
441 case 'x':
442 noident = 1;
443 notimeouts = 1;
444 break;
445
446 case '?':
447 default:
448 usage(stderr);
449 }
450 }
451
452 if (wanted && noflags)
453 errx(EXIT_FAILURE, _("--flags and --noflags are mutually exclusive"));
454
455 if (!ncolumns) {
456 /* default columns */
457 columns[ncolumns++] = COL_FLAG;
458 columns[ncolumns++] = COL_DESC;
459 columns[ncolumns++] = COL_STATUS;
460 columns[ncolumns++] = COL_BSTATUS;
461 }
462
463 do {
464 int rc;
465
466 memset(&wd, 0, sizeof(wd));
467
468 if (optind == argc)
469 wd.device = _PATH_WATCHDOG_DEV;
470 else
471 wd.device = argv[optind++];
472
473 if (count)
474 fputc('\n', stdout);
475 count++;
476
477 rc = read_watchdog(&wd);
478 if (rc) {
479 res = EXIT_FAILURE;
480 continue;
481 }
482
483 if (oneline) {
484 print_oneline(&wd, wanted, noident, notimeouts, noflags);
485 continue;
486 }
487
488 /* pretty output */
489 if (!noident) {
490 printf("%-15s%s\n", _("Device:"), wd.device);
491 printf(_("%-15s%s [version %x]\n"),
492 ("Identity:"),
493 wd.ident.identity,
494 wd.ident.firmware_version);
495 }
496 if (!notimeouts)
497 show_timeouts(&wd);
498 if (!noflags)
499 show_flags(&wd, tt_flags, wanted);
500 } while (optind < argc);
501
502 return res;
503 }