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