]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/rfkill.c
docs: update year in libs docs
[thirdparty/util-linux.git] / sys-utils / rfkill.c
CommitLineData
d5fad6eb 1/*
e69263c0
SK
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>
f4949fcc 7 * Copyright 2017 Sami Kerola <kerolasa@iki.fi>
7d2a9960 8 * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
e69263c0
SK
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.
d5fad6eb
JB
21 */
22
b1849b89 23#include <ctype.h>
367f4080 24#include <getopt.h>
b3849c66 25#include <libsmartcols.h>
367f4080 26#include <linux/rfkill.h>
74ccba3e 27#include <poll.h>
fd3d4ec7 28#include <sys/syslog.h>
6217bea3 29#include <sys/time.h>
d5fad6eb 30
c0d78c90
SK
31#include "c.h"
32#include "closestream.h"
33#include "nls.h"
b3849c66 34#include "optutils.h"
367f4080
SK
35#include "pathnames.h"
36#include "strutils.h"
7d0b3acb 37#include "timeutils.h"
367f4080 38#include "widechar.h"
b3849c66 39#include "xalloc.h"
367f4080 40
ea05c23a
KZ
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
40defd0c
RM
47# ifndef RFKILL_TYPE_FM
48# define RFKILL_TYPE_FM RFKILL_TYPE_GPS + 1
49# endif
ea05c23a 50# define RFKILL_TYPE_NFC RFKILL_TYPE_FM + 1
40defd0c
RM
51# undef NUM_RFKILL_TYPES
52# define NUM_RFKILL_TYPES RFKILL_TYPE_NFC + 1
ea05c23a
KZ
53#endif
54
367f4080 55struct rfkill_type_str {
7d2a9960
KZ
56 enum rfkill_type type; /* ID */
57 const char *name; /* generic name */
58 const char *desc; /* human readable name */
367f4080
SK
59};
60
61static const struct rfkill_type_str rfkill_type_strings[] = {
62 { .type = RFKILL_TYPE_ALL, .name = "all" },
7d2a9960
KZ
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" },
367f4080 67 { .type = RFKILL_TYPE_UWB, .name = "ultrawideband" }, /* alias */
7d2a9960
KZ
68 { .type = RFKILL_TYPE_WIMAX, .name = "wimax", .desc = "WiMAX" },
69 { .type = RFKILL_TYPE_WWAN, .name = "wwan", .desc = "Wireless WAN" },
9e33cddd 70 { .type = RFKILL_TYPE_GPS, .name = "gps", .desc = "GPS" },
7d2a9960
KZ
71 { .type = RFKILL_TYPE_FM, .name = "fm", .desc = "FM" },
72 { .type = RFKILL_TYPE_NFC, .name = "nfc", .desc = "NFC" },
367f4080
SK
73 { .type = NUM_RFKILL_TYPES, .name = NULL }
74};
75
76struct 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,
b03378e7 85 RFKILL_IS_ALL
367f4080
SK
86 } result;
87};
d5fad6eb 88
18d3a03f
KZ
89/* supported actions */
90enum {
91 ACT_LIST,
92 ACT_HELP,
93 ACT_EVENT,
94 ACT_BLOCK,
7d2a9960
KZ
95 ACT_UNBLOCK,
96
97 ACT_LIST_OLD
18d3a03f
KZ
98};
99
100static 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
b3849c66
SK
108/* column IDs */
109enum {
110 COL_DEVICE,
111 COL_ID,
112 COL_TYPE,
7d2a9960 113 COL_DESC,
b3849c66
SK
114 COL_SOFT,
115 COL_HARD
116};
117
118/* column names */
119struct 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 */
127static const struct colinfo infos[] = {
128 [COL_DEVICE] = {"DEVICE", 0, 0, N_("kernel device name")},
7d8e3bcd 129 [COL_ID] = {"ID", 2, SCOLS_FL_RIGHT, N_("device identifier value")},
b3849c66 130 [COL_TYPE] = {"TYPE", 0, 0, N_("device type name that can be used as identifier")},
7d2a9960 131 [COL_DESC] = {"TYPE-DESC", 0, 0, N_("device type description")},
7d8e3bcd
KZ
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")}
b3849c66
SK
134};
135
136static int columns[ARRAY_SIZE(infos) * 2];
137static size_t ncolumns;
138
139struct control {
4f892d77 140 struct libscols_table *tb;
b3849c66
SK
141 unsigned int
142 json:1,
143 no_headings:1,
144 raw:1;
145};
146
147static 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
163static 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
170static const struct colinfo *get_column_info(int num)
171{
172 return &infos[get_column_id(num)];
173}
174
18d3a03f
KZ
175static 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
49f4f7b6
KZ
186static 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 */
206static int rfkill_read_event(int fd, struct rfkill_event *event)
207{
dec4fae2 208 ssize_t len = read(fd, event, sizeof(*event));
49f4f7b6
KZ
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
367f4080 226static int rfkill_event(void)
07d517c0 227{
51da1093 228 struct rfkill_event event;
6217bea3 229 struct timeval tv;
4111bb3a 230 char date_buf[ISO_BUFSIZ];
07d517c0 231 struct pollfd p;
3c5022c6 232 int fd, n;
07d517c0 233
49f4f7b6
KZ
234 fd = rfkill_ro_open(0);
235 if (fd < 0)
3c5022c6 236 return -errno;
07d517c0
MH
237
238 memset(&p, 0, sizeof(p));
239 p.fd = fd;
240 p.events = POLLIN | POLLHUP;
241
3c5022c6 242 /* interrupted by signal only */
07d517c0 243 while (1) {
49f4f7b6
KZ
244 int rc = 1; /* recover-able error */
245
07d517c0
MH
246 n = poll(&p, 1, -1);
247 if (n < 0) {
367f4080 248 warn(_("failed to poll %s"), _PATH_DEV_RFKILL);
3c5022c6 249 goto failed;
07d517c0
MH
250 }
251
49f4f7b6
KZ
252 if (n)
253 rc = rfkill_read_event(fd, &event);
254 if (rc < 0)
3c5022c6 255 goto failed;
49f4f7b6 256 if (rc)
07d517c0 257 continue;
49f4f7b6 258
6217bea3 259 gettimeofday(&tv, NULL);
4111bb3a
WP
260 strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA, date_buf,
261 sizeof(date_buf));
7d0b3acb
SK
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);
1634aef2 265 fflush(stdout);
07d517c0
MH
266 }
267
3c5022c6 268failed:
07d517c0 269 close(fd);
3c5022c6 270 return -1;
07d517c0
MH
271}
272
7d2a9960 273static const char *get_sys_attr(uint32_t idx, const char *attr)
07d517c0 274{
7d2a9960 275 static char name[128];
f99148d6
KZ
276 char path[PATH_MAX];
277 FILE *f;
7d2a9960
KZ
278 char *p;
279
f99148d6
KZ
280 snprintf(path, sizeof(path), _PATH_SYS_RFKILL "/rfkill%u/%s", idx, attr);
281 f = fopen(path, "r");
7d2a9960
KZ
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';
289done:
290 if (f)
291 fclose(f);
07d517c0
MH
292 return name;
293}
294
fafd9121
DS
295static 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++) {
367f4080 302 if (!strcmp(s, p->name)) {
fafd9121 303 ret.type = p->type;
b03378e7
SK
304 if (!strcmp(s, "all"))
305 ret.result = RFKILL_IS_ALL;
306 else
307 ret.result = RFKILL_IS_TYPE;
fafd9121
DS
308 return ret;
309 }
310 }
311 } else if (isdigit(*s)) {
312 /* assume a numeric character implies an index. */
f806a238
SK
313 char filename[64];
314
367f4080 315 ret.index = strtou32_or_err(s, _("invalid identifier"));
f806a238
SK
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;
fafd9121
DS
322 return ret;
323 }
324
325 ret.result = RFKILL_IS_INVALID;
326 return ret;
327}
328
7d2a9960
KZ
329static 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
49f4f7b6
KZ
342static 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
b3849c66
SK
366static 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:
7d2a9960 383 str = xstrdup(get_sys_attr(event->idx, "name"));
b3849c66
SK
384 break;
385 case COL_ID:
386 xasprintf(&str, "%" PRIu32, event->idx);
387 break;
388 case COL_TYPE:
7d2a9960
KZ
389 str = xstrdup(get_sys_attr(event->idx, "type"));
390 break;
391 case COL_DESC:
392 str = xstrdup(rfkill_type_to_desc(event->type));
b3849c66
SK
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
7d2a9960
KZ
408static 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
4f892d77 451static void rfkill_list_init(struct control *ctrl)
07d517c0 452{
e8f6060f
KZ
453 size_t i;
454
b3849c66 455 scols_init_debug(0);
e8f6060f 456
4f892d77
SK
457 ctrl->tb = scols_new_table();
458 if (!ctrl->tb)
b3849c66
SK
459 err(EXIT_FAILURE, _("failed to allocate output table"));
460
4f892d77
SK
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);
b3849c66 464
e8f6060f
KZ
465 for (i = 0; i < (size_t) ncolumns; i++) {
466 const struct colinfo *col = get_column_info(i);
2d6d789b 467 struct libscols_column *cl;
b3849c66 468
2d6d789b
KZ
469 cl = scols_table_new_column(ctrl->tb, col->name, col->whint, col->flags);
470 if (!cl)
e8f6060f 471 err(EXIT_FAILURE, _("failed to allocate output column"));
2d6d789b
KZ
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 }
b3849c66 477 }
4f892d77
SK
478}
479
4f892d77
SK
480static 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;
49f4f7b6 484 int fd, rc = 0;
07d517c0 485
a9405c24
DS
486 if (param) {
487 id = rfkill_id_to_type(param);
488 if (id.result == RFKILL_IS_INVALID) {
367f4080 489 warnx(_("invalid identifier: %s"), param);
3c5022c6 490 return -EINVAL;
a9405c24 491 }
a9405c24
DS
492 }
493
49f4f7b6 494 fd = rfkill_ro_open(1);
07d517c0
MH
495
496 while (1) {
49f4f7b6
KZ
497 rc = rfkill_read_event(fd, &event);
498 if (rc < 0)
a9405c24 499 break;
49f4f7b6
KZ
500 if (rc == 1 && errno == EAGAIN) {
501 rc = 0; /* done */
a9405c24 502 break;
a9405c24 503 }
49f4f7b6
KZ
504 if (rc == 0 && event_match(&event, &id))
505 fill_table_row(ctrl->tb, &event);
07d517c0 506 }
07d517c0 507 close(fd);
49f4f7b6 508 return rc;
07d517c0
MH
509}
510
4f892d77
SK
511static void rfkill_list_output(struct control const *ctrl)
512{
513 scols_print_table(ctrl->tb);
514 scols_unref_table(ctrl->tb);
515}
516
367f4080 517static int rfkill_block(uint8_t block, const char *param)
07d517c0 518{
fafd9121 519 struct rfkill_id id;
b03378e7
SK
520 struct rfkill_event event = {
521 .op = RFKILL_OP_CHANGE_ALL,
522 .soft = block,
523 0
524 };
07d517c0
MH
525 ssize_t len;
526 int fd;
fd3d4ec7 527 char *message = NULL;
07d517c0 528
fafd9121 529 id = rfkill_id_to_type(param);
07d517c0 530
fafd9121 531 switch (id.result) {
b03378e7
SK
532 case RFKILL_IS_INVALID:
533 warnx(_("invalid identifier: %s"), param);
3c5022c6 534 return -1;
fafd9121 535 case RFKILL_IS_TYPE:
fafd9121 536 event.type = id.type;
fd3d4ec7 537 xasprintf(&message, "type %s", param);
fafd9121
DS
538 break;
539 case RFKILL_IS_INDEX:
540 event.op = RFKILL_OP_CHANGE;
541 event.idx = id.index;
fd3d4ec7 542 xasprintf(&message, "id %d", id.index);
fafd9121 543 break;
b03378e7 544 case RFKILL_IS_ALL:
fd3d4ec7 545 message = xstrdup("all");
367f4080
SK
546 break;
547 default:
548 abort();
94297403 549 }
b03378e7
SK
550
551 fd = open(_PATH_DEV_RFKILL, O_RDWR);
552 if (fd < 0) {
553 warn(_("cannot open %s"), _PATH_DEV_RFKILL);
fd3d4ec7 554 free(message);
3c5022c6 555 return -errno;
b03378e7 556 }
07d517c0
MH
557
558 len = write(fd, &event, sizeof(event));
559 if (len < 0)
367f4080 560 warn(_("write failed: %s"), _PATH_DEV_RFKILL);
3c5022c6
KZ
561 else {
562 openlog("rfkill", 0, LOG_USER);
563 syslog(LOG_NOTICE, "%s set for %s", block ? "block" : "unblock", message);
564 closelog();
565 }
fd3d4ec7 566 free(message);
b1209668 567 return close(fd);
07d517c0
MH
568}
569
367f4080 570static void __attribute__((__noreturn__)) usage(void)
07d517c0 571{
b3849c66 572 size_t i;
47eaa870 573
367f4080 574 fputs(USAGE_HEADER, stdout);
4f892d77 575 fprintf(stdout, _(" %s [options] command [identifier ...]\n"), program_invocation_short_name);
367f4080
SK
576
577 fputs(USAGE_SEPARATOR, stdout);
578 fputs(_("Tool for enabling and disabling wireless devices.\n"), stdout);
579
b3849c66
SK
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);
4dfd172d 584 fputs(_(" --output-all output all columns\n"), stdout);
b3849c66
SK
585 fputs(_(" -r, --raw use the raw output format\n"), stdout);
586
587 fputs(USAGE_SEPARATOR, stdout);
fcc3efb4 588 printf(USAGE_HELP_OPTIONS(24));
b3849c66 589
fcc3efb4 590 fputs(USAGE_COLUMNS, stdout);
b3849c66 591 for (i = 0; i < ARRAY_SIZE(infos); i++)
7d2a9960 592 fprintf(stdout, " %-10s %s\n", infos[i].name, _(infos[i].help));
b3849c66 593
fcc3efb4
KZ
594 fputs(USAGE_COMMANDS, stdout);
595
367f4080
SK
596 /*
597 * TRANSLATORS: command names should not be translated, explaining
73afd3f8 598 * them as additional field after identifier is fine, for example
367f4080
SK
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
367f4080
SK
608 fprintf(stdout, USAGE_MAN_TAIL("rfkill(8)"));
609 exit(EXIT_SUCCESS);
d5fad6eb
JB
610}
611
612int main(int argc, char **argv)
613{
b3849c66 614 struct control ctrl = { 0 };
4dfd172d 615 int c, act = ACT_LIST, list_all = 0;
b3849c66 616 char *outarg = NULL;
4dfd172d
SK
617 enum {
618 OPT_LIST_TYPES = CHAR_MAX + 1
619 };
367f4080 620 static const struct option longopts[] = {
b3849c66
SK
621 { "json", no_argument, NULL, 'J' },
622 { "noheadings", no_argument, NULL, 'n' },
623 { "output", required_argument, NULL, 'o' },
4dfd172d 624 { "output-all", no_argument, NULL, OPT_LIST_TYPES },
b3849c66
SK
625 { "raw", no_argument, NULL, 'r' },
626 { "version", no_argument, NULL, 'V' },
627 { "help", no_argument, NULL, 'h' },
367f4080
SK
628 { NULL, 0, NULL, 0 }
629 };
b3849c66
SK
630 static const ul_excl_t excl[] = {
631 {'J', 'r'},
632 {0}
633 };
634 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
4f892d77 635 int ret = 0;
367f4080
SK
636
637 setlocale(LC_ALL, "");
638 bindtextdomain(PACKAGE, LOCALEDIR);
639 textdomain(PACKAGE);
2c308875 640 close_stdout_atexit();
367f4080 641
b3849c66
SK
642 while ((c = getopt_long(argc, argv, "Jno:rVh", longopts, NULL)) != -1) {
643 err_exclusive_options(c, longopts, excl, excl_st);
367f4080 644 switch (c) {
b3849c66
SK
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;
4dfd172d
SK
654 case OPT_LIST_TYPES:
655 list_all = 1;
656 break;
b3849c66
SK
657 case 'r':
658 ctrl.raw = 1;
659 break;
2c308875 660
367f4080 661 case 'V':
2c308875 662 print_version(EXIT_SUCCESS);
367f4080
SK
663 case 'h':
664 usage();
665 default:
666 errtryhelp(EXIT_FAILURE);
667 }
b3849c66
SK
668 }
669 argc -= optind;
670 argv += optind;
671
18d3a03f
KZ
672 if (argc > 0) {
673 act = string_to_action(*argv);
674 if (act < 0)
675 errtryhelp(EXIT_FAILURE);
676 argv++;
677 argc--;
7d2a9960
KZ
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;
18d3a03f
KZ
685 }
686
687 switch (act) {
7d2a9960
KZ
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
18d3a03f 699 case ACT_LIST:
b3849c66
SK
700 columns[ncolumns++] = COL_ID;
701 columns[ncolumns++] = COL_TYPE;
7d8e3bcd 702 columns[ncolumns++] = COL_DEVICE;
4dfd172d
SK
703 if (list_all)
704 columns[ncolumns++] = COL_DESC;
b3849c66
SK
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;
18d3a03f 713
4f892d77 714 rfkill_list_init(&ctrl);
18d3a03f
KZ
715 if (!argc)
716 ret |= rfkill_list_fill(&ctrl, NULL); /* ALL */
717 else while (argc) {
4f892d77 718 ret |= rfkill_list_fill(&ctrl, *argv);
18d3a03f
KZ
719 argc--;
720 argv++;
b3849c66 721 }
4f892d77 722 rfkill_list_output(&ctrl);
18d3a03f
KZ
723 break;
724
725 case ACT_EVENT:
367f4080 726 ret = rfkill_event();
18d3a03f
KZ
727 break;
728
729 case ACT_HELP:
b3849c66 730 usage();
18d3a03f
KZ
731 break;
732
733 case ACT_BLOCK:
734 while (argc) {
4f892d77 735 ret |= rfkill_block(1, *argv);
18d3a03f 736 argc--;
4f892d77 737 argv++;
18d3a03f
KZ
738 }
739 break;
740
741 case ACT_UNBLOCK:
742 while (argc) {
4f892d77 743 ret |= rfkill_block(0, *argv);
18d3a03f
KZ
744 argv++;
745 argc--;
4f892d77 746 }
18d3a03f
KZ
747 break;
748 }
07d517c0 749
3c5022c6 750 return ret ? EXIT_FAILURE : EXIT_SUCCESS;
d5fad6eb 751}