]>
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> |
07d517c0 | 27 | #include <sys/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" |
7d2a9960 | 40 | #include "path.h" |
367f4080 | 41 | |
ea05c23a KZ |
42 | |
43 | /* | |
44 | * NFC supported by kernel since v3.10 (year 2013); FM and another types are from | |
45 | * year 2009 (2.6.33) or older. | |
46 | */ | |
47 | #ifndef RFKILL_TYPE_NFC | |
48 | # define RFKILL_TYPE_NFC RFKILL_TYPE_FM + 1 | |
49 | #endif | |
50 | ||
367f4080 | 51 | struct rfkill_type_str { |
7d2a9960 KZ |
52 | enum rfkill_type type; /* ID */ |
53 | const char *name; /* generic name */ | |
54 | const char *desc; /* human readable name */ | |
367f4080 SK |
55 | }; |
56 | ||
57 | static const struct rfkill_type_str rfkill_type_strings[] = { | |
58 | { .type = RFKILL_TYPE_ALL, .name = "all" }, | |
7d2a9960 KZ |
59 | { .type = RFKILL_TYPE_WLAN, .name = "wlan", .desc = "Wireless LAN" }, |
60 | { .type = RFKILL_TYPE_WLAN, .name = "wifi" }, /* alias */ | |
61 | { .type = RFKILL_TYPE_BLUETOOTH, .name = "bluetooth", .desc = "Bluetooth" }, | |
62 | { .type = RFKILL_TYPE_UWB, .name = "uwb", .desc = "Ultra-Wideband" }, | |
367f4080 | 63 | { .type = RFKILL_TYPE_UWB, .name = "ultrawideband" }, /* alias */ |
7d2a9960 KZ |
64 | { .type = RFKILL_TYPE_WIMAX, .name = "wimax", .desc = "WiMAX" }, |
65 | { .type = RFKILL_TYPE_WWAN, .name = "wwan", .desc = "Wireless WAN" }, | |
9e33cddd | 66 | { .type = RFKILL_TYPE_GPS, .name = "gps", .desc = "GPS" }, |
7d2a9960 KZ |
67 | { .type = RFKILL_TYPE_FM, .name = "fm", .desc = "FM" }, |
68 | { .type = RFKILL_TYPE_NFC, .name = "nfc", .desc = "NFC" }, | |
367f4080 SK |
69 | { .type = NUM_RFKILL_TYPES, .name = NULL } |
70 | }; | |
71 | ||
72 | struct rfkill_id { | |
73 | union { | |
74 | enum rfkill_type type; | |
75 | uint32_t index; | |
76 | }; | |
77 | enum { | |
78 | RFKILL_IS_INVALID, | |
79 | RFKILL_IS_TYPE, | |
80 | RFKILL_IS_INDEX, | |
b03378e7 | 81 | RFKILL_IS_ALL |
367f4080 SK |
82 | } result; |
83 | }; | |
d5fad6eb | 84 | |
18d3a03f KZ |
85 | /* supported actions */ |
86 | enum { | |
87 | ACT_LIST, | |
88 | ACT_HELP, | |
89 | ACT_EVENT, | |
90 | ACT_BLOCK, | |
7d2a9960 KZ |
91 | ACT_UNBLOCK, |
92 | ||
93 | ACT_LIST_OLD | |
18d3a03f KZ |
94 | }; |
95 | ||
96 | static char *rfkill_actions[] = { | |
97 | [ACT_LIST] = "list", | |
98 | [ACT_HELP] = "help", | |
99 | [ACT_EVENT] = "event", | |
100 | [ACT_BLOCK] = "block", | |
101 | [ACT_UNBLOCK] = "unblock" | |
102 | }; | |
103 | ||
b3849c66 SK |
104 | /* column IDs */ |
105 | enum { | |
106 | COL_DEVICE, | |
107 | COL_ID, | |
108 | COL_TYPE, | |
7d2a9960 | 109 | COL_DESC, |
b3849c66 SK |
110 | COL_SOFT, |
111 | COL_HARD | |
112 | }; | |
113 | ||
114 | /* column names */ | |
115 | struct colinfo { | |
116 | const char *name; /* header */ | |
117 | double whint; /* width hint (N < 1 is in percent of termwidth) */ | |
118 | int flags; /* SCOLS_FL_* */ | |
119 | const char *help; | |
120 | }; | |
121 | ||
122 | /* columns descriptions */ | |
123 | static const struct colinfo infos[] = { | |
124 | [COL_DEVICE] = {"DEVICE", 0, 0, N_("kernel device name")}, | |
7d8e3bcd | 125 | [COL_ID] = {"ID", 2, SCOLS_FL_RIGHT, N_("device identifier value")}, |
b3849c66 | 126 | [COL_TYPE] = {"TYPE", 0, 0, N_("device type name that can be used as identifier")}, |
7d2a9960 | 127 | [COL_DESC] = {"TYPE-DESC", 0, 0, N_("device type description")}, |
7d8e3bcd KZ |
128 | [COL_SOFT] = {"SOFT", 0, SCOLS_FL_RIGHT, N_("status of software block")}, |
129 | [COL_HARD] = {"HARD", 0, SCOLS_FL_RIGHT, N_("status of hardware block")} | |
b3849c66 SK |
130 | }; |
131 | ||
132 | static int columns[ARRAY_SIZE(infos) * 2]; | |
133 | static size_t ncolumns; | |
134 | ||
135 | struct control { | |
4f892d77 | 136 | struct libscols_table *tb; |
b3849c66 SK |
137 | unsigned int |
138 | json:1, | |
139 | no_headings:1, | |
140 | raw:1; | |
141 | }; | |
142 | ||
143 | static int column_name_to_id(const char *name, size_t namesz) | |
144 | { | |
145 | size_t i; | |
146 | ||
147 | assert(name); | |
148 | ||
149 | for (i = 0; i < ARRAY_SIZE(infos); i++) { | |
150 | const char *cn = infos[i].name; | |
151 | ||
152 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) | |
153 | return i; | |
154 | } | |
155 | warnx(_("unknown column: %s"), name); | |
156 | return -1; | |
157 | } | |
158 | ||
159 | static int get_column_id(size_t num) | |
160 | { | |
161 | assert(num < ncolumns); | |
162 | assert(columns[num] < (int)ARRAY_SIZE(infos)); | |
163 | return columns[num]; | |
164 | } | |
165 | ||
166 | static const struct colinfo *get_column_info(int num) | |
167 | { | |
168 | return &infos[get_column_id(num)]; | |
169 | } | |
170 | ||
18d3a03f KZ |
171 | static int string_to_action(const char *str) |
172 | { | |
173 | size_t i; | |
174 | ||
175 | for (i = 0; i < ARRAY_SIZE(rfkill_actions); i++) | |
176 | if (strcmp(str, rfkill_actions[i]) == 0) | |
177 | return i; | |
178 | ||
179 | return -EINVAL; | |
180 | } | |
181 | ||
49f4f7b6 KZ |
182 | static int rfkill_ro_open(int nonblock) |
183 | { | |
184 | int fd; | |
185 | ||
186 | fd = open(_PATH_DEV_RFKILL, O_RDONLY); | |
187 | if (fd < 0) { | |
188 | warn(_("cannot open %s"), _PATH_DEV_RFKILL); | |
189 | return -errno; | |
190 | } | |
191 | ||
192 | if (nonblock && fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { | |
193 | warn(_("cannot set non-blocking %s"), _PATH_DEV_RFKILL); | |
194 | close(fd); | |
195 | return -errno; | |
196 | } | |
197 | ||
198 | return fd; | |
199 | } | |
200 | ||
201 | /* returns: 0 success, 1 read again, < 0 error */ | |
202 | static int rfkill_read_event(int fd, struct rfkill_event *event) | |
203 | { | |
dec4fae2 | 204 | ssize_t len = read(fd, event, sizeof(*event)); |
49f4f7b6 KZ |
205 | |
206 | if (len < 0) { | |
207 | if (errno == EAGAIN) | |
208 | return 1; | |
209 | warn(_("cannot read %s"), _PATH_DEV_RFKILL); | |
210 | return -errno; | |
211 | } | |
212 | ||
213 | if (len < RFKILL_EVENT_SIZE_V1) { | |
214 | warnx(_("wrong size of rfkill event: %zu < %d"), len, RFKILL_EVENT_SIZE_V1); | |
215 | return 1; | |
216 | } | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
221 | ||
367f4080 | 222 | static int rfkill_event(void) |
07d517c0 | 223 | { |
51da1093 | 224 | struct rfkill_event event; |
6217bea3 | 225 | struct timeval tv; |
4111bb3a | 226 | char date_buf[ISO_BUFSIZ]; |
07d517c0 | 227 | struct pollfd p; |
3c5022c6 | 228 | int fd, n; |
07d517c0 | 229 | |
49f4f7b6 KZ |
230 | fd = rfkill_ro_open(0); |
231 | if (fd < 0) | |
3c5022c6 | 232 | return -errno; |
07d517c0 MH |
233 | |
234 | memset(&p, 0, sizeof(p)); | |
235 | p.fd = fd; | |
236 | p.events = POLLIN | POLLHUP; | |
237 | ||
3c5022c6 | 238 | /* interrupted by signal only */ |
07d517c0 | 239 | while (1) { |
49f4f7b6 KZ |
240 | int rc = 1; /* recover-able error */ |
241 | ||
07d517c0 MH |
242 | n = poll(&p, 1, -1); |
243 | if (n < 0) { | |
367f4080 | 244 | warn(_("failed to poll %s"), _PATH_DEV_RFKILL); |
3c5022c6 | 245 | goto failed; |
07d517c0 MH |
246 | } |
247 | ||
49f4f7b6 KZ |
248 | if (n) |
249 | rc = rfkill_read_event(fd, &event); | |
250 | if (rc < 0) | |
3c5022c6 | 251 | goto failed; |
49f4f7b6 | 252 | if (rc) |
07d517c0 | 253 | continue; |
49f4f7b6 | 254 | |
6217bea3 | 255 | gettimeofday(&tv, NULL); |
4111bb3a WP |
256 | strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA, date_buf, |
257 | sizeof(date_buf)); | |
7d0b3acb SK |
258 | printf("%s: idx %u type %u op %u soft %u hard %u\n", |
259 | date_buf, | |
260 | event.idx, event.type, event.op, event.soft, event.hard); | |
1634aef2 | 261 | fflush(stdout); |
07d517c0 MH |
262 | } |
263 | ||
3c5022c6 | 264 | failed: |
07d517c0 | 265 | close(fd); |
3c5022c6 | 266 | return -1; |
07d517c0 MH |
267 | } |
268 | ||
7d2a9960 | 269 | static const char *get_sys_attr(uint32_t idx, const char *attr) |
07d517c0 | 270 | { |
7d2a9960 KZ |
271 | static char name[128]; |
272 | FILE *f = path_fopen("r", 0, _PATH_SYS_RFKILL "/rfkill%u/%s", idx, attr); | |
273 | char *p; | |
274 | ||
275 | if (!f) | |
276 | goto done; | |
277 | if (!fgets(name, sizeof(name), f)) | |
278 | goto done; | |
279 | p = strchr(name, '\n'); | |
280 | if (p) | |
281 | *p = '\0'; | |
282 | done: | |
283 | if (f) | |
284 | fclose(f); | |
07d517c0 MH |
285 | return name; |
286 | } | |
287 | ||
fafd9121 DS |
288 | static struct rfkill_id rfkill_id_to_type(const char *s) |
289 | { | |
290 | const struct rfkill_type_str *p; | |
291 | struct rfkill_id ret; | |
292 | ||
293 | if (islower(*s)) { | |
294 | for (p = rfkill_type_strings; p->name != NULL; p++) { | |
367f4080 | 295 | if (!strcmp(s, p->name)) { |
fafd9121 | 296 | ret.type = p->type; |
b03378e7 SK |
297 | if (!strcmp(s, "all")) |
298 | ret.result = RFKILL_IS_ALL; | |
299 | else | |
300 | ret.result = RFKILL_IS_TYPE; | |
fafd9121 DS |
301 | return ret; |
302 | } | |
303 | } | |
304 | } else if (isdigit(*s)) { | |
305 | /* assume a numeric character implies an index. */ | |
f806a238 SK |
306 | char filename[64]; |
307 | ||
367f4080 | 308 | ret.index = strtou32_or_err(s, _("invalid identifier")); |
f806a238 SK |
309 | snprintf(filename, sizeof(filename) - 1, |
310 | _PATH_SYS_RFKILL "/rfkill%" PRIu32 "/name", ret.index); | |
311 | if (access(filename, F_OK) == 0) | |
312 | ret.result = RFKILL_IS_INDEX; | |
313 | else | |
314 | ret.result = RFKILL_IS_INVALID; | |
fafd9121 DS |
315 | return ret; |
316 | } | |
317 | ||
318 | ret.result = RFKILL_IS_INVALID; | |
319 | return ret; | |
320 | } | |
321 | ||
7d2a9960 KZ |
322 | static const char *rfkill_type_to_desc(enum rfkill_type type) |
323 | { | |
324 | size_t i; | |
325 | ||
326 | for (i = 0; i < ARRAY_SIZE(rfkill_type_strings); i++) { | |
327 | if (type == rfkill_type_strings[i].type) | |
328 | return rfkill_type_strings[i].desc; | |
329 | } | |
330 | ||
331 | return NULL; | |
332 | } | |
333 | ||
334 | ||
49f4f7b6 KZ |
335 | static int event_match(struct rfkill_event *event, struct rfkill_id *id) |
336 | { | |
337 | if (event->op != RFKILL_OP_ADD) | |
338 | return 0; | |
339 | ||
340 | /* filter out unwanted results */ | |
341 | switch (id->result) { | |
342 | case RFKILL_IS_TYPE: | |
343 | if (event->type != id->type) | |
344 | return 0; | |
345 | break; | |
346 | case RFKILL_IS_INDEX: | |
347 | if (event->idx != id->index) | |
348 | return 0; | |
349 | break; | |
350 | case RFKILL_IS_ALL: | |
351 | break; | |
352 | default: | |
353 | abort(); | |
354 | } | |
355 | ||
356 | return 1; | |
357 | } | |
358 | ||
b3849c66 SK |
359 | static void fill_table_row(struct libscols_table *tb, struct rfkill_event *event) |
360 | { | |
361 | static struct libscols_line *ln; | |
362 | size_t i; | |
363 | ||
364 | assert(tb); | |
365 | ||
366 | ln = scols_table_new_line(tb, NULL); | |
367 | if (!ln) { | |
368 | errno = ENOMEM; | |
369 | errx(EXIT_FAILURE, _("failed to allocate output line")); | |
370 | } | |
371 | ||
372 | for (i = 0; i < (size_t)ncolumns; i++) { | |
373 | char *str = NULL; | |
374 | switch (get_column_id(i)) { | |
375 | case COL_DEVICE: | |
7d2a9960 | 376 | str = xstrdup(get_sys_attr(event->idx, "name")); |
b3849c66 SK |
377 | break; |
378 | case COL_ID: | |
379 | xasprintf(&str, "%" PRIu32, event->idx); | |
380 | break; | |
381 | case COL_TYPE: | |
7d2a9960 KZ |
382 | str = xstrdup(get_sys_attr(event->idx, "type")); |
383 | break; | |
384 | case COL_DESC: | |
385 | str = xstrdup(rfkill_type_to_desc(event->type)); | |
b3849c66 SK |
386 | break; |
387 | case COL_SOFT: | |
388 | str = xstrdup(event->soft ? _("blocked") : _("unblocked")); | |
389 | break; | |
390 | case COL_HARD: | |
391 | str = xstrdup(event->hard ? _("blocked") : _("unblocked")); | |
392 | break; | |
393 | default: | |
394 | abort(); | |
395 | } | |
396 | if (str && scols_line_refer_data(ln, i, str)) | |
397 | errx(EXIT_FAILURE, _("failed to add output data")); | |
398 | } | |
399 | } | |
400 | ||
7d2a9960 KZ |
401 | static int rfkill_list_old(const char *param) |
402 | { | |
403 | struct rfkill_id id = { .result = RFKILL_IS_ALL }; | |
404 | struct rfkill_event event; | |
405 | int fd, rc = 0; | |
406 | ||
407 | if (param) { | |
408 | id = rfkill_id_to_type(param); | |
409 | if (id.result == RFKILL_IS_INVALID) { | |
410 | warnx(_("invalid identifier: %s"), param); | |
411 | return -EINVAL; | |
412 | } | |
413 | } | |
414 | ||
415 | fd = rfkill_ro_open(1); | |
416 | ||
417 | while (1) { | |
418 | rc = rfkill_read_event(fd, &event); | |
419 | if (rc < 0) | |
420 | break; | |
421 | if (rc == 1 && errno == EAGAIN) { | |
422 | rc = 0; /* done */ | |
423 | break; | |
424 | } | |
425 | if (rc == 0 && event_match(&event, &id)) { | |
426 | char *name = xstrdup(get_sys_attr(event.idx, "name")), | |
427 | *type = xstrdup(rfkill_type_to_desc(event.type)); | |
428 | ||
429 | if (!type) | |
430 | type = xstrdup(get_sys_attr(event.idx, "type")); | |
431 | ||
432 | printf("%u: %s: %s\n", event.idx, name, type); | |
433 | printf("\tSoft blocked: %s\n", event.soft ? "yes" : "no"); | |
434 | printf("\tHard blocked: %s\n", event.hard ? "yes" : "no"); | |
435 | ||
436 | free(name); | |
437 | free(type); | |
438 | } | |
439 | } | |
440 | close(fd); | |
441 | return rc; | |
442 | } | |
443 | ||
4f892d77 | 444 | static void rfkill_list_init(struct control *ctrl) |
07d517c0 | 445 | { |
e8f6060f KZ |
446 | size_t i; |
447 | ||
b3849c66 | 448 | scols_init_debug(0); |
e8f6060f | 449 | |
4f892d77 SK |
450 | ctrl->tb = scols_new_table(); |
451 | if (!ctrl->tb) | |
b3849c66 SK |
452 | err(EXIT_FAILURE, _("failed to allocate output table")); |
453 | ||
4f892d77 SK |
454 | scols_table_enable_json(ctrl->tb, ctrl->json); |
455 | scols_table_enable_noheadings(ctrl->tb, ctrl->no_headings); | |
456 | scols_table_enable_raw(ctrl->tb, ctrl->raw); | |
b3849c66 | 457 | |
e8f6060f KZ |
458 | for (i = 0; i < (size_t) ncolumns; i++) { |
459 | const struct colinfo *col = get_column_info(i); | |
b3849c66 | 460 | |
e8f6060f KZ |
461 | if (!scols_table_new_column(ctrl->tb, col->name, col->whint, col->flags)) |
462 | err(EXIT_FAILURE, _("failed to allocate output column")); | |
b3849c66 | 463 | } |
4f892d77 SK |
464 | } |
465 | ||
4f892d77 SK |
466 | static int rfkill_list_fill(struct control const *ctrl, const char *param) |
467 | { | |
468 | struct rfkill_id id = { .result = RFKILL_IS_ALL }; | |
469 | struct rfkill_event event; | |
49f4f7b6 | 470 | int fd, rc = 0; |
07d517c0 | 471 | |
a9405c24 DS |
472 | if (param) { |
473 | id = rfkill_id_to_type(param); | |
474 | if (id.result == RFKILL_IS_INVALID) { | |
367f4080 | 475 | warnx(_("invalid identifier: %s"), param); |
3c5022c6 | 476 | return -EINVAL; |
a9405c24 | 477 | } |
a9405c24 DS |
478 | } |
479 | ||
49f4f7b6 | 480 | fd = rfkill_ro_open(1); |
07d517c0 MH |
481 | |
482 | while (1) { | |
49f4f7b6 KZ |
483 | rc = rfkill_read_event(fd, &event); |
484 | if (rc < 0) | |
a9405c24 | 485 | break; |
49f4f7b6 KZ |
486 | if (rc == 1 && errno == EAGAIN) { |
487 | rc = 0; /* done */ | |
a9405c24 | 488 | break; |
a9405c24 | 489 | } |
49f4f7b6 KZ |
490 | if (rc == 0 && event_match(&event, &id)) |
491 | fill_table_row(ctrl->tb, &event); | |
07d517c0 | 492 | } |
07d517c0 | 493 | close(fd); |
49f4f7b6 | 494 | return rc; |
07d517c0 MH |
495 | } |
496 | ||
4f892d77 SK |
497 | static void rfkill_list_output(struct control const *ctrl) |
498 | { | |
499 | scols_print_table(ctrl->tb); | |
500 | scols_unref_table(ctrl->tb); | |
501 | } | |
502 | ||
367f4080 | 503 | static int rfkill_block(uint8_t block, const char *param) |
07d517c0 | 504 | { |
fafd9121 | 505 | struct rfkill_id id; |
b03378e7 SK |
506 | struct rfkill_event event = { |
507 | .op = RFKILL_OP_CHANGE_ALL, | |
508 | .soft = block, | |
509 | 0 | |
510 | }; | |
07d517c0 MH |
511 | ssize_t len; |
512 | int fd; | |
fd3d4ec7 | 513 | char *message = NULL; |
07d517c0 | 514 | |
fafd9121 | 515 | id = rfkill_id_to_type(param); |
07d517c0 | 516 | |
fafd9121 | 517 | switch (id.result) { |
b03378e7 SK |
518 | case RFKILL_IS_INVALID: |
519 | warnx(_("invalid identifier: %s"), param); | |
3c5022c6 | 520 | return -1; |
fafd9121 | 521 | case RFKILL_IS_TYPE: |
fafd9121 | 522 | event.type = id.type; |
fd3d4ec7 | 523 | xasprintf(&message, "type %s", param); |
fafd9121 DS |
524 | break; |
525 | case RFKILL_IS_INDEX: | |
526 | event.op = RFKILL_OP_CHANGE; | |
527 | event.idx = id.index; | |
fd3d4ec7 | 528 | xasprintf(&message, "id %d", id.index); |
fafd9121 | 529 | break; |
b03378e7 | 530 | case RFKILL_IS_ALL: |
fd3d4ec7 | 531 | message = xstrdup("all"); |
367f4080 SK |
532 | break; |
533 | default: | |
534 | abort(); | |
94297403 | 535 | } |
b03378e7 SK |
536 | |
537 | fd = open(_PATH_DEV_RFKILL, O_RDWR); | |
538 | if (fd < 0) { | |
539 | warn(_("cannot open %s"), _PATH_DEV_RFKILL); | |
fd3d4ec7 | 540 | free(message); |
3c5022c6 | 541 | return -errno; |
b03378e7 | 542 | } |
07d517c0 MH |
543 | |
544 | len = write(fd, &event, sizeof(event)); | |
545 | if (len < 0) | |
367f4080 | 546 | warn(_("write failed: %s"), _PATH_DEV_RFKILL); |
3c5022c6 KZ |
547 | else { |
548 | openlog("rfkill", 0, LOG_USER); | |
549 | syslog(LOG_NOTICE, "%s set for %s", block ? "block" : "unblock", message); | |
550 | closelog(); | |
551 | } | |
fd3d4ec7 | 552 | free(message); |
b1209668 | 553 | return close(fd); |
07d517c0 MH |
554 | } |
555 | ||
367f4080 | 556 | static void __attribute__((__noreturn__)) usage(void) |
07d517c0 | 557 | { |
b3849c66 | 558 | size_t i; |
47eaa870 | 559 | |
367f4080 | 560 | fputs(USAGE_HEADER, stdout); |
4f892d77 | 561 | fprintf(stdout, _(" %s [options] command [identifier ...]\n"), program_invocation_short_name); |
367f4080 SK |
562 | |
563 | fputs(USAGE_SEPARATOR, stdout); | |
564 | fputs(_("Tool for enabling and disabling wireless devices.\n"), stdout); | |
565 | ||
b3849c66 SK |
566 | fputs(USAGE_OPTIONS, stdout); |
567 | fputs(_(" -J, --json use JSON output format\n"), stdout); | |
568 | fputs(_(" -n, --noheadings don't print headings\n"), stdout); | |
569 | fputs(_(" -o, --output <list> define which output columns to use\n"), stdout); | |
570 | fputs(_(" -r, --raw use the raw output format\n"), stdout); | |
571 | ||
572 | fputs(USAGE_SEPARATOR, stdout); | |
fcc3efb4 | 573 | printf(USAGE_HELP_OPTIONS(24)); |
b3849c66 | 574 | |
fcc3efb4 | 575 | fputs(USAGE_COLUMNS, stdout); |
b3849c66 | 576 | for (i = 0; i < ARRAY_SIZE(infos); i++) |
7d2a9960 | 577 | fprintf(stdout, " %-10s %s\n", infos[i].name, _(infos[i].help)); |
b3849c66 | 578 | |
fcc3efb4 KZ |
579 | fputs(USAGE_COMMANDS, stdout); |
580 | ||
367f4080 SK |
581 | /* |
582 | * TRANSLATORS: command names should not be translated, explaining | |
583 | * them as additional field after identifer is fine, for example | |
584 | * | |
585 | * list [identifier] (lista [tarkenne]) | |
586 | */ | |
587 | fputs(_(" help\n"), stdout); | |
588 | fputs(_(" event\n"), stdout); | |
589 | fputs(_(" list [identifier]\n"), stdout); | |
590 | fputs(_(" block identifier\n"), stdout); | |
591 | fputs(_(" unblock identifier\n"), stdout); | |
592 | ||
367f4080 SK |
593 | fprintf(stdout, USAGE_MAN_TAIL("rfkill(8)")); |
594 | exit(EXIT_SUCCESS); | |
d5fad6eb JB |
595 | } |
596 | ||
597 | int main(int argc, char **argv) | |
598 | { | |
b3849c66 | 599 | struct control ctrl = { 0 }; |
18d3a03f | 600 | int c, act = ACT_LIST; |
b3849c66 | 601 | char *outarg = NULL; |
367f4080 | 602 | static const struct option longopts[] = { |
b3849c66 SK |
603 | { "json", no_argument, NULL, 'J' }, |
604 | { "noheadings", no_argument, NULL, 'n' }, | |
605 | { "output", required_argument, NULL, 'o' }, | |
606 | { "raw", no_argument, NULL, 'r' }, | |
607 | { "version", no_argument, NULL, 'V' }, | |
608 | { "help", no_argument, NULL, 'h' }, | |
367f4080 SK |
609 | { NULL, 0, NULL, 0 } |
610 | }; | |
b3849c66 SK |
611 | static const ul_excl_t excl[] = { |
612 | {'J', 'r'}, | |
613 | {0} | |
614 | }; | |
615 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
4f892d77 | 616 | int ret = 0; |
367f4080 SK |
617 | |
618 | setlocale(LC_ALL, ""); | |
619 | bindtextdomain(PACKAGE, LOCALEDIR); | |
620 | textdomain(PACKAGE); | |
621 | atexit(close_stdout); | |
622 | ||
b3849c66 SK |
623 | while ((c = getopt_long(argc, argv, "Jno:rVh", longopts, NULL)) != -1) { |
624 | err_exclusive_options(c, longopts, excl, excl_st); | |
367f4080 | 625 | switch (c) { |
b3849c66 SK |
626 | case 'J': |
627 | ctrl.json = 1; | |
628 | break; | |
629 | case 'n': | |
630 | ctrl.no_headings = 1; | |
631 | break; | |
632 | case 'o': | |
633 | outarg = optarg; | |
634 | break; | |
635 | case 'r': | |
636 | ctrl.raw = 1; | |
637 | break; | |
367f4080 SK |
638 | case 'V': |
639 | printf(UTIL_LINUX_VERSION); | |
640 | return EXIT_SUCCESS; | |
641 | case 'h': | |
642 | usage(); | |
643 | default: | |
644 | errtryhelp(EXIT_FAILURE); | |
645 | } | |
b3849c66 SK |
646 | } |
647 | argc -= optind; | |
648 | argv += optind; | |
649 | ||
18d3a03f KZ |
650 | if (argc > 0) { |
651 | act = string_to_action(*argv); | |
652 | if (act < 0) | |
653 | errtryhelp(EXIT_FAILURE); | |
654 | argv++; | |
655 | argc--; | |
7d2a9960 KZ |
656 | |
657 | /* | |
658 | * For backward compatibility we use old output format if | |
659 | * "list" explicitly specified and--output not defined. | |
660 | */ | |
661 | if (!outarg && act == ACT_LIST) | |
662 | act = ACT_LIST_OLD; | |
18d3a03f KZ |
663 | } |
664 | ||
665 | switch (act) { | |
7d2a9960 KZ |
666 | case ACT_LIST_OLD: |
667 | /* Deprecated in favour of ACT_LIST */ | |
668 | if (!argc) | |
669 | ret |= rfkill_list_old(NULL); /* ALL */ | |
670 | else while (argc) { | |
671 | ret |= rfkill_list_old(*argv); | |
672 | argc--; | |
673 | argv++; | |
674 | } | |
675 | break; | |
676 | ||
18d3a03f | 677 | case ACT_LIST: |
b3849c66 SK |
678 | columns[ncolumns++] = COL_ID; |
679 | columns[ncolumns++] = COL_TYPE; | |
7d8e3bcd | 680 | columns[ncolumns++] = COL_DEVICE; |
b3849c66 SK |
681 | columns[ncolumns++] = COL_SOFT; |
682 | columns[ncolumns++] = COL_HARD; | |
683 | ||
684 | if (outarg | |
685 | && string_add_to_idarray(outarg, columns, | |
686 | ARRAY_SIZE(columns), &ncolumns, | |
687 | column_name_to_id) < 0) | |
688 | return EXIT_FAILURE; | |
18d3a03f | 689 | |
4f892d77 | 690 | rfkill_list_init(&ctrl); |
18d3a03f KZ |
691 | if (!argc) |
692 | ret |= rfkill_list_fill(&ctrl, NULL); /* ALL */ | |
693 | else while (argc) { | |
4f892d77 | 694 | ret |= rfkill_list_fill(&ctrl, *argv); |
18d3a03f KZ |
695 | argc--; |
696 | argv++; | |
b3849c66 | 697 | } |
4f892d77 | 698 | rfkill_list_output(&ctrl); |
18d3a03f KZ |
699 | break; |
700 | ||
701 | case ACT_EVENT: | |
367f4080 | 702 | ret = rfkill_event(); |
18d3a03f KZ |
703 | break; |
704 | ||
705 | case ACT_HELP: | |
b3849c66 | 706 | usage(); |
18d3a03f KZ |
707 | break; |
708 | ||
709 | case ACT_BLOCK: | |
710 | while (argc) { | |
4f892d77 | 711 | ret |= rfkill_block(1, *argv); |
18d3a03f | 712 | argc--; |
4f892d77 | 713 | argv++; |
18d3a03f KZ |
714 | } |
715 | break; | |
716 | ||
717 | case ACT_UNBLOCK: | |
718 | while (argc) { | |
4f892d77 | 719 | ret |= rfkill_block(0, *argv); |
18d3a03f KZ |
720 | argv++; |
721 | argc--; | |
4f892d77 | 722 | } |
18d3a03f KZ |
723 | break; |
724 | } | |
07d517c0 | 725 | |
3c5022c6 | 726 | return ret ? EXIT_FAILURE : EXIT_SUCCESS; |
d5fad6eb | 727 | } |