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