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