2 * SPDX-License-Identifier: ISC
4 * /dev/rfkill userspace tool
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>
9 * Copyright 2017 Sami Kerola <kerolasa@iki.fi>
10 * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
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.
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.
26 #include <libsmartcols.h>
27 #include <linux/rfkill.h>
29 #include <sys/syslog.h>
33 #include "closestream.h"
36 #include "pathnames.h"
38 #include "timeutils.h"
45 * NFC supported by kernel since v3.10 (year 2013); FM and another types are from
46 * year 2009 (2.6.33) or older.
48 #ifndef RFKILL_TYPE_NFC
49 # ifndef RFKILL_TYPE_FM
50 # define RFKILL_TYPE_FM RFKILL_TYPE_GPS + 1
52 # define RFKILL_TYPE_NFC RFKILL_TYPE_FM + 1
53 # undef NUM_RFKILL_TYPES
54 # define NUM_RFKILL_TYPES RFKILL_TYPE_NFC + 1
57 struct rfkill_type_str
{
58 enum rfkill_type type
; /* ID */
59 const char *name
; /* generic name */
60 const char *desc
; /* human readable name */
63 static const struct rfkill_type_str rfkill_type_strings
[] = {
64 { .type
= RFKILL_TYPE_ALL
, .name
= "all" },
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" },
69 { .type
= RFKILL_TYPE_UWB
, .name
= "ultrawideband" }, /* alias */
70 { .type
= RFKILL_TYPE_WIMAX
, .name
= "wimax", .desc
= "WiMAX" },
71 { .type
= RFKILL_TYPE_WWAN
, .name
= "wwan", .desc
= "Wireless WAN" },
72 { .type
= RFKILL_TYPE_GPS
, .name
= "gps", .desc
= "GPS" },
73 { .type
= RFKILL_TYPE_FM
, .name
= "fm", .desc
= "FM" },
74 { .type
= RFKILL_TYPE_NFC
, .name
= "nfc", .desc
= "NFC" },
75 { .type
= NUM_RFKILL_TYPES
, .name
= NULL
}
80 enum rfkill_type type
;
91 /* supported actions */
103 static char *rfkill_actions
[] = {
106 [ACT_EVENT
] = "event",
107 [ACT_BLOCK
] = "block",
108 [ACT_UNBLOCK
] = "unblock",
109 [ACT_TOGGLE
] = "toggle"
124 const char *name
; /* header */
125 double whint
; /* width hint (N < 1 is in percent of termwidth) */
126 int flags
; /* SCOLS_FL_* */
130 /* columns descriptions */
131 static const struct colinfo infos
[] = {
132 [COL_DEVICE
] = {"DEVICE", 0, 0, N_("kernel device name")},
133 [COL_ID
] = {"ID", 2, SCOLS_FL_RIGHT
, N_("device identifier value")},
134 [COL_TYPE
] = {"TYPE", 0, 0, N_("device type name that can be used as identifier")},
135 [COL_DESC
] = {"TYPE-DESC", 0, 0, N_("device type description")},
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")}
140 static int columns
[ARRAY_SIZE(infos
) * 2];
141 static size_t ncolumns
;
144 struct libscols_table
*tb
;
151 static int column_name_to_id(const char *name
, size_t namesz
)
157 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
158 const char *cn
= infos
[i
].name
;
160 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
163 warnx(_("unknown column: %s"), name
);
167 static int get_column_id(size_t num
)
169 assert(num
< ncolumns
);
170 assert(columns
[num
] < (int)ARRAY_SIZE(infos
));
174 static const struct colinfo
*get_column_info(int num
)
176 return &infos
[get_column_id(num
)];
179 static int string_to_action(const char *str
)
183 for (i
= 0; i
< ARRAY_SIZE(rfkill_actions
); i
++)
184 if (strcmp(str
, rfkill_actions
[i
]) == 0)
190 static int rfkill_open(int rdonly
, int nonblock
)
194 fd
= open(_PATH_DEV_RFKILL
, rdonly
? O_RDONLY
: O_RDWR
);
196 warn(_("cannot open %s"), _PATH_DEV_RFKILL
);
200 if (nonblock
&& fcntl(fd
, F_SETFL
, O_NONBLOCK
) < 0) {
201 warn(_("cannot set non-blocking %s"), _PATH_DEV_RFKILL
);
209 /* returns: 0 success, 1 read again, < 0 error */
210 static int rfkill_read_event(int fd
, struct rfkill_event
*event
)
212 ssize_t len
= read(fd
, event
, sizeof(*event
));
217 warn(_("cannot read %s"), _PATH_DEV_RFKILL
);
221 if ((size_t) len
< (size_t) RFKILL_EVENT_SIZE_V1
) {
222 warnx(_("wrong size of rfkill event: %zu < %zu"),
223 (size_t) len
, (size_t) RFKILL_EVENT_SIZE_V1
);
231 static int rfkill_event(void)
238 struct rfkill_event event
;
240 char date_buf
[ISO_BUFSIZ
];
241 struct pollfd p
[POLLFD_COUNT
];
244 fd
= rfkill_open(1, 0);
248 memset(&p
, 0, sizeof(p
));
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;
254 /* interrupted by signal only */
256 int rc
= 1; /* recover-able error */
258 n
= poll(p
, ARRAY_SIZE(p
), -1);
260 warn(_("failed to poll %s"), _PATH_DEV_RFKILL
);
264 if (p
[POLLFD_STDOUT
].revents
)
265 goto failed
; /* read end of stdout closed */
266 if (p
[POLLFD_RFKILL
].revents
)
267 rc
= rfkill_read_event(fd
, &event
);
273 gettimeofday(&tv
, NULL
);
274 strtimeval_iso(&tv
, ISO_TIMESTAMP_COMMA
, date_buf
,
276 printf("%s: idx %u type %u op %u soft %u hard %u\n",
278 event
.idx
, event
.type
, event
.op
, event
.soft
, event
.hard
);
287 static const char *get_sys_attr(uint32_t idx
, const char *attr
)
289 static char name
[128];
294 snprintf(path
, sizeof(path
), _PATH_SYS_RFKILL
"/rfkill%u/%s", idx
, attr
);
295 f
= fopen(path
, "r");
298 if (!fgets(name
, sizeof(name
), f
))
300 p
= strchr(name
, '\n');
309 static struct rfkill_id
rfkill_id_to_type(const char *s
)
311 const struct rfkill_type_str
*p
;
312 struct rfkill_id ret
= { .result
= 0 };
315 for (p
= rfkill_type_strings
; p
->name
!= NULL
; p
++) {
316 if (!strcmp(s
, p
->name
)) {
318 if (!strcmp(s
, "all"))
319 ret
.result
= RFKILL_IS_ALL
;
321 ret
.result
= RFKILL_IS_TYPE
;
325 } else if (isdigit(*s
)) {
326 /* assume a numeric character implies an index. */
329 ret
.index
= strtou32_or_err(s
, _("invalid identifier"));
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
;
335 ret
.result
= RFKILL_IS_INVALID
;
339 ret
.result
= RFKILL_IS_INVALID
;
343 static const char *rfkill_type_to_desc(enum rfkill_type type
)
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
;
356 static int event_match(struct rfkill_event
*event
, struct rfkill_id
*id
)
358 if (event
->op
!= RFKILL_OP_ADD
)
361 /* filter out unwanted results */
362 switch (id
->result
) {
364 if (event
->type
!= id
->type
)
367 case RFKILL_IS_INDEX
:
368 if (event
->idx
!= id
->index
)
380 static void fill_table_row(struct libscols_table
*tb
, struct rfkill_event
*event
)
382 static struct libscols_line
*ln
;
387 ln
= scols_table_new_line(tb
, NULL
);
390 errx(EXIT_FAILURE
, _("failed to allocate output line"));
393 for (i
= 0; i
< (size_t)ncolumns
; i
++) {
395 switch (get_column_id(i
)) {
397 str
= xstrdup(get_sys_attr(event
->idx
, "name"));
400 xasprintf(&str
, "%" PRIu32
, event
->idx
);
403 str
= xstrdup(get_sys_attr(event
->idx
, "type"));
406 str
= xstrdup(rfkill_type_to_desc(event
->type
));
409 str
= xstrdup(event
->soft
? _("blocked") : _("unblocked"));
412 str
= xstrdup(event
->hard
? _("blocked") : _("unblocked"));
417 if (str
&& scols_line_refer_data(ln
, i
, str
))
418 errx(EXIT_FAILURE
, _("failed to add output data"));
422 static int rfkill_list_old(const char *param
)
424 struct rfkill_id id
= { .result
= RFKILL_IS_ALL
};
425 struct rfkill_event event
;
429 id
= rfkill_id_to_type(param
);
430 if (id
.result
== RFKILL_IS_INVALID
) {
431 warnx(_("invalid identifier: %s"), param
);
436 fd
= rfkill_open(1, 1);
441 rc
= rfkill_read_event(fd
, &event
);
444 if (rc
== 1 && errno
== EAGAIN
) {
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
));
453 type
= xstrdup(get_sys_attr(event
.idx
, "type"));
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");
467 static void rfkill_list_init(struct control
*ctrl
)
473 ctrl
->tb
= scols_new_table();
475 err(EXIT_FAILURE
, _("failed to allocate output table"));
477 scols_table_enable_json(ctrl
->tb
, ctrl
->json
);
478 scols_table_set_name(ctrl
->tb
, "rfkilldevices");
479 scols_table_enable_noheadings(ctrl
->tb
, ctrl
->no_headings
);
480 scols_table_enable_raw(ctrl
->tb
, ctrl
->raw
);
482 for (i
= 0; i
< (size_t) ncolumns
; i
++) {
483 const struct colinfo
*col
= get_column_info(i
);
484 struct libscols_column
*cl
;
486 cl
= scols_table_new_column(ctrl
->tb
, col
->name
, col
->whint
, col
->flags
);
488 err(EXIT_FAILURE
, _("failed to allocate output column"));
490 int id
= get_column_id(i
);
492 scols_column_set_json_type(cl
, SCOLS_JSON_NUMBER
);
497 static int rfkill_list_fill(struct control
const *ctrl
, const char *param
)
499 struct rfkill_id id
= { .result
= RFKILL_IS_ALL
};
500 struct rfkill_event event
;
504 id
= rfkill_id_to_type(param
);
505 if (id
.result
== RFKILL_IS_INVALID
) {
506 warnx(_("invalid identifier: %s"), param
);
511 fd
= rfkill_open(1, 1);
516 rc
= rfkill_read_event(fd
, &event
);
519 if (rc
== 1 && errno
== EAGAIN
) {
523 if (rc
== 0 && event_match(&event
, &id
))
524 fill_table_row(ctrl
->tb
, &event
);
530 static void rfkill_list_output(struct control
const *ctrl
)
532 scols_print_table(ctrl
->tb
);
533 scols_unref_table(ctrl
->tb
);
536 static int __rfkill_block(int fd
, struct rfkill_id
*id
, uint8_t block
, const char *param
)
538 struct rfkill_event event
= {
539 .op
= RFKILL_OP_CHANGE_ALL
,
543 char *message
= NULL
;
545 switch (id
->result
) {
546 case RFKILL_IS_INVALID
:
547 warnx(_("invalid identifier: %s"), param
);
550 event
.type
= id
->type
;
551 xasprintf(&message
, "type %s", param
);
553 case RFKILL_IS_INDEX
:
554 event
.op
= RFKILL_OP_CHANGE
;
555 event
.idx
= id
->index
;
556 xasprintf(&message
, "id %d", id
->index
);
559 message
= xstrdup("all");
565 if (write_all(fd
, &event
, sizeof(event
)) != 0)
566 warn(_("write failed: %s"), _PATH_DEV_RFKILL
);
568 openlog("rfkill", 0, LOG_USER
);
569 syslog(LOG_NOTICE
, "%s set for %s", block
? "block" : "unblock", message
);
576 static int rfkill_block(uint8_t block
, const char *param
)
581 id
= rfkill_id_to_type(param
);
582 if (id
.result
== RFKILL_IS_INVALID
) {
583 warnx(_("invalid identifier: %s"), param
);
587 fd
= rfkill_open(0, 0);
591 __rfkill_block(fd
, &id
, block
, param
);
596 static int rfkill_toggle(const char *param
)
598 struct rfkill_id id
= { .result
= RFKILL_IS_ALL
};
599 struct rfkill_event event
;
602 id
= rfkill_id_to_type(param
);
603 if (id
.result
== RFKILL_IS_INVALID
) {
604 warnx(_("invalid identifier: %s"), param
);
608 fd
= rfkill_open(0, 1);
613 rc
= rfkill_read_event(fd
, &event
);
616 if (rc
== 1 && errno
== EAGAIN
) {
620 if (rc
== 0 && event_match(&event
, &id
))
621 __rfkill_block(fd
, &id
, event
.soft
? 0 : 1, param
);
629 static void __attribute__((__noreturn__
)) usage(void)
633 fputs(USAGE_HEADER
, stdout
);
634 fprintf(stdout
, _(" %s [options] command [identifier ...]\n"), program_invocation_short_name
);
636 fputs(USAGE_SEPARATOR
, stdout
);
637 fputs(_("Tool for enabling and disabling wireless devices.\n"), stdout
);
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
);
643 fputs(_(" --output-all output all columns\n"), stdout
);
644 fputs(_(" -r, --raw use the raw output format\n"), stdout
);
646 fputs(USAGE_SEPARATOR
, stdout
);
647 fprintf(stdout
, USAGE_HELP_OPTIONS(24));
649 fputs(USAGE_COLUMNS
, stdout
);
650 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
651 fprintf(stdout
, " %-10s %s\n", infos
[i
].name
, _(infos
[i
].help
));
653 fputs(USAGE_COMMANDS
, stdout
);
656 * TRANSLATORS: command names should not be translated, explaining
657 * them as additional field after identifier is fine, for example
659 * list [identifier] (lista [tarkenne])
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
);
666 fputs(_(" toggle identifier\n"), stdout
);
668 fprintf(stdout
, USAGE_MAN_TAIL("rfkill(8)"));
672 int main(int argc
, char **argv
)
674 struct control ctrl
= { 0 };
675 int c
, act
= ACT_LIST
, list_all
= 0;
678 OPT_LIST_TYPES
= CHAR_MAX
+ 1
680 static const struct option longopts
[] = {
681 { "json", no_argument
, NULL
, 'J' },
682 { "noheadings", no_argument
, NULL
, 'n' },
683 { "output", required_argument
, NULL
, 'o' },
684 { "output-all", no_argument
, NULL
, OPT_LIST_TYPES
},
685 { "raw", no_argument
, NULL
, 'r' },
686 { "version", no_argument
, NULL
, 'V' },
687 { "help", no_argument
, NULL
, 'h' },
690 static const ul_excl_t excl
[] = {
694 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
697 setlocale(LC_ALL
, "");
698 bindtextdomain(PACKAGE
, LOCALEDIR
);
700 close_stdout_atexit();
702 while ((c
= getopt_long(argc
, argv
, "Jno:rVh", longopts
, NULL
)) != -1) {
703 err_exclusive_options(c
, longopts
, excl
, excl_st
);
709 ctrl
.no_headings
= 1;
722 print_version(EXIT_SUCCESS
);
726 errtryhelp(EXIT_FAILURE
);
733 act
= string_to_action(*argv
);
735 errtryhelp(EXIT_FAILURE
);
740 * For backward compatibility we use old output format if
741 * "list" explicitly specified and --output not defined.
743 if (!outarg
&& act
== ACT_LIST
)
749 /* Deprecated in favour of ACT_LIST */
751 ret
|= rfkill_list_old(NULL
); /* ALL */
753 ret
|= rfkill_list_old(*argv
);
760 columns
[ncolumns
++] = COL_ID
;
761 columns
[ncolumns
++] = COL_TYPE
;
762 columns
[ncolumns
++] = COL_DEVICE
;
764 columns
[ncolumns
++] = COL_DESC
;
765 columns
[ncolumns
++] = COL_SOFT
;
766 columns
[ncolumns
++] = COL_HARD
;
769 && string_add_to_idarray(outarg
, columns
,
770 ARRAY_SIZE(columns
), &ncolumns
,
771 column_name_to_id
) < 0)
774 rfkill_list_init(&ctrl
);
776 ret
|= rfkill_list_fill(&ctrl
, NULL
); /* ALL */
778 ret
|= rfkill_list_fill(&ctrl
, *argv
);
782 rfkill_list_output(&ctrl
);
786 ret
= rfkill_event();
795 ret
|= rfkill_block(1, *argv
);
803 ret
|= rfkill_block(0, *argv
);
811 ret
|= rfkill_toggle(*argv
);
818 return ret
? EXIT_FAILURE
: EXIT_SUCCESS
;