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"
43 * NFC supported by kernel since v3.10 (year 2013); FM and another types are from
44 * year 2009 (2.6.33) or older.
46 #ifndef RFKILL_TYPE_NFC
47 # ifndef RFKILL_TYPE_FM
48 # define RFKILL_TYPE_FM RFKILL_TYPE_GPS + 1
50 # define RFKILL_TYPE_NFC RFKILL_TYPE_FM + 1
51 # undef NUM_RFKILL_TYPES
52 # define NUM_RFKILL_TYPES RFKILL_TYPE_NFC + 1
55 struct rfkill_type_str
{
56 enum rfkill_type type
; /* ID */
57 const char *name
; /* generic name */
58 const char *desc
; /* human readable name */
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
}
78 enum rfkill_type type
;
89 /* supported actions */
100 static char *rfkill_actions
[] = {
103 [ACT_EVENT
] = "event",
104 [ACT_BLOCK
] = "block",
105 [ACT_UNBLOCK
] = "unblock"
120 const char *name
; /* header */
121 double whint
; /* width hint (N < 1 is in percent of termwidth) */
122 int flags
; /* SCOLS_FL_* */
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")}
136 static int columns
[ARRAY_SIZE(infos
) * 2];
137 static size_t ncolumns
;
140 struct libscols_table
*tb
;
147 static int column_name_to_id(const char *name
, size_t namesz
)
153 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
154 const char *cn
= infos
[i
].name
;
156 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
159 warnx(_("unknown column: %s"), name
);
163 static int get_column_id(size_t num
)
165 assert(num
< ncolumns
);
166 assert(columns
[num
] < (int)ARRAY_SIZE(infos
));
170 static const struct colinfo
*get_column_info(int num
)
172 return &infos
[get_column_id(num
)];
175 static int string_to_action(const char *str
)
179 for (i
= 0; i
< ARRAY_SIZE(rfkill_actions
); i
++)
180 if (strcmp(str
, rfkill_actions
[i
]) == 0)
186 static int rfkill_ro_open(int nonblock
)
190 fd
= open(_PATH_DEV_RFKILL
, O_RDONLY
);
192 warn(_("cannot open %s"), _PATH_DEV_RFKILL
);
196 if (nonblock
&& fcntl(fd
, F_SETFL
, O_NONBLOCK
) < 0) {
197 warn(_("cannot set non-blocking %s"), _PATH_DEV_RFKILL
);
205 /* returns: 0 success, 1 read again, < 0 error */
206 static int rfkill_read_event(int fd
, struct rfkill_event
*event
)
208 ssize_t len
= read(fd
, event
, sizeof(*event
));
213 warn(_("cannot read %s"), _PATH_DEV_RFKILL
);
217 if (len
< RFKILL_EVENT_SIZE_V1
) {
218 warnx(_("wrong size of rfkill event: %zu < %d"), len
, RFKILL_EVENT_SIZE_V1
);
226 static int rfkill_event(void)
228 struct rfkill_event event
;
230 char date_buf
[ISO_BUFSIZ
];
234 fd
= rfkill_ro_open(0);
238 memset(&p
, 0, sizeof(p
));
240 p
.events
= POLLIN
| POLLHUP
;
242 /* interrupted by signal only */
244 int rc
= 1; /* recover-able error */
248 warn(_("failed to poll %s"), _PATH_DEV_RFKILL
);
253 rc
= rfkill_read_event(fd
, &event
);
259 gettimeofday(&tv
, NULL
);
260 strtimeval_iso(&tv
, ISO_TIMESTAMP_COMMA
, date_buf
,
262 printf("%s: idx %u type %u op %u soft %u hard %u\n",
264 event
.idx
, event
.type
, event
.op
, event
.soft
, event
.hard
);
273 static const char *get_sys_attr(uint32_t idx
, const char *attr
)
275 static char name
[128];
280 snprintf(path
, sizeof(path
), _PATH_SYS_RFKILL
"/rfkill%u/%s", idx
, attr
);
281 f
= fopen(path
, "r");
284 if (!fgets(name
, sizeof(name
), f
))
286 p
= strchr(name
, '\n');
295 static struct rfkill_id
rfkill_id_to_type(const char *s
)
297 const struct rfkill_type_str
*p
;
298 struct rfkill_id ret
;
301 for (p
= rfkill_type_strings
; p
->name
!= NULL
; p
++) {
302 if (!strcmp(s
, p
->name
)) {
304 if (!strcmp(s
, "all"))
305 ret
.result
= RFKILL_IS_ALL
;
307 ret
.result
= RFKILL_IS_TYPE
;
311 } else if (isdigit(*s
)) {
312 /* assume a numeric character implies an index. */
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
;
321 ret
.result
= RFKILL_IS_INVALID
;
325 ret
.result
= RFKILL_IS_INVALID
;
329 static const char *rfkill_type_to_desc(enum rfkill_type type
)
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
;
342 static int event_match(struct rfkill_event
*event
, struct rfkill_id
*id
)
344 if (event
->op
!= RFKILL_OP_ADD
)
347 /* filter out unwanted results */
348 switch (id
->result
) {
350 if (event
->type
!= id
->type
)
353 case RFKILL_IS_INDEX
:
354 if (event
->idx
!= id
->index
)
366 static void fill_table_row(struct libscols_table
*tb
, struct rfkill_event
*event
)
368 static struct libscols_line
*ln
;
373 ln
= scols_table_new_line(tb
, NULL
);
376 errx(EXIT_FAILURE
, _("failed to allocate output line"));
379 for (i
= 0; i
< (size_t)ncolumns
; i
++) {
381 switch (get_column_id(i
)) {
383 str
= xstrdup(get_sys_attr(event
->idx
, "name"));
386 xasprintf(&str
, "%" PRIu32
, event
->idx
);
389 str
= xstrdup(get_sys_attr(event
->idx
, "type"));
392 str
= xstrdup(rfkill_type_to_desc(event
->type
));
395 str
= xstrdup(event
->soft
? _("blocked") : _("unblocked"));
398 str
= xstrdup(event
->hard
? _("blocked") : _("unblocked"));
403 if (str
&& scols_line_refer_data(ln
, i
, str
))
404 errx(EXIT_FAILURE
, _("failed to add output data"));
408 static int rfkill_list_old(const char *param
)
410 struct rfkill_id id
= { .result
= RFKILL_IS_ALL
};
411 struct rfkill_event event
;
415 id
= rfkill_id_to_type(param
);
416 if (id
.result
== RFKILL_IS_INVALID
) {
417 warnx(_("invalid identifier: %s"), param
);
422 fd
= rfkill_ro_open(1);
425 rc
= rfkill_read_event(fd
, &event
);
428 if (rc
== 1 && errno
== EAGAIN
) {
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
));
437 type
= xstrdup(get_sys_attr(event
.idx
, "type"));
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");
451 static void rfkill_list_init(struct control
*ctrl
)
457 ctrl
->tb
= scols_new_table();
459 err(EXIT_FAILURE
, _("failed to allocate output table"));
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
);
465 for (i
= 0; i
< (size_t) ncolumns
; i
++) {
466 const struct colinfo
*col
= get_column_info(i
);
467 struct libscols_column
*cl
;
469 cl
= scols_table_new_column(ctrl
->tb
, col
->name
, col
->whint
, col
->flags
);
471 err(EXIT_FAILURE
, _("failed to allocate output column"));
473 int id
= get_column_id(i
);
475 scols_column_set_json_type(cl
, SCOLS_JSON_NUMBER
);
480 static int rfkill_list_fill(struct control
const *ctrl
, const char *param
)
482 struct rfkill_id id
= { .result
= RFKILL_IS_ALL
};
483 struct rfkill_event event
;
487 id
= rfkill_id_to_type(param
);
488 if (id
.result
== RFKILL_IS_INVALID
) {
489 warnx(_("invalid identifier: %s"), param
);
494 fd
= rfkill_ro_open(1);
497 rc
= rfkill_read_event(fd
, &event
);
500 if (rc
== 1 && errno
== EAGAIN
) {
504 if (rc
== 0 && event_match(&event
, &id
))
505 fill_table_row(ctrl
->tb
, &event
);
511 static void rfkill_list_output(struct control
const *ctrl
)
513 scols_print_table(ctrl
->tb
);
514 scols_unref_table(ctrl
->tb
);
517 static int rfkill_block(uint8_t block
, const char *param
)
520 struct rfkill_event event
= {
521 .op
= RFKILL_OP_CHANGE_ALL
,
527 char *message
= NULL
;
529 id
= rfkill_id_to_type(param
);
532 case RFKILL_IS_INVALID
:
533 warnx(_("invalid identifier: %s"), param
);
536 event
.type
= id
.type
;
537 xasprintf(&message
, "type %s", param
);
539 case RFKILL_IS_INDEX
:
540 event
.op
= RFKILL_OP_CHANGE
;
541 event
.idx
= id
.index
;
542 xasprintf(&message
, "id %d", id
.index
);
545 message
= xstrdup("all");
551 fd
= open(_PATH_DEV_RFKILL
, O_RDWR
);
553 warn(_("cannot open %s"), _PATH_DEV_RFKILL
);
558 len
= write(fd
, &event
, sizeof(event
));
560 warn(_("write failed: %s"), _PATH_DEV_RFKILL
);
562 openlog("rfkill", 0, LOG_USER
);
563 syslog(LOG_NOTICE
, "%s set for %s", block
? "block" : "unblock", message
);
570 static void __attribute__((__noreturn__
)) usage(void)
574 fputs(USAGE_HEADER
, stdout
);
575 fprintf(stdout
, _(" %s [options] command [identifier ...]\n"), program_invocation_short_name
);
577 fputs(USAGE_SEPARATOR
, stdout
);
578 fputs(_("Tool for enabling and disabling wireless devices.\n"), stdout
);
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
);
587 fputs(USAGE_SEPARATOR
, stdout
);
588 printf(USAGE_HELP_OPTIONS(24));
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
));
594 fputs(USAGE_COMMANDS
, stdout
);
597 * TRANSLATORS: command names should not be translated, explaining
598 * them as additional field after identifier is fine, for example
600 * list [identifier] (lista [tarkenne])
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
);
608 fprintf(stdout
, USAGE_MAN_TAIL("rfkill(8)"));
612 int main(int argc
, char **argv
)
614 struct control ctrl
= { 0 };
615 int c
, act
= ACT_LIST
, list_all
= 0;
618 OPT_LIST_TYPES
= CHAR_MAX
+ 1
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' },
630 static const ul_excl_t excl
[] = {
634 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
637 setlocale(LC_ALL
, "");
638 bindtextdomain(PACKAGE
, LOCALEDIR
);
640 close_stdout_atexit();
642 while ((c
= getopt_long(argc
, argv
, "Jno:rVh", longopts
, NULL
)) != -1) {
643 err_exclusive_options(c
, longopts
, excl
, excl_st
);
649 ctrl
.no_headings
= 1;
662 print_version(EXIT_SUCCESS
);
666 errtryhelp(EXIT_FAILURE
);
673 act
= string_to_action(*argv
);
675 errtryhelp(EXIT_FAILURE
);
680 * For backward compatibility we use old output format if
681 * "list" explicitly specified and--output not defined.
683 if (!outarg
&& act
== ACT_LIST
)
689 /* Deprecated in favour of ACT_LIST */
691 ret
|= rfkill_list_old(NULL
); /* ALL */
693 ret
|= rfkill_list_old(*argv
);
700 columns
[ncolumns
++] = COL_ID
;
701 columns
[ncolumns
++] = COL_TYPE
;
702 columns
[ncolumns
++] = COL_DEVICE
;
704 columns
[ncolumns
++] = COL_DESC
;
705 columns
[ncolumns
++] = COL_SOFT
;
706 columns
[ncolumns
++] = COL_HARD
;
709 && string_add_to_idarray(outarg
, columns
,
710 ARRAY_SIZE(columns
), &ncolumns
,
711 column_name_to_id
) < 0)
714 rfkill_list_init(&ctrl
);
716 ret
|= rfkill_list_fill(&ctrl
, NULL
); /* ALL */
718 ret
|= rfkill_list_fill(&ctrl
, *argv
);
722 rfkill_list_output(&ctrl
);
726 ret
= rfkill_event();
735 ret
|= rfkill_block(1, *argv
);
743 ret
|= rfkill_block(0, *argv
);
750 return ret
? EXIT_FAILURE
: EXIT_SUCCESS
;