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