]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/rfkill/rfkill.c
Merge pull request #4510 from keszybz/tree-wide-cleanups
[thirdparty/systemd.git] / src / rfkill / rfkill.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2013 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <linux/rfkill.h>
21 #include <poll.h>
22
23 #include "libudev.h"
24 #include "sd-daemon.h"
25
26 #include "alloc-util.h"
27 #include "escape.h"
28 #include "fd-util.h"
29 #include "fileio.h"
30 #include "io-util.h"
31 #include "mkdir.h"
32 #include "parse-util.h"
33 #include "proc-cmdline.h"
34 #include "string-table.h"
35 #include "string-util.h"
36 #include "udev-util.h"
37 #include "util.h"
38
39 #define EXIT_USEC (5 * USEC_PER_SEC)
40
41 static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
42 [RFKILL_TYPE_ALL] = "all",
43 [RFKILL_TYPE_WLAN] = "wlan",
44 [RFKILL_TYPE_BLUETOOTH] = "bluetooth",
45 [RFKILL_TYPE_UWB] = "uwb",
46 [RFKILL_TYPE_WIMAX] = "wimax",
47 [RFKILL_TYPE_WWAN] = "wwan",
48 [RFKILL_TYPE_GPS] = "gps",
49 [RFKILL_TYPE_FM] = "fm",
50 [RFKILL_TYPE_NFC] = "nfc",
51 };
52
53 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
54
55 static int find_device(
56 struct udev *udev,
57 const struct rfkill_event *event,
58 struct udev_device **ret) {
59
60 _cleanup_free_ char *sysname = NULL;
61 struct udev_device *device;
62 const char *name;
63
64 assert(udev);
65 assert(event);
66 assert(ret);
67
68 if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
69 return log_oom();
70
71 device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
72 if (!device)
73 return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m");
74
75 name = udev_device_get_sysattr_value(device, "name");
76 if (!name) {
77 log_debug("Device has no name, ignoring.");
78 udev_device_unref(device);
79 return -ENOENT;
80 }
81
82 log_debug("Operating on rfkill device '%s'.", name);
83
84 *ret = device;
85 return 0;
86 }
87
88 static int wait_for_initialized(
89 struct udev *udev,
90 struct udev_device *device,
91 struct udev_device **ret) {
92
93 _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL;
94 struct udev_device *d;
95 const char *sysname;
96 int watch_fd, r;
97
98 assert(udev);
99 assert(device);
100 assert(ret);
101
102 if (udev_device_get_is_initialized(device) != 0) {
103 *ret = udev_device_ref(device);
104 return 0;
105 }
106
107 assert_se(sysname = udev_device_get_sysname(device));
108
109 /* Wait until the device is initialized, so that we can get
110 * access to the ID_PATH property */
111
112 monitor = udev_monitor_new_from_netlink(udev, "udev");
113 if (!monitor)
114 return log_error_errno(errno, "Failed to acquire monitor: %m");
115
116 r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL);
117 if (r < 0)
118 return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m");
119
120 r = udev_monitor_enable_receiving(monitor);
121 if (r < 0)
122 return log_error_errno(r, "Failed to enable udev receiving: %m");
123
124 watch_fd = udev_monitor_get_fd(monitor);
125 if (watch_fd < 0)
126 return log_error_errno(watch_fd, "Failed to get watch fd: %m");
127
128 /* Check again, maybe things changed */
129 d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
130 if (!d)
131 return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m");
132
133 if (udev_device_get_is_initialized(d) != 0) {
134 *ret = d;
135 return 0;
136 }
137
138 for (;;) {
139 _cleanup_udev_device_unref_ struct udev_device *t = NULL;
140
141 r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY);
142 if (r == -EINTR)
143 continue;
144 if (r < 0)
145 return log_error_errno(r, "Failed to watch udev monitor: %m");
146
147 t = udev_monitor_receive_device(monitor);
148 if (!t)
149 continue;
150
151 if (streq_ptr(udev_device_get_sysname(device), sysname)) {
152 *ret = udev_device_ref(t);
153 return 0;
154 }
155 }
156 }
157
158 static int determine_state_file(
159 struct udev *udev,
160 const struct rfkill_event *event,
161 struct udev_device *d,
162 char **ret) {
163
164 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
165 const char *path_id, *type;
166 char *state_file;
167 int r;
168
169 assert(event);
170 assert(d);
171 assert(ret);
172
173 r = wait_for_initialized(udev, d, &device);
174 if (r < 0)
175 return r;
176
177 assert_se(type = rfkill_type_to_string(event->type));
178
179 path_id = udev_device_get_property_value(device, "ID_PATH");
180 if (path_id) {
181 _cleanup_free_ char *escaped_path_id = NULL;
182
183 escaped_path_id = cescape(path_id);
184 if (!escaped_path_id)
185 return log_oom();
186
187 state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type);
188 } else
189 state_file = strjoin("/var/lib/systemd/rfkill/", type);
190
191 if (!state_file)
192 return log_oom();
193
194 *ret = state_file;
195 return 0;
196 }
197
198 static int load_state(
199 int rfkill_fd,
200 struct udev *udev,
201 const struct rfkill_event *event) {
202
203 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
204 _cleanup_free_ char *state_file = NULL, *value = NULL;
205 struct rfkill_event we;
206 ssize_t l;
207 int b, r;
208
209 assert(rfkill_fd >= 0);
210 assert(udev);
211 assert(event);
212
213 if (shall_restore_state() == 0)
214 return 0;
215
216 r = find_device(udev, event, &device);
217 if (r < 0)
218 return r;
219
220 r = determine_state_file(udev, event, device, &state_file);
221 if (r < 0)
222 return r;
223
224 r = read_one_line_file(state_file, &value);
225 if (r == -ENOENT) {
226 /* No state file? Then save the current state */
227
228 r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
229 if (r < 0)
230 return log_error_errno(r, "Failed to write state file %s: %m", state_file);
231
232 log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
233 return 0;
234 }
235 if (r < 0)
236 return log_error_errno(r, "Failed to read state file %s: %m", state_file);
237
238 b = parse_boolean(value);
239 if (b < 0)
240 return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
241
242 we = (struct rfkill_event) {
243 .op = RFKILL_OP_CHANGE,
244 .idx = event->idx,
245 .soft = b,
246 };
247
248 l = write(rfkill_fd, &we, sizeof(we));
249 if (l < 0)
250 return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
251 if (l != sizeof(we)) {
252 log_error("Couldn't write rfkill event structure, too short.");
253 return -EIO;
254 }
255
256 log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
257 return 0;
258 }
259
260 static int save_state(
261 int rfkill_fd,
262 struct udev *udev,
263 const struct rfkill_event *event) {
264
265 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
266 _cleanup_free_ char *state_file = NULL;
267 int r;
268
269 assert(rfkill_fd >= 0);
270 assert(udev);
271 assert(event);
272
273 r = find_device(udev, event, &device);
274 if (r < 0)
275 return r;
276
277 r = determine_state_file(udev, event, device, &state_file);
278 if (r < 0)
279 return r;
280
281 r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
282 if (r < 0)
283 return log_error_errno(r, "Failed to write state file %s: %m", state_file);
284
285 log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
286 return 0;
287 }
288
289 int main(int argc, char *argv[]) {
290 _cleanup_udev_unref_ struct udev *udev = NULL;
291 _cleanup_close_ int rfkill_fd = -1;
292 bool ready = false;
293 int r, n;
294
295 if (argc > 1) {
296 log_error("This program requires no arguments.");
297 return EXIT_FAILURE;
298 }
299
300 log_set_target(LOG_TARGET_AUTO);
301 log_parse_environment();
302 log_open();
303
304 umask(0022);
305
306 udev = udev_new();
307 if (!udev) {
308 r = log_oom();
309 goto finish;
310 }
311
312 r = mkdir_p("/var/lib/systemd/rfkill", 0755);
313 if (r < 0) {
314 log_error_errno(r, "Failed to create rfkill directory: %m");
315 goto finish;
316 }
317
318 n = sd_listen_fds(false);
319 if (n < 0) {
320 r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
321 goto finish;
322 }
323 if (n > 1) {
324 log_error("Got too many file descriptors.");
325 r = -EINVAL;
326 goto finish;
327 }
328
329 if (n == 0) {
330 rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
331 if (rfkill_fd < 0) {
332 if (errno == ENOENT) {
333 log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
334 r = 0;
335 goto finish;
336 }
337
338 r = log_error_errno(errno, "Failed to open /dev/rfkill: %m");
339 goto finish;
340 }
341 } else {
342 rfkill_fd = SD_LISTEN_FDS_START;
343
344 r = fd_nonblock(rfkill_fd, 1);
345 if (r < 0) {
346 log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
347 goto finish;
348 }
349 }
350
351 for (;;) {
352 struct rfkill_event event;
353 const char *type;
354 ssize_t l;
355
356 l = read(rfkill_fd, &event, sizeof(event));
357 if (l < 0) {
358 if (errno == EAGAIN) {
359
360 if (!ready) {
361 /* Notify manager that we are
362 * now finished with
363 * processing whatever was
364 * queued */
365 (void) sd_notify(false, "READY=1");
366 ready = true;
367 }
368
369 /* Hang around for a bit, maybe there's more coming */
370
371 r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC);
372 if (r == -EINTR)
373 continue;
374 if (r < 0) {
375 log_error_errno(r, "Failed to poll() on device: %m");
376 goto finish;
377 }
378 if (r > 0)
379 continue;
380
381 log_debug("All events read and idle, exiting.");
382 break;
383 }
384
385 log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
386 }
387
388 if (l != RFKILL_EVENT_SIZE_V1) {
389 log_error("Read event structure of invalid size.");
390 r = -EIO;
391 goto finish;
392 }
393
394 type = rfkill_type_to_string(event.type);
395 if (!type) {
396 log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type);
397 continue;
398 }
399
400 switch (event.op) {
401
402 case RFKILL_OP_ADD:
403 log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
404 (void) load_state(rfkill_fd, udev, &event);
405 break;
406
407 case RFKILL_OP_DEL:
408 log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
409 break;
410
411 case RFKILL_OP_CHANGE:
412 log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
413 (void) save_state(rfkill_fd, udev, &event);
414 break;
415
416 default:
417 log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type);
418 break;
419 }
420 }
421
422 r = 0;
423
424 finish:
425 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
426 }