]>
Commit | Line | Data |
---|---|---|
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 | 55 | struct 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 | ||
61 | static 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 | ||
76 | struct 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 */ |
90 | enum { | |
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 | ||
100 | static 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 */ |
109 | enum { | |
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 */ | |
119 | struct 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 */ | |
127 | static 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 | ||
136 | static int columns[ARRAY_SIZE(infos) * 2]; | |
137 | static size_t ncolumns; | |
138 | ||
139 | struct control { | |
4f892d77 | 140 | struct libscols_table *tb; |
b3849c66 SK |
141 | unsigned int |
142 | json:1, | |
143 | no_headings:1, | |
144 | raw:1; | |
145 | }; | |
146 | ||
147 | static 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 | ||
163 | static 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 | ||
170 | static const struct colinfo *get_column_info(int num) | |
171 | { | |
172 | return &infos[get_column_id(num)]; | |
173 | } | |
174 | ||
18d3a03f KZ |
175 | static 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 |
186 | static 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 */ | |
206 | static 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 | 226 | static 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 | 268 | failed: |
07d517c0 | 269 | close(fd); |
3c5022c6 | 270 | return -1; |
07d517c0 MH |
271 | } |
272 | ||
7d2a9960 | 273 | static 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'; | |
289 | done: | |
290 | if (f) | |
291 | fclose(f); | |
07d517c0 MH |
292 | return name; |
293 | } | |
294 | ||
fafd9121 DS |
295 | static 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 |
329 | static 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 |
342 | static 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 |
366 | static 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 |
408 | static 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 | 451 | static 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 |
480 | static 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 |
511 | static void rfkill_list_output(struct control const *ctrl) |
512 | { | |
513 | scols_print_table(ctrl->tb); | |
514 | scols_unref_table(ctrl->tb); | |
515 | } | |
516 | ||
367f4080 | 517 | static 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 | 570 | static 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 | ||
612 | int 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 | } |