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