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