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