]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/rfkill.c
Make the ways of using output stream consistent in usage()
[thirdparty/util-linux.git] / sys-utils / rfkill.c
1 /*
2 * /dev/rfkill userspace tool
3 *
4 * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
5 * Copyright 2009 Marcel Holtmann <marcel@holtmann.org>
6 * Copyright 2009 Tim Gardner <tim.gardner@canonical.com>
7 * Copyright 2017 Sami Kerola <kerolasa@iki.fi>
8 * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
9 *
10 * Permission to use, copy, modify, and/or distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23 #include <ctype.h>
24 #include <getopt.h>
25 #include <libsmartcols.h>
26 #include <linux/rfkill.h>
27 #include <poll.h>
28 #include <sys/syslog.h>
29 #include <sys/time.h>
30
31 #include "c.h"
32 #include "closestream.h"
33 #include "nls.h"
34 #include "optutils.h"
35 #include "pathnames.h"
36 #include "strutils.h"
37 #include "timeutils.h"
38 #include "widechar.h"
39 #include "xalloc.h"
40 #include "all-io.h"
41
42
43 /*
44 * NFC supported by kernel since v3.10 (year 2013); FM and another types are from
45 * year 2009 (2.6.33) or older.
46 */
47 #ifndef RFKILL_TYPE_NFC
48 # ifndef RFKILL_TYPE_FM
49 # define RFKILL_TYPE_FM RFKILL_TYPE_GPS + 1
50 # endif
51 # define RFKILL_TYPE_NFC RFKILL_TYPE_FM + 1
52 # undef NUM_RFKILL_TYPES
53 # define NUM_RFKILL_TYPES RFKILL_TYPE_NFC + 1
54 #endif
55
56 struct rfkill_type_str {
57 enum rfkill_type type; /* ID */
58 const char *name; /* generic name */
59 const char *desc; /* human readable name */
60 };
61
62 static const struct rfkill_type_str rfkill_type_strings[] = {
63 { .type = RFKILL_TYPE_ALL, .name = "all" },
64 { .type = RFKILL_TYPE_WLAN, .name = "wlan", .desc = "Wireless LAN" },
65 { .type = RFKILL_TYPE_WLAN, .name = "wifi" }, /* alias */
66 { .type = RFKILL_TYPE_BLUETOOTH, .name = "bluetooth", .desc = "Bluetooth" },
67 { .type = RFKILL_TYPE_UWB, .name = "uwb", .desc = "Ultra-Wideband" },
68 { .type = RFKILL_TYPE_UWB, .name = "ultrawideband" }, /* alias */
69 { .type = RFKILL_TYPE_WIMAX, .name = "wimax", .desc = "WiMAX" },
70 { .type = RFKILL_TYPE_WWAN, .name = "wwan", .desc = "Wireless WAN" },
71 { .type = RFKILL_TYPE_GPS, .name = "gps", .desc = "GPS" },
72 { .type = RFKILL_TYPE_FM, .name = "fm", .desc = "FM" },
73 { .type = RFKILL_TYPE_NFC, .name = "nfc", .desc = "NFC" },
74 { .type = NUM_RFKILL_TYPES, .name = NULL }
75 };
76
77 struct rfkill_id {
78 union {
79 enum rfkill_type type;
80 uint32_t index;
81 };
82 enum {
83 RFKILL_IS_INVALID,
84 RFKILL_IS_TYPE,
85 RFKILL_IS_INDEX,
86 RFKILL_IS_ALL
87 } result;
88 };
89
90 /* supported actions */
91 enum {
92 ACT_LIST,
93 ACT_HELP,
94 ACT_EVENT,
95 ACT_BLOCK,
96 ACT_UNBLOCK,
97 ACT_TOGGLE,
98
99 ACT_LIST_OLD
100 };
101
102 static char *rfkill_actions[] = {
103 [ACT_LIST] = "list",
104 [ACT_HELP] = "help",
105 [ACT_EVENT] = "event",
106 [ACT_BLOCK] = "block",
107 [ACT_UNBLOCK] = "unblock",
108 [ACT_TOGGLE] = "toggle"
109 };
110
111 /* column IDs */
112 enum {
113 COL_DEVICE,
114 COL_ID,
115 COL_TYPE,
116 COL_DESC,
117 COL_SOFT,
118 COL_HARD
119 };
120
121 /* column names */
122 struct colinfo {
123 const char *name; /* header */
124 double whint; /* width hint (N < 1 is in percent of termwidth) */
125 int flags; /* SCOLS_FL_* */
126 const char *help;
127 };
128
129 /* columns descriptions */
130 static const struct colinfo infos[] = {
131 [COL_DEVICE] = {"DEVICE", 0, 0, N_("kernel device name")},
132 [COL_ID] = {"ID", 2, SCOLS_FL_RIGHT, N_("device identifier value")},
133 [COL_TYPE] = {"TYPE", 0, 0, N_("device type name that can be used as identifier")},
134 [COL_DESC] = {"TYPE-DESC", 0, 0, N_("device type description")},
135 [COL_SOFT] = {"SOFT", 0, SCOLS_FL_RIGHT, N_("status of software block")},
136 [COL_HARD] = {"HARD", 0, SCOLS_FL_RIGHT, N_("status of hardware block")}
137 };
138
139 static int columns[ARRAY_SIZE(infos) * 2];
140 static size_t ncolumns;
141
142 struct control {
143 struct libscols_table *tb;
144 unsigned int
145 json:1,
146 no_headings:1,
147 raw:1;
148 };
149
150 static int column_name_to_id(const char *name, size_t namesz)
151 {
152 size_t i;
153
154 assert(name);
155
156 for (i = 0; i < ARRAY_SIZE(infos); i++) {
157 const char *cn = infos[i].name;
158
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(size_t num)
167 {
168 assert(num < ncolumns);
169 assert(columns[num] < (int)ARRAY_SIZE(infos));
170 return columns[num];
171 }
172
173 static const struct colinfo *get_column_info(int num)
174 {
175 return &infos[get_column_id(num)];
176 }
177
178 static int string_to_action(const char *str)
179 {
180 size_t i;
181
182 for (i = 0; i < ARRAY_SIZE(rfkill_actions); i++)
183 if (strcmp(str, rfkill_actions[i]) == 0)
184 return i;
185
186 return -EINVAL;
187 }
188
189 static int rfkill_open(int rdonly, int nonblock)
190 {
191 int fd;
192
193 fd = open(_PATH_DEV_RFKILL, rdonly ? O_RDONLY : O_RDWR);
194 if (fd < 0) {
195 warn(_("cannot open %s"), _PATH_DEV_RFKILL);
196 return -errno;
197 }
198
199 if (nonblock && fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
200 warn(_("cannot set non-blocking %s"), _PATH_DEV_RFKILL);
201 close(fd);
202 return -errno;
203 }
204
205 return fd;
206 }
207
208 /* returns: 0 success, 1 read again, < 0 error */
209 static int rfkill_read_event(int fd, struct rfkill_event *event)
210 {
211 ssize_t len = read(fd, event, sizeof(*event));
212
213 if (len < 0) {
214 if (errno == EAGAIN)
215 return 1;
216 warn(_("cannot read %s"), _PATH_DEV_RFKILL);
217 return -errno;
218 }
219
220 if ((size_t) len < (size_t) RFKILL_EVENT_SIZE_V1) {
221 warnx(_("wrong size of rfkill event: %zu < %zu"),
222 (size_t) len, (size_t) RFKILL_EVENT_SIZE_V1);
223 return 1;
224 }
225
226 return 0;
227 }
228
229
230 static int rfkill_event(void)
231 {
232 enum {
233 POLLFD_RFKILL,
234 POLLFD_STDOUT,
235 POLLFD_COUNT
236 };
237 struct rfkill_event event;
238 struct timeval tv;
239 char date_buf[ISO_BUFSIZ];
240 struct pollfd p[POLLFD_COUNT];
241 int fd, n;
242
243 fd = rfkill_open(1, 0);
244 if (fd < 0)
245 return fd;
246
247 memset(&p, 0, sizeof(p));
248 p[POLLFD_RFKILL].fd = fd;
249 p[POLLFD_RFKILL].events = POLLIN | POLLHUP;
250 p[POLLFD_STDOUT].fd = STDOUT_FILENO;
251 p[POLLFD_STDOUT].events = 0;
252
253 /* interrupted by signal only */
254 while (1) {
255 int rc = 1; /* recover-able error */
256
257 n = poll(p, ARRAY_SIZE(p), -1);
258 if (n < 0) {
259 warn(_("failed to poll %s"), _PATH_DEV_RFKILL);
260 goto failed;
261 }
262
263 if (p[POLLFD_STDOUT].revents)
264 goto failed; /* read end of stdout closed */
265 if (p[POLLFD_RFKILL].revents)
266 rc = rfkill_read_event(fd, &event);
267 if (rc < 0)
268 goto failed;
269 if (rc)
270 continue;
271
272 gettimeofday(&tv, NULL);
273 strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA, date_buf,
274 sizeof(date_buf));
275 printf("%s: idx %u type %u op %u soft %u hard %u\n",
276 date_buf,
277 event.idx, event.type, event.op, event.soft, event.hard);
278 fflush(stdout);
279 }
280
281 failed:
282 close(fd);
283 return -1;
284 }
285
286 static const char *get_sys_attr(uint32_t idx, const char *attr)
287 {
288 static char name[128];
289 char path[PATH_MAX];
290 FILE *f;
291 char *p;
292
293 snprintf(path, sizeof(path), _PATH_SYS_RFKILL "/rfkill%u/%s", idx, attr);
294 f = fopen(path, "r");
295 if (!f)
296 goto done;
297 if (!fgets(name, sizeof(name), f))
298 goto done;
299 p = strchr(name, '\n');
300 if (p)
301 *p = '\0';
302 done:
303 if (f)
304 fclose(f);
305 return name;
306 }
307
308 static struct rfkill_id rfkill_id_to_type(const char *s)
309 {
310 const struct rfkill_type_str *p;
311 struct rfkill_id ret = { .result = 0 };
312
313 if (islower(*s)) {
314 for (p = rfkill_type_strings; p->name != NULL; p++) {
315 if (!strcmp(s, p->name)) {
316 ret.type = p->type;
317 if (!strcmp(s, "all"))
318 ret.result = RFKILL_IS_ALL;
319 else
320 ret.result = RFKILL_IS_TYPE;
321 return ret;
322 }
323 }
324 } else if (isdigit(*s)) {
325 /* assume a numeric character implies an index. */
326 char filename[64];
327
328 ret.index = strtou32_or_err(s, _("invalid identifier"));
329 snprintf(filename, sizeof(filename) - 1,
330 _PATH_SYS_RFKILL "/rfkill%" PRIu32 "/name", ret.index);
331 if (access(filename, F_OK) == 0)
332 ret.result = RFKILL_IS_INDEX;
333 else
334 ret.result = RFKILL_IS_INVALID;
335 return ret;
336 }
337
338 ret.result = RFKILL_IS_INVALID;
339 return ret;
340 }
341
342 static const char *rfkill_type_to_desc(enum rfkill_type type)
343 {
344 size_t i;
345
346 for (i = 0; i < ARRAY_SIZE(rfkill_type_strings); i++) {
347 if (type == rfkill_type_strings[i].type)
348 return rfkill_type_strings[i].desc;
349 }
350
351 return NULL;
352 }
353
354
355 static int event_match(struct rfkill_event *event, struct rfkill_id *id)
356 {
357 if (event->op != RFKILL_OP_ADD)
358 return 0;
359
360 /* filter out unwanted results */
361 switch (id->result) {
362 case RFKILL_IS_TYPE:
363 if (event->type != id->type)
364 return 0;
365 break;
366 case RFKILL_IS_INDEX:
367 if (event->idx != id->index)
368 return 0;
369 break;
370 case RFKILL_IS_ALL:
371 break;
372 default:
373 abort();
374 }
375
376 return 1;
377 }
378
379 static void fill_table_row(struct libscols_table *tb, struct rfkill_event *event)
380 {
381 static struct libscols_line *ln;
382 size_t i;
383
384 assert(tb);
385
386 ln = scols_table_new_line(tb, NULL);
387 if (!ln) {
388 errno = ENOMEM;
389 errx(EXIT_FAILURE, _("failed to allocate output line"));
390 }
391
392 for (i = 0; i < (size_t)ncolumns; i++) {
393 char *str = NULL;
394 switch (get_column_id(i)) {
395 case COL_DEVICE:
396 str = xstrdup(get_sys_attr(event->idx, "name"));
397 break;
398 case COL_ID:
399 xasprintf(&str, "%" PRIu32, event->idx);
400 break;
401 case COL_TYPE:
402 str = xstrdup(get_sys_attr(event->idx, "type"));
403 break;
404 case COL_DESC:
405 str = xstrdup(rfkill_type_to_desc(event->type));
406 break;
407 case COL_SOFT:
408 str = xstrdup(event->soft ? _("blocked") : _("unblocked"));
409 break;
410 case COL_HARD:
411 str = xstrdup(event->hard ? _("blocked") : _("unblocked"));
412 break;
413 default:
414 abort();
415 }
416 if (str && scols_line_refer_data(ln, i, str))
417 errx(EXIT_FAILURE, _("failed to add output data"));
418 }
419 }
420
421 static int rfkill_list_old(const char *param)
422 {
423 struct rfkill_id id = { .result = RFKILL_IS_ALL };
424 struct rfkill_event event;
425 int fd, rc = 0;
426
427 if (param) {
428 id = rfkill_id_to_type(param);
429 if (id.result == RFKILL_IS_INVALID) {
430 warnx(_("invalid identifier: %s"), param);
431 return -EINVAL;
432 }
433 }
434
435 fd = rfkill_open(1, 1);
436 if (fd < 0)
437 return fd;
438
439 while (1) {
440 rc = rfkill_read_event(fd, &event);
441 if (rc < 0)
442 break;
443 if (rc == 1 && errno == EAGAIN) {
444 rc = 0; /* done */
445 break;
446 }
447 if (rc == 0 && event_match(&event, &id)) {
448 char *name = xstrdup(get_sys_attr(event.idx, "name")),
449 *type = xstrdup(rfkill_type_to_desc(event.type));
450
451 if (!type)
452 type = xstrdup(get_sys_attr(event.idx, "type"));
453
454 printf("%u: %s: %s\n", event.idx, name, type);
455 printf("\tSoft blocked: %s\n", event.soft ? "yes" : "no");
456 printf("\tHard blocked: %s\n", event.hard ? "yes" : "no");
457
458 free(name);
459 free(type);
460 }
461 }
462 close(fd);
463 return rc;
464 }
465
466 static void rfkill_list_init(struct control *ctrl)
467 {
468 size_t i;
469
470 scols_init_debug(0);
471
472 ctrl->tb = scols_new_table();
473 if (!ctrl->tb)
474 err(EXIT_FAILURE, _("failed to allocate output table"));
475
476 scols_table_enable_json(ctrl->tb, ctrl->json);
477 scols_table_set_name(ctrl->tb, "rfkilldevices");
478 scols_table_enable_noheadings(ctrl->tb, ctrl->no_headings);
479 scols_table_enable_raw(ctrl->tb, ctrl->raw);
480
481 for (i = 0; i < (size_t) ncolumns; i++) {
482 const struct colinfo *col = get_column_info(i);
483 struct libscols_column *cl;
484
485 cl = scols_table_new_column(ctrl->tb, col->name, col->whint, col->flags);
486 if (!cl)
487 err(EXIT_FAILURE, _("failed to allocate output column"));
488 if (ctrl->json) {
489 int id = get_column_id(i);
490 if (id == COL_ID)
491 scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
492 }
493 }
494 }
495
496 static int rfkill_list_fill(struct control const *ctrl, const char *param)
497 {
498 struct rfkill_id id = { .result = RFKILL_IS_ALL };
499 struct rfkill_event event;
500 int fd, rc = 0;
501
502 if (param) {
503 id = rfkill_id_to_type(param);
504 if (id.result == RFKILL_IS_INVALID) {
505 warnx(_("invalid identifier: %s"), param);
506 return -EINVAL;
507 }
508 }
509
510 fd = rfkill_open(1, 1);
511 if (fd < 0)
512 return fd;
513
514 while (1) {
515 rc = rfkill_read_event(fd, &event);
516 if (rc < 0)
517 break;
518 if (rc == 1 && errno == EAGAIN) {
519 rc = 0; /* done */
520 break;
521 }
522 if (rc == 0 && event_match(&event, &id))
523 fill_table_row(ctrl->tb, &event);
524 }
525 close(fd);
526 return rc;
527 }
528
529 static void rfkill_list_output(struct control const *ctrl)
530 {
531 scols_print_table(ctrl->tb);
532 scols_unref_table(ctrl->tb);
533 }
534
535 static int __rfkill_block(int fd, struct rfkill_id *id, uint8_t block, const char *param)
536 {
537 struct rfkill_event event = {
538 .op = RFKILL_OP_CHANGE_ALL,
539 .soft = block,
540 0
541 };
542 char *message = NULL;
543
544 switch (id->result) {
545 case RFKILL_IS_INVALID:
546 warnx(_("invalid identifier: %s"), param);
547 return -1;
548 case RFKILL_IS_TYPE:
549 event.type = id->type;
550 xasprintf(&message, "type %s", param);
551 break;
552 case RFKILL_IS_INDEX:
553 event.op = RFKILL_OP_CHANGE;
554 event.idx = id->index;
555 xasprintf(&message, "id %d", id->index);
556 break;
557 case RFKILL_IS_ALL:
558 message = xstrdup("all");
559 break;
560 default:
561 abort();
562 }
563
564 if (write_all(fd, &event, sizeof(event)) != 0)
565 warn(_("write failed: %s"), _PATH_DEV_RFKILL);
566 else {
567 openlog("rfkill", 0, LOG_USER);
568 syslog(LOG_NOTICE, "%s set for %s", block ? "block" : "unblock", message);
569 closelog();
570 }
571 free(message);
572 return 0;
573 }
574
575 static int rfkill_block(uint8_t block, const char *param)
576 {
577 struct rfkill_id id;
578 int fd;
579
580 id = rfkill_id_to_type(param);
581 if (id.result == RFKILL_IS_INVALID) {
582 warnx(_("invalid identifier: %s"), param);
583 return -EINVAL;
584 }
585
586 fd = rfkill_open(0, 0);
587 if (fd < 0)
588 return fd;
589
590 __rfkill_block(fd, &id, block, param);
591
592 return close(fd);
593 }
594
595 static int rfkill_toggle(const char *param)
596 {
597 struct rfkill_id id = { .result = RFKILL_IS_ALL };
598 struct rfkill_event event;
599 int fd, rc = 0;
600
601 id = rfkill_id_to_type(param);
602 if (id.result == RFKILL_IS_INVALID) {
603 warnx(_("invalid identifier: %s"), param);
604 return -EINVAL;
605 }
606
607 fd = rfkill_open(0, 1);
608 if (fd < 0)
609 return fd;
610
611 while (1) {
612 rc = rfkill_read_event(fd, &event);
613 if (rc < 0)
614 break;
615 if (rc == 1 && errno == EAGAIN) {
616 rc = 0; /* done */
617 break;
618 }
619 if (rc == 0 && event_match(&event, &id))
620 __rfkill_block(fd, &id, event.soft ? 0 : 1, param);
621 }
622
623 close(fd);
624 return rc;
625 }
626
627
628 static void __attribute__((__noreturn__)) usage(void)
629 {
630 size_t i;
631
632 fputs(USAGE_HEADER, stdout);
633 fprintf(stdout, _(" %s [options] command [identifier ...]\n"), program_invocation_short_name);
634
635 fputs(USAGE_SEPARATOR, stdout);
636 fputs(_("Tool for enabling and disabling wireless devices.\n"), stdout);
637
638 fputs(USAGE_OPTIONS, stdout);
639 fputs(_(" -J, --json use JSON output format\n"), stdout);
640 fputs(_(" -n, --noheadings don't print headings\n"), stdout);
641 fputs(_(" -o, --output <list> define which output columns to use\n"), stdout);
642 fputs(_(" --output-all output all columns\n"), stdout);
643 fputs(_(" -r, --raw use the raw output format\n"), stdout);
644
645 fputs(USAGE_SEPARATOR, stdout);
646 fprintf(stdout, USAGE_HELP_OPTIONS(24));
647
648 fputs(USAGE_COLUMNS, stdout);
649 for (i = 0; i < ARRAY_SIZE(infos); i++)
650 fprintf(stdout, " %-10s %s\n", infos[i].name, _(infos[i].help));
651
652 fputs(USAGE_COMMANDS, stdout);
653
654 /*
655 * TRANSLATORS: command names should not be translated, explaining
656 * them as additional field after identifier is fine, for example
657 *
658 * list [identifier] (lista [tarkenne])
659 */
660 fputs(_(" help\n"), stdout);
661 fputs(_(" event\n"), stdout);
662 fputs(_(" list [identifier]\n"), stdout);
663 fputs(_(" block identifier\n"), stdout);
664 fputs(_(" unblock identifier\n"), stdout);
665 fputs(_(" toggle identifier\n"), stdout);
666
667 fprintf(stdout, USAGE_MAN_TAIL("rfkill(8)"));
668 exit(EXIT_SUCCESS);
669 }
670
671 int main(int argc, char **argv)
672 {
673 struct control ctrl = { 0 };
674 int c, act = ACT_LIST, list_all = 0;
675 char *outarg = NULL;
676 enum {
677 OPT_LIST_TYPES = CHAR_MAX + 1
678 };
679 static const struct option longopts[] = {
680 { "json", no_argument, NULL, 'J' },
681 { "noheadings", no_argument, NULL, 'n' },
682 { "output", required_argument, NULL, 'o' },
683 { "output-all", no_argument, NULL, OPT_LIST_TYPES },
684 { "raw", no_argument, NULL, 'r' },
685 { "version", no_argument, NULL, 'V' },
686 { "help", no_argument, NULL, 'h' },
687 { NULL, 0, NULL, 0 }
688 };
689 static const ul_excl_t excl[] = {
690 {'J', 'r'},
691 {0}
692 };
693 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
694 int ret = 0;
695
696 setlocale(LC_ALL, "");
697 bindtextdomain(PACKAGE, LOCALEDIR);
698 textdomain(PACKAGE);
699 close_stdout_atexit();
700
701 while ((c = getopt_long(argc, argv, "Jno:rVh", longopts, NULL)) != -1) {
702 err_exclusive_options(c, longopts, excl, excl_st);
703 switch (c) {
704 case 'J':
705 ctrl.json = 1;
706 break;
707 case 'n':
708 ctrl.no_headings = 1;
709 break;
710 case 'o':
711 outarg = optarg;
712 break;
713 case OPT_LIST_TYPES:
714 list_all = 1;
715 break;
716 case 'r':
717 ctrl.raw = 1;
718 break;
719
720 case 'V':
721 print_version(EXIT_SUCCESS);
722 case 'h':
723 usage();
724 default:
725 errtryhelp(EXIT_FAILURE);
726 }
727 }
728 argc -= optind;
729 argv += optind;
730
731 if (argc > 0) {
732 act = string_to_action(*argv);
733 if (act < 0)
734 errtryhelp(EXIT_FAILURE);
735 argv++;
736 argc--;
737
738 /*
739 * For backward compatibility we use old output format if
740 * "list" explicitly specified and --output not defined.
741 */
742 if (!outarg && act == ACT_LIST)
743 act = ACT_LIST_OLD;
744 }
745
746 switch (act) {
747 case ACT_LIST_OLD:
748 /* Deprecated in favour of ACT_LIST */
749 if (!argc)
750 ret |= rfkill_list_old(NULL); /* ALL */
751 else while (argc) {
752 ret |= rfkill_list_old(*argv);
753 argc--;
754 argv++;
755 }
756 break;
757
758 case ACT_LIST:
759 columns[ncolumns++] = COL_ID;
760 columns[ncolumns++] = COL_TYPE;
761 columns[ncolumns++] = COL_DEVICE;
762 if (list_all)
763 columns[ncolumns++] = COL_DESC;
764 columns[ncolumns++] = COL_SOFT;
765 columns[ncolumns++] = COL_HARD;
766
767 if (outarg
768 && string_add_to_idarray(outarg, columns,
769 ARRAY_SIZE(columns), &ncolumns,
770 column_name_to_id) < 0)
771 return EXIT_FAILURE;
772
773 rfkill_list_init(&ctrl);
774 if (!argc)
775 ret |= rfkill_list_fill(&ctrl, NULL); /* ALL */
776 else while (argc) {
777 ret |= rfkill_list_fill(&ctrl, *argv);
778 argc--;
779 argv++;
780 }
781 rfkill_list_output(&ctrl);
782 break;
783
784 case ACT_EVENT:
785 ret = rfkill_event();
786 break;
787
788 case ACT_HELP:
789 usage();
790 break;
791
792 case ACT_BLOCK:
793 while (argc) {
794 ret |= rfkill_block(1, *argv);
795 argc--;
796 argv++;
797 }
798 break;
799
800 case ACT_UNBLOCK:
801 while (argc) {
802 ret |= rfkill_block(0, *argv);
803 argv++;
804 argc--;
805 }
806 break;
807
808 case ACT_TOGGLE:
809 while (argc) {
810 ret |= rfkill_toggle(*argv);
811 argv++;
812 argc--;
813 }
814 break;
815 }
816
817 return ret ? EXIT_FAILURE : EXIT_SUCCESS;
818 }