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