]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/rfkill/rfkill.c
tree-wide: drop 'This file is part of systemd' blurb
[thirdparty/systemd.git] / src / rfkill / rfkill.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
3990f247 2/***
3990f247 3 Copyright 2013 Lennart Poettering
3990f247
LP
4***/
5
d35c1bb1
LP
6#include <linux/rfkill.h>
7#include <poll.h>
8
b4bbcaa9 9#include "libudev.h"
d35c1bb1
LP
10#include "sd-daemon.h"
11
b5efdb8a 12#include "alloc-util.h"
4f5dd394 13#include "escape.h"
3ffd4af2 14#include "fd-util.h"
d35c1bb1 15#include "fileio.h"
c004493c 16#include "io-util.h"
d35c1bb1 17#include "mkdir.h"
6bedfcbb 18#include "parse-util.h"
4e731273 19#include "proc-cmdline.h"
8b43440b 20#include "string-table.h"
07630cea 21#include "string-util.h"
3990f247 22#include "udev-util.h"
d35c1bb1 23#include "util.h"
202cb8c3 24#include "list.h"
3990f247 25
202cb8c3
BB
26/* Note that any write is delayed until exit and the rfkill state will not be
27 * stored for rfkill indices that disappear after a change. */
d35c1bb1 28#define EXIT_USEC (5 * USEC_PER_SEC)
3990f247 29
202cb8c3
BB
30typedef struct write_queue_item {
31 LIST_FIELDS(struct write_queue_item, queue);
32 int rfkill_idx;
33 char *file;
34 int state;
35} write_queue_item;
36
3b3d1737
LP
37static struct write_queue_item* write_queue_item_free(struct write_queue_item *item) {
38 if (!item)
39 return NULL;
202cb8c3
BB
40
41 free(item->file);
3b3d1737 42 return mfree(item);
202cb8c3
BB
43}
44
d35c1bb1
LP
45static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
46 [RFKILL_TYPE_ALL] = "all",
47 [RFKILL_TYPE_WLAN] = "wlan",
48 [RFKILL_TYPE_BLUETOOTH] = "bluetooth",
49 [RFKILL_TYPE_UWB] = "uwb",
50 [RFKILL_TYPE_WIMAX] = "wimax",
51 [RFKILL_TYPE_WWAN] = "wwan",
52 [RFKILL_TYPE_GPS] = "gps",
ac9455ef
TA
53 [RFKILL_TYPE_FM] = "fm",
54 [RFKILL_TYPE_NFC] = "nfc",
d35c1bb1 55};
3990f247 56
d35c1bb1 57DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
3990f247 58
d35c1bb1
LP
59static int find_device(
60 struct udev *udev,
61 const struct rfkill_event *event,
62 struct udev_device **ret) {
3990f247 63
d35c1bb1
LP
64 _cleanup_free_ char *sysname = NULL;
65 struct udev_device *device;
66 const char *name;
3990f247 67
d35c1bb1
LP
68 assert(udev);
69 assert(event);
70 assert(ret);
3990f247 71
d35c1bb1
LP
72 if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
73 return log_oom();
74
75 device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
76 if (!device)
bc5e002e
LP
77 return log_full_errno(IN_SET(errno, ENOENT, ENXIO, ENODEV) ? LOG_DEBUG : LOG_ERR, errno,
78 "Failed to open device '%s': %m", sysname);
3990f247
LP
79
80 name = udev_device_get_sysattr_value(device, "name");
81 if (!name) {
d35c1bb1
LP
82 log_debug("Device has no name, ignoring.");
83 udev_device_unref(device);
84 return -ENOENT;
4844262f
LP
85 }
86
87 log_debug("Operating on rfkill device '%s'.", name);
88
d35c1bb1
LP
89 *ret = device;
90 return 0;
91}
92
93static int wait_for_initialized(
94 struct udev *udev,
95 struct udev_device *device,
96 struct udev_device **ret) {
97
8e766630 98 _cleanup_(udev_monitor_unrefp) struct udev_monitor *monitor = NULL;
d35c1bb1
LP
99 struct udev_device *d;
100 const char *sysname;
101 int watch_fd, r;
102
103 assert(udev);
104 assert(device);
105 assert(ret);
106
107 if (udev_device_get_is_initialized(device) != 0) {
108 *ret = udev_device_ref(device);
109 return 0;
3990f247
LP
110 }
111
d35c1bb1
LP
112 assert_se(sysname = udev_device_get_sysname(device));
113
114 /* Wait until the device is initialized, so that we can get
115 * access to the ID_PATH property */
116
117 monitor = udev_monitor_new_from_netlink(udev, "udev");
118 if (!monitor)
119 return log_error_errno(errno, "Failed to acquire monitor: %m");
120
121 r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL);
122 if (r < 0)
123 return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m");
124
125 r = udev_monitor_enable_receiving(monitor);
126 if (r < 0)
127 return log_error_errno(r, "Failed to enable udev receiving: %m");
128
129 watch_fd = udev_monitor_get_fd(monitor);
130 if (watch_fd < 0)
131 return log_error_errno(watch_fd, "Failed to get watch fd: %m");
132
133 /* Check again, maybe things changed */
134 d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
135 if (!d)
bc5e002e
LP
136 return log_full_errno(IN_SET(errno, ENOENT, ENXIO, ENODEV) ? LOG_DEBUG : LOG_ERR, errno,
137 "Failed to open device '%s': %m", sysname);
d35c1bb1
LP
138
139 if (udev_device_get_is_initialized(d) != 0) {
140 *ret = d;
141 return 0;
142 }
143
144 for (;;) {
8e766630 145 _cleanup_(udev_device_unrefp) struct udev_device *t = NULL;
d35c1bb1 146
8ec1a079 147 r = fd_wait_for_event(watch_fd, POLLIN, EXIT_USEC);
d35c1bb1
LP
148 if (r == -EINTR)
149 continue;
150 if (r < 0)
151 return log_error_errno(r, "Failed to watch udev monitor: %m");
8ec1a079 152 if (r == 0) {
c7f6ca93 153 log_error("Timed out waiting for udev monitor.");
8ec1a079
F
154 return -ETIMEDOUT;
155 }
d35c1bb1
LP
156
157 t = udev_monitor_receive_device(monitor);
158 if (!t)
159 continue;
160
8ec1a079 161 if (streq_ptr(udev_device_get_sysname(t), sysname)) {
d35c1bb1
LP
162 *ret = udev_device_ref(t);
163 return 0;
164 }
3990f247 165 }
d35c1bb1
LP
166}
167
168static int determine_state_file(
169 struct udev *udev,
170 const struct rfkill_event *event,
d35c1bb1
LP
171 char **ret) {
172
8e766630
LP
173 _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
174 _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
d35c1bb1
LP
175 const char *path_id, *type;
176 char *state_file;
177 int r;
178
179 assert(event);
d35c1bb1
LP
180 assert(ret);
181
8e707663
BB
182 r = find_device(udev, event, &d);
183 if (r < 0)
184 return r;
185
d35c1bb1
LP
186 r = wait_for_initialized(udev, d, &device);
187 if (r < 0)
188 return r;
189
190 assert_se(type = rfkill_type_to_string(event->type));
3990f247 191
f6f738db
LP
192 path_id = udev_device_get_property_value(device, "ID_PATH");
193 if (path_id) {
d35c1bb1
LP
194 _cleanup_free_ char *escaped_path_id = NULL;
195
f6f738db 196 escaped_path_id = cescape(path_id);
d35c1bb1
LP
197 if (!escaped_path_id)
198 return log_oom();
f6f738db 199
605405c6 200 state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type);
f6f738db 201 } else
605405c6 202 state_file = strjoin("/var/lib/systemd/rfkill/", type);
d35c1bb1
LP
203
204 if (!state_file)
205 return log_oom();
206
207 *ret = state_file;
208 return 0;
209}
210
211static int load_state(
212 int rfkill_fd,
213 struct udev *udev,
214 const struct rfkill_event *event) {
215
d35c1bb1
LP
216 _cleanup_free_ char *state_file = NULL, *value = NULL;
217 struct rfkill_event we;
218 ssize_t l;
219 int b, r;
f6f738db 220
d35c1bb1
LP
221 assert(rfkill_fd >= 0);
222 assert(udev);
223 assert(event);
224
934ae16b 225 if (shall_restore_state() == 0)
d35c1bb1
LP
226 return 0;
227
8e707663 228 r = determine_state_file(udev, event, &state_file);
d35c1bb1
LP
229 if (r < 0)
230 return r;
231
232 r = read_one_line_file(state_file, &value);
233 if (r == -ENOENT) {
234 /* No state file? Then save the current state */
235
236 r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
237 if (r < 0)
238 return log_error_errno(r, "Failed to write state file %s: %m", state_file);
239
240 log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
241 return 0;
242 }
243 if (r < 0)
244 return log_error_errno(r, "Failed to read state file %s: %m", state_file);
245
246 b = parse_boolean(value);
247 if (b < 0)
248 return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
249
250 we = (struct rfkill_event) {
251 .op = RFKILL_OP_CHANGE,
252 .idx = event->idx,
253 .soft = b,
254 };
255
256 l = write(rfkill_fd, &we, sizeof(we));
257 if (l < 0)
258 return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
259 if (l != sizeof(we)) {
260 log_error("Couldn't write rfkill event structure, too short.");
261 return -EIO;
262 }
263
264 log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
265 return 0;
266}
267
202cb8c3
BB
268static void save_state_queue_remove(
269 struct write_queue_item **write_queue,
270 int idx,
271 char *state_file) {
272
273 struct write_queue_item *item, *tmp;
274
275 LIST_FOREACH_SAFE(queue, item, tmp, *write_queue) {
276 if ((state_file && streq(item->file, state_file)) || idx == item->rfkill_idx) {
277 log_debug("Canceled previous save state of '%s' to %s.", one_zero(item->state), item->file);
278 LIST_REMOVE(queue, *write_queue, item);
279 write_queue_item_free(item);
280 }
281 }
282}
283
284static int save_state_queue(
285 struct write_queue_item **write_queue,
d35c1bb1
LP
286 int rfkill_fd,
287 struct udev *udev,
288 const struct rfkill_event *event) {
289
d35c1bb1 290 _cleanup_free_ char *state_file = NULL;
202cb8c3 291 struct write_queue_item *item;
d35c1bb1
LP
292 int r;
293
294 assert(rfkill_fd >= 0);
295 assert(udev);
296 assert(event);
297
8e707663 298 r = determine_state_file(udev, event, &state_file);
d35c1bb1
LP
299 if (r < 0)
300 return r;
bc5e002e 301
202cb8c3 302 save_state_queue_remove(write_queue, event->idx, state_file);
d35c1bb1 303
202cb8c3
BB
304 item = new0(struct write_queue_item, 1);
305 if (!item)
306 return -ENOMEM;
307
1cc6c93a 308 item->file = TAKE_PTR(state_file);
202cb8c3
BB
309 item->rfkill_idx = event->idx;
310 item->state = event->soft;
202cb8c3
BB
311
312 LIST_APPEND(queue, *write_queue, item);
313
314 return 0;
315}
316
317static int save_state_cancel(
318 struct write_queue_item **write_queue,
319 int rfkill_fd,
320 struct udev *udev,
321 const struct rfkill_event *event) {
322
323 _cleanup_free_ char *state_file = NULL;
324 int r;
325
326 assert(rfkill_fd >= 0);
327 assert(udev);
328 assert(event);
329
330 r = determine_state_file(udev, event, &state_file);
331 save_state_queue_remove(write_queue, event->idx, state_file);
d35c1bb1 332 if (r < 0)
202cb8c3 333 return r;
d35c1bb1 334
d35c1bb1
LP
335 return 0;
336}
337
202cb8c3
BB
338static int save_state_write(struct write_queue_item **write_queue) {
339 struct write_queue_item *item, *tmp;
340 int result = 0;
341 bool error_logged = false;
342 int r;
343
344 LIST_FOREACH_SAFE(queue, item, tmp, *write_queue) {
345 r = write_string_file(item->file, one_zero(item->state), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
346 if (r < 0) {
347 result = r;
348 if (!error_logged) {
349 log_error_errno(r, "Failed to write state file %s: %m", item->file);
350 error_logged = true;
351 } else
352 log_warning_errno(r, "Failed to write state file %s: %m", item->file);
353 } else
354 log_debug("Saved state '%s' to %s.", one_zero(item->state), item->file);
355
356 LIST_REMOVE(queue, *write_queue, item);
357 write_queue_item_free(item);
358 }
359 return result;
360}
361
d35c1bb1 362int main(int argc, char *argv[]) {
202cb8c3 363 LIST_HEAD(write_queue_item, write_queue);
8e766630 364 _cleanup_(udev_unrefp) struct udev *udev = NULL;
d35c1bb1
LP
365 _cleanup_close_ int rfkill_fd = -1;
366 bool ready = false;
367 int r, n;
368
369 if (argc > 1) {
370 log_error("This program requires no arguments.");
3990f247
LP
371 return EXIT_FAILURE;
372 }
373
202cb8c3
BB
374 LIST_HEAD_INIT(write_queue);
375
d35c1bb1
LP
376 log_set_target(LOG_TARGET_AUTO);
377 log_parse_environment();
378 log_open();
3990f247 379
d35c1bb1 380 umask(0022);
354806fb 381
d35c1bb1
LP
382 udev = udev_new();
383 if (!udev) {
384 r = log_oom();
385 goto finish;
386 }
387
388 r = mkdir_p("/var/lib/systemd/rfkill", 0755);
389 if (r < 0) {
390 log_error_errno(r, "Failed to create rfkill directory: %m");
391 goto finish;
392 }
393
394 n = sd_listen_fds(false);
395 if (n < 0) {
396 r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
397 goto finish;
398 }
399 if (n > 1) {
400 log_error("Got too many file descriptors.");
401 r = -EINVAL;
402 goto finish;
403 }
404
405 if (n == 0) {
406 rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
407 if (rfkill_fd < 0) {
408 if (errno == ENOENT) {
409 log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
410 r = 0;
411 goto finish;
412 }
413
414 r = log_error_errno(errno, "Failed to open /dev/rfkill: %m");
415 goto finish;
3990f247 416 }
d35c1bb1
LP
417 } else {
418 rfkill_fd = SD_LISTEN_FDS_START;
3990f247 419
d35c1bb1 420 r = fd_nonblock(rfkill_fd, 1);
3990f247 421 if (r < 0) {
d35c1bb1
LP
422 log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
423 goto finish;
3990f247 424 }
d35c1bb1
LP
425 }
426
427 for (;;) {
428 struct rfkill_event event;
429 const char *type;
430 ssize_t l;
3990f247 431
d35c1bb1
LP
432 l = read(rfkill_fd, &event, sizeof(event));
433 if (l < 0) {
434 if (errno == EAGAIN) {
3990f247 435
d35c1bb1
LP
436 if (!ready) {
437 /* Notify manager that we are
438 * now finished with
439 * processing whatever was
440 * queued */
441 (void) sd_notify(false, "READY=1");
442 ready = true;
443 }
444
445 /* Hang around for a bit, maybe there's more coming */
446
447 r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC);
448 if (r == -EINTR)
449 continue;
450 if (r < 0) {
451 log_error_errno(r, "Failed to poll() on device: %m");
452 goto finish;
453 }
454 if (r > 0)
455 continue;
456
457 log_debug("All events read and idle, exiting.");
458 break;
459 }
460
461 log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
3990f247
LP
462 }
463
d35c1bb1
LP
464 if (l != RFKILL_EVENT_SIZE_V1) {
465 log_error("Read event structure of invalid size.");
466 r = -EIO;
467 goto finish;
3990f247
LP
468 }
469
d35c1bb1
LP
470 type = rfkill_type_to_string(event.type);
471 if (!type) {
472 log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type);
473 continue;
474 }
475
476 switch (event.op) {
477
478 case RFKILL_OP_ADD:
479 log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
480 (void) load_state(rfkill_fd, udev, &event);
481 break;
482
483 case RFKILL_OP_DEL:
484 log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
202cb8c3 485 (void) save_state_cancel(&write_queue, rfkill_fd, udev, &event);
d35c1bb1
LP
486 break;
487
488 case RFKILL_OP_CHANGE:
489 log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
202cb8c3 490 (void) save_state_queue(&write_queue, rfkill_fd, udev, &event);
d35c1bb1
LP
491 break;
492
493 default:
494 log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type);
495 break;
496 }
3990f247
LP
497 }
498
d35c1bb1
LP
499 r = 0;
500
501finish:
202cb8c3
BB
502 (void) save_state_write(&write_queue);
503
d35c1bb1 504 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
3990f247 505}