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