]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/rfkill/rfkill.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[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 "main-func.h"
16 #include "mkdir.h"
17 #include "parse-util.h"
18 #include "proc-cmdline.h"
19 #include "string-table.h"
20 #include "string-util.h"
21 #include "udev-util.h"
22 #include "util.h"
23 #include "list.h"
24
25 /* Note that any write is delayed until exit and the rfkill state will not be
26 * stored for rfkill indices that disappear after a change. */
27 #define EXIT_USEC (5 * USEC_PER_SEC)
28
29 typedef struct write_queue_item {
30 LIST_FIELDS(struct write_queue_item, queue);
31 int rfkill_idx;
32 char *file;
33 int state;
34 } write_queue_item;
35
36 typedef struct Context {
37 LIST_HEAD(write_queue_item, write_queue);
38 int rfkill_fd;
39 } Context;
40
41 static struct write_queue_item* write_queue_item_free(struct write_queue_item *item) {
42 if (!item)
43 return NULL;
44
45 free(item->file);
46 return mfree(item);
47 }
48
49 static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
50 [RFKILL_TYPE_ALL] = "all",
51 [RFKILL_TYPE_WLAN] = "wlan",
52 [RFKILL_TYPE_BLUETOOTH] = "bluetooth",
53 [RFKILL_TYPE_UWB] = "uwb",
54 [RFKILL_TYPE_WIMAX] = "wimax",
55 [RFKILL_TYPE_WWAN] = "wwan",
56 [RFKILL_TYPE_GPS] = "gps",
57 [RFKILL_TYPE_FM] = "fm",
58 [RFKILL_TYPE_NFC] = "nfc",
59 };
60
61 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
62
63 static int find_device(
64 const struct rfkill_event *event,
65 sd_device **ret) {
66 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
67 _cleanup_free_ char *sysname = NULL;
68 const char *name;
69 int r;
70
71 assert(event);
72 assert(ret);
73
74 if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
75 return log_oom();
76
77 r = sd_device_new_from_subsystem_sysname(&device, "rfkill", sysname);
78 if (r < 0)
79 return log_full_errno(IN_SET(r, -ENOENT, -ENXIO, -ENODEV) ? LOG_DEBUG : LOG_ERR, r,
80 "Failed to open device '%s': %m", sysname);
81
82 r = sd_device_get_sysattr_value(device, "name", &name);
83 if (r < 0)
84 return log_device_debug_errno(device, r, "Device has no name, ignoring: %m");
85
86 log_device_debug(device, "Operating on rfkill device '%s'.", name);
87
88 *ret = TAKE_PTR(device);
89 return 0;
90 }
91
92 static int determine_state_file(
93 const struct rfkill_event *event,
94 char **ret) {
95
96 _cleanup_(sd_device_unrefp) sd_device *d = NULL, *device = NULL;
97 const char *path_id, *type;
98 char *state_file;
99 int r;
100
101 assert(event);
102 assert(ret);
103
104 r = find_device(event, &d);
105 if (r < 0)
106 return r;
107
108 r = device_wait_for_initialization(d, "rfkill", &device);
109 if (r < 0)
110 return r;
111
112 assert_se(type = rfkill_type_to_string(event->type));
113
114 if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
115 _cleanup_free_ char *escaped_path_id = NULL;
116
117 escaped_path_id = cescape(path_id);
118 if (!escaped_path_id)
119 return log_oom();
120
121 state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type);
122 } else
123 state_file = strjoin("/var/lib/systemd/rfkill/", type);
124
125 if (!state_file)
126 return log_oom();
127
128 *ret = state_file;
129 return 0;
130 }
131
132 static int load_state(Context *c, const struct rfkill_event *event) {
133 _cleanup_free_ char *state_file = NULL, *value = NULL;
134 struct rfkill_event we;
135 ssize_t l;
136 int b, r;
137
138 assert(c);
139 assert(c->rfkill_fd >= 0);
140 assert(event);
141
142 if (shall_restore_state() == 0)
143 return 0;
144
145 r = determine_state_file(event, &state_file);
146 if (r < 0)
147 return r;
148
149 r = read_one_line_file(state_file, &value);
150 if (IN_SET(r, -ENOENT, 0)) {
151 /* No state file or it's truncated? Then save the current state */
152
153 r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
154 if (r < 0)
155 return log_error_errno(r, "Failed to write state file %s: %m", state_file);
156
157 log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
158 return 0;
159 }
160 if (r < 0)
161 return log_error_errno(r, "Failed to read state file %s: %m", state_file);
162
163 b = parse_boolean(value);
164 if (b < 0)
165 return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
166
167 we = (struct rfkill_event) {
168 .op = RFKILL_OP_CHANGE,
169 .idx = event->idx,
170 .soft = b,
171 };
172
173 l = write(c->rfkill_fd, &we, sizeof(we));
174 if (l < 0)
175 return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
176 if (l != sizeof(we))
177 return log_error_errno(SYNTHETIC_ERRNO(EIO),
178 "Couldn't write rfkill event structure, too short.");
179
180 log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
181 return 0;
182 }
183
184 static void save_state_queue_remove(Context *c, int idx, const char *state_file) {
185 struct write_queue_item *item, *tmp;
186
187 assert(c);
188
189 LIST_FOREACH_SAFE(queue, item, tmp, c->write_queue) {
190 if ((state_file && streq(item->file, state_file)) || idx == item->rfkill_idx) {
191 log_debug("Canceled previous save state of '%s' to %s.", one_zero(item->state), item->file);
192 LIST_REMOVE(queue, c->write_queue, item);
193 write_queue_item_free(item);
194 }
195 }
196 }
197
198 static int save_state_queue(Context *c, const struct rfkill_event *event) {
199 _cleanup_free_ char *state_file = NULL;
200 struct write_queue_item *item;
201 int r;
202
203 assert(c);
204 assert(c->rfkill_fd >= 0);
205 assert(event);
206
207 r = determine_state_file(event, &state_file);
208 if (r < 0)
209 return r;
210
211 save_state_queue_remove(c, event->idx, state_file);
212
213 item = new0(struct write_queue_item, 1);
214 if (!item)
215 return -ENOMEM;
216
217 item->file = TAKE_PTR(state_file);
218 item->rfkill_idx = event->idx;
219 item->state = event->soft;
220
221 LIST_APPEND(queue, c->write_queue, item);
222
223 return 0;
224 }
225
226 static int save_state_cancel(Context *c, const struct rfkill_event *event) {
227 _cleanup_free_ char *state_file = NULL;
228 int r;
229
230 assert(c);
231 assert(c->rfkill_fd >= 0);
232 assert(event);
233
234 r = determine_state_file(event, &state_file);
235 save_state_queue_remove(c, event->idx, state_file);
236 if (r < 0)
237 return r;
238
239 return 0;
240 }
241
242 static int save_state_write_one(struct write_queue_item *item) {
243 int r;
244
245 r = write_string_file(item->file, one_zero(item->state), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
246 if (r < 0)
247 return log_error_errno(r, "Failed to write state file %s: %m", item->file);
248
249 log_debug("Saved state '%s' to %s.", one_zero(item->state), item->file);
250 return 0;
251 }
252
253 static void context_save_and_clear(Context *c) {
254 struct write_queue_item *i;
255
256 assert(c);
257
258 while ((i = c->write_queue)) {
259 LIST_REMOVE(queue, c->write_queue, i);
260 (void) save_state_write_one(i);
261 write_queue_item_free(i);
262 }
263
264 safe_close(c->rfkill_fd);
265 }
266
267 static int run(int argc, char *argv[]) {
268 _cleanup_(context_save_and_clear) Context c = { .rfkill_fd = -1 };
269 bool ready = false;
270 int r, n;
271
272 if (argc > 1)
273 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires no arguments.");
274
275 log_setup_service();
276
277 umask(0022);
278
279 r = mkdir_p("/var/lib/systemd/rfkill", 0755);
280 if (r < 0)
281 return log_error_errno(r, "Failed to create rfkill directory: %m");
282
283 n = sd_listen_fds(false);
284 if (n < 0)
285 return log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
286 if (n > 1)
287 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Got too many file descriptors.");
288
289 if (n == 0) {
290 c.rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
291 if (c.rfkill_fd < 0) {
292 if (errno == ENOENT) {
293 log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
294 return 0;
295 }
296
297 return log_error_errno(errno, "Failed to open /dev/rfkill: %m");
298 }
299 } else {
300 c.rfkill_fd = SD_LISTEN_FDS_START;
301
302 r = fd_nonblock(c.rfkill_fd, 1);
303 if (r < 0)
304 return log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
305 }
306
307 for (;;) {
308 struct rfkill_event event;
309 const char *type;
310 ssize_t l;
311
312 l = read(c.rfkill_fd, &event, sizeof(event));
313 if (l < 0) {
314 if (errno == EAGAIN) {
315
316 if (!ready) {
317 /* Notify manager that we are
318 * now finished with
319 * processing whatever was
320 * queued */
321 (void) sd_notify(false, "READY=1");
322 ready = true;
323 }
324
325 /* Hang around for a bit, maybe there's more coming */
326
327 r = fd_wait_for_event(c.rfkill_fd, POLLIN, EXIT_USEC);
328 if (r == -EINTR)
329 continue;
330 if (r < 0)
331 return log_error_errno(r, "Failed to poll() on device: %m");
332 if (r > 0)
333 continue;
334
335 log_debug("All events read and idle, exiting.");
336 break;
337 }
338
339 log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
340 }
341
342 if (l != RFKILL_EVENT_SIZE_V1)
343 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Read event structure of invalid size.");
344
345 type = rfkill_type_to_string(event.type);
346 if (!type) {
347 log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type);
348 continue;
349 }
350
351 switch (event.op) {
352
353 case RFKILL_OP_ADD:
354 log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
355 (void) load_state(&c, &event);
356 break;
357
358 case RFKILL_OP_DEL:
359 log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
360 (void) save_state_cancel(&c, &event);
361 break;
362
363 case RFKILL_OP_CHANGE:
364 log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
365 (void) save_state_queue(&c, &event);
366 break;
367
368 default:
369 log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type);
370 break;
371 }
372 }
373
374 return 0;
375 }
376
377 DEFINE_MAIN_FUNCTION(run);