2 * /dev/rfkill userspace tool
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>
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.
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.
25 #include <libsmartcols.h>
26 #include <linux/rfkill.h>
28 #include <sys/syslog.h>
32 #include "closestream.h"
35 #include "pathnames.h"
37 #include "timeutils.h"
44 * NFC supported by kernel since v3.10 (year 2013); FM and another types are from
45 * year 2009 (2.6.33) or older.
47 #ifndef RFKILL_TYPE_NFC
48 # ifndef RFKILL_TYPE_FM
49 # define RFKILL_TYPE_FM RFKILL_TYPE_GPS + 1
51 # define RFKILL_TYPE_NFC RFKILL_TYPE_FM + 1
52 # undef NUM_RFKILL_TYPES
53 # define NUM_RFKILL_TYPES RFKILL_TYPE_NFC + 1
56 struct rfkill_type_str
{
57 enum rfkill_type type
; /* ID */
58 const char *name
; /* generic name */
59 const char *desc
; /* human readable name */
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
}
79 enum rfkill_type type
;
90 /* supported actions */
102 static char *rfkill_actions
[] = {
105 [ACT_EVENT
] = "event",
106 [ACT_BLOCK
] = "block",
107 [ACT_UNBLOCK
] = "unblock",
108 [ACT_TOGGLE
] = "toggle"
123 const char *name
; /* header */
124 double whint
; /* width hint (N < 1 is in percent of termwidth) */
125 int flags
; /* SCOLS_FL_* */
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")}
139 static int columns
[ARRAY_SIZE(infos
) * 2];
140 static size_t ncolumns
;
143 struct libscols_table
*tb
;
150 static int column_name_to_id(const char *name
, size_t namesz
)
156 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
157 const char *cn
= infos
[i
].name
;
159 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
162 warnx(_("unknown column: %s"), name
);
166 static int get_column_id(size_t num
)
168 assert(num
< ncolumns
);
169 assert(columns
[num
] < (int)ARRAY_SIZE(infos
));
173 static const struct colinfo
*get_column_info(int num
)
175 return &infos
[get_column_id(num
)];
178 static int string_to_action(const char *str
)
182 for (i
= 0; i
< ARRAY_SIZE(rfkill_actions
); i
++)
183 if (strcmp(str
, rfkill_actions
[i
]) == 0)
189 static int rfkill_open(int rdonly
, int nonblock
)
193 fd
= open(_PATH_DEV_RFKILL
, rdonly
? O_RDONLY
: O_RDWR
);
195 warn(_("cannot open %s"), _PATH_DEV_RFKILL
);
199 if (nonblock
&& fcntl(fd
, F_SETFL
, O_NONBLOCK
) < 0) {
200 warn(_("cannot set non-blocking %s"), _PATH_DEV_RFKILL
);
208 /* returns: 0 success, 1 read again, < 0 error */
209 static int rfkill_read_event(int fd
, struct rfkill_event
*event
)
211 ssize_t len
= read(fd
, event
, sizeof(*event
));
216 warn(_("cannot read %s"), _PATH_DEV_RFKILL
);
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
);
230 static int rfkill_event(void)
237 struct rfkill_event event
;
239 char date_buf
[ISO_BUFSIZ
];
240 struct pollfd p
[POLLFD_COUNT
];
243 fd
= rfkill_open(1, 0);
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;
253 /* interrupted by signal only */
255 int rc
= 1; /* recover-able error */
257 n
= poll(p
, ARRAY_SIZE(p
), -1);
259 warn(_("failed to poll %s"), _PATH_DEV_RFKILL
);
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
);
272 gettimeofday(&tv
, NULL
);
273 strtimeval_iso(&tv
, ISO_TIMESTAMP_COMMA
, date_buf
,
275 printf("%s: idx %u type %u op %u soft %u hard %u\n",
277 event
.idx
, event
.type
, event
.op
, event
.soft
, event
.hard
);
286 static const char *get_sys_attr(uint32_t idx
, const char *attr
)
288 static char name
[128];
293 snprintf(path
, sizeof(path
), _PATH_SYS_RFKILL
"/rfkill%u/%s", idx
, attr
);
294 f
= fopen(path
, "r");
297 if (!fgets(name
, sizeof(name
), f
))
299 p
= strchr(name
, '\n');
308 static struct rfkill_id
rfkill_id_to_type(const char *s
)
310 const struct rfkill_type_str
*p
;
311 struct rfkill_id ret
= { .result
= 0 };
314 for (p
= rfkill_type_strings
; p
->name
!= NULL
; p
++) {
315 if (!strcmp(s
, p
->name
)) {
317 if (!strcmp(s
, "all"))
318 ret
.result
= RFKILL_IS_ALL
;
320 ret
.result
= RFKILL_IS_TYPE
;
324 } else if (isdigit(*s
)) {
325 /* assume a numeric character implies an index. */
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
;
334 ret
.result
= RFKILL_IS_INVALID
;
338 ret
.result
= RFKILL_IS_INVALID
;
342 static const char *rfkill_type_to_desc(enum rfkill_type type
)
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
;
355 static int event_match(struct rfkill_event
*event
, struct rfkill_id
*id
)
357 if (event
->op
!= RFKILL_OP_ADD
)
360 /* filter out unwanted results */
361 switch (id
->result
) {
363 if (event
->type
!= id
->type
)
366 case RFKILL_IS_INDEX
:
367 if (event
->idx
!= id
->index
)
379 static void fill_table_row(struct libscols_table
*tb
, struct rfkill_event
*event
)
381 static struct libscols_line
*ln
;
386 ln
= scols_table_new_line(tb
, NULL
);
389 errx(EXIT_FAILURE
, _("failed to allocate output line"));
392 for (i
= 0; i
< (size_t)ncolumns
; i
++) {
394 switch (get_column_id(i
)) {
396 str
= xstrdup(get_sys_attr(event
->idx
, "name"));
399 xasprintf(&str
, "%" PRIu32
, event
->idx
);
402 str
= xstrdup(get_sys_attr(event
->idx
, "type"));
405 str
= xstrdup(rfkill_type_to_desc(event
->type
));
408 str
= xstrdup(event
->soft
? _("blocked") : _("unblocked"));
411 str
= xstrdup(event
->hard
? _("blocked") : _("unblocked"));
416 if (str
&& scols_line_refer_data(ln
, i
, str
))
417 errx(EXIT_FAILURE
, _("failed to add output data"));
421 static int rfkill_list_old(const char *param
)
423 struct rfkill_id id
= { .result
= RFKILL_IS_ALL
};
424 struct rfkill_event event
;
428 id
= rfkill_id_to_type(param
);
429 if (id
.result
== RFKILL_IS_INVALID
) {
430 warnx(_("invalid identifier: %s"), param
);
435 fd
= rfkill_open(1, 1);
440 rc
= rfkill_read_event(fd
, &event
);
443 if (rc
== 1 && errno
== EAGAIN
) {
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
));
452 type
= xstrdup(get_sys_attr(event
.idx
, "type"));
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");
466 static void rfkill_list_init(struct control
*ctrl
)
472 ctrl
->tb
= scols_new_table();
474 err(EXIT_FAILURE
, _("failed to allocate output table"));
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
);
481 for (i
= 0; i
< (size_t) ncolumns
; i
++) {
482 const struct colinfo
*col
= get_column_info(i
);
483 struct libscols_column
*cl
;
485 cl
= scols_table_new_column(ctrl
->tb
, col
->name
, col
->whint
, col
->flags
);
487 err(EXIT_FAILURE
, _("failed to allocate output column"));
489 int id
= get_column_id(i
);
491 scols_column_set_json_type(cl
, SCOLS_JSON_NUMBER
);
496 static int rfkill_list_fill(struct control
const *ctrl
, const char *param
)
498 struct rfkill_id id
= { .result
= RFKILL_IS_ALL
};
499 struct rfkill_event event
;
503 id
= rfkill_id_to_type(param
);
504 if (id
.result
== RFKILL_IS_INVALID
) {
505 warnx(_("invalid identifier: %s"), param
);
510 fd
= rfkill_open(1, 1);
515 rc
= rfkill_read_event(fd
, &event
);
518 if (rc
== 1 && errno
== EAGAIN
) {
522 if (rc
== 0 && event_match(&event
, &id
))
523 fill_table_row(ctrl
->tb
, &event
);
529 static void rfkill_list_output(struct control
const *ctrl
)
531 scols_print_table(ctrl
->tb
);
532 scols_unref_table(ctrl
->tb
);
535 static int __rfkill_block(int fd
, struct rfkill_id
*id
, uint8_t block
, const char *param
)
537 struct rfkill_event event
= {
538 .op
= RFKILL_OP_CHANGE_ALL
,
542 char *message
= NULL
;
544 switch (id
->result
) {
545 case RFKILL_IS_INVALID
:
546 warnx(_("invalid identifier: %s"), param
);
549 event
.type
= id
->type
;
550 xasprintf(&message
, "type %s", param
);
552 case RFKILL_IS_INDEX
:
553 event
.op
= RFKILL_OP_CHANGE
;
554 event
.idx
= id
->index
;
555 xasprintf(&message
, "id %d", id
->index
);
558 message
= xstrdup("all");
564 if (write_all(fd
, &event
, sizeof(event
)) != 0)
565 warn(_("write failed: %s"), _PATH_DEV_RFKILL
);
567 openlog("rfkill", 0, LOG_USER
);
568 syslog(LOG_NOTICE
, "%s set for %s", block
? "block" : "unblock", message
);
575 static int rfkill_block(uint8_t block
, const char *param
)
580 id
= rfkill_id_to_type(param
);
581 if (id
.result
== RFKILL_IS_INVALID
) {
582 warnx(_("invalid identifier: %s"), param
);
586 fd
= rfkill_open(0, 0);
590 __rfkill_block(fd
, &id
, block
, param
);
595 static int rfkill_toggle(const char *param
)
597 struct rfkill_id id
= { .result
= RFKILL_IS_ALL
};
598 struct rfkill_event event
;
601 id
= rfkill_id_to_type(param
);
602 if (id
.result
== RFKILL_IS_INVALID
) {
603 warnx(_("invalid identifier: %s"), param
);
607 fd
= rfkill_open(0, 1);
612 rc
= rfkill_read_event(fd
, &event
);
615 if (rc
== 1 && errno
== EAGAIN
) {
619 if (rc
== 0 && event_match(&event
, &id
))
620 __rfkill_block(fd
, &id
, event
.soft
? 0 : 1, param
);
628 static void __attribute__((__noreturn__
)) usage(void)
632 fputs(USAGE_HEADER
, stdout
);
633 fprintf(stdout
, _(" %s [options] command [identifier ...]\n"), program_invocation_short_name
);
635 fputs(USAGE_SEPARATOR
, stdout
);
636 fputs(_("Tool for enabling and disabling wireless devices.\n"), stdout
);
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
);
645 fputs(USAGE_SEPARATOR
, stdout
);
646 fprintf(stdout
, USAGE_HELP_OPTIONS(24));
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
));
652 fputs(USAGE_COMMANDS
, stdout
);
655 * TRANSLATORS: command names should not be translated, explaining
656 * them as additional field after identifier is fine, for example
658 * list [identifier] (lista [tarkenne])
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
);
667 fprintf(stdout
, USAGE_MAN_TAIL("rfkill(8)"));
671 int main(int argc
, char **argv
)
673 struct control ctrl
= { 0 };
674 int c
, act
= ACT_LIST
, list_all
= 0;
677 OPT_LIST_TYPES
= CHAR_MAX
+ 1
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' },
689 static const ul_excl_t excl
[] = {
693 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
696 setlocale(LC_ALL
, "");
697 bindtextdomain(PACKAGE
, LOCALEDIR
);
699 close_stdout_atexit();
701 while ((c
= getopt_long(argc
, argv
, "Jno:rVh", longopts
, NULL
)) != -1) {
702 err_exclusive_options(c
, longopts
, excl
, excl_st
);
708 ctrl
.no_headings
= 1;
721 print_version(EXIT_SUCCESS
);
725 errtryhelp(EXIT_FAILURE
);
732 act
= string_to_action(*argv
);
734 errtryhelp(EXIT_FAILURE
);
739 * For backward compatibility we use old output format if
740 * "list" explicitly specified and --output not defined.
742 if (!outarg
&& act
== ACT_LIST
)
748 /* Deprecated in favour of ACT_LIST */
750 ret
|= rfkill_list_old(NULL
); /* ALL */
752 ret
|= rfkill_list_old(*argv
);
759 columns
[ncolumns
++] = COL_ID
;
760 columns
[ncolumns
++] = COL_TYPE
;
761 columns
[ncolumns
++] = COL_DEVICE
;
763 columns
[ncolumns
++] = COL_DESC
;
764 columns
[ncolumns
++] = COL_SOFT
;
765 columns
[ncolumns
++] = COL_HARD
;
768 && string_add_to_idarray(outarg
, columns
,
769 ARRAY_SIZE(columns
), &ncolumns
,
770 column_name_to_id
) < 0)
773 rfkill_list_init(&ctrl
);
775 ret
|= rfkill_list_fill(&ctrl
, NULL
); /* ALL */
777 ret
|= rfkill_list_fill(&ctrl
, *argv
);
781 rfkill_list_output(&ctrl
);
785 ret
= rfkill_event();
794 ret
|= rfkill_block(1, *argv
);
802 ret
|= rfkill_block(0, *argv
);
810 ret
|= rfkill_toggle(*argv
);
817 return ret
? EXIT_FAILURE
: EXIT_SUCCESS
;