]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/rfkill/rfkill.c
Merge pull request #6472 from yuwata/journal-gateway-fix
[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, EXIT_USEC);
142 if (r == -EINTR)
143 continue;
144 if (r < 0)
145 return log_error_errno(r, "Failed to watch udev monitor: %m");
146 if (r == 0) {
147 log_error("Timed out wating for udev monitor.");
148 return -ETIMEDOUT;
149 }
150
151 t = udev_monitor_receive_device(monitor);
152 if (!t)
153 continue;
154
155 if (streq_ptr(udev_device_get_sysname(t), sysname)) {
156 *ret = udev_device_ref(t);
157 return 0;
158 }
159 }
160 }
161
162 static int determine_state_file(
163 struct udev *udev,
164 const struct rfkill_event *event,
165 struct udev_device *d,
166 char **ret) {
167
168 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
169 const char *path_id, *type;
170 char *state_file;
171 int r;
172
173 assert(event);
174 assert(d);
175 assert(ret);
176
177 r = wait_for_initialized(udev, d, &device);
178 if (r < 0)
179 return r;
180
181 assert_se(type = rfkill_type_to_string(event->type));
182
183 path_id = udev_device_get_property_value(device, "ID_PATH");
184 if (path_id) {
185 _cleanup_free_ char *escaped_path_id = NULL;
186
187 escaped_path_id = cescape(path_id);
188 if (!escaped_path_id)
189 return log_oom();
190
191 state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type);
192 } else
193 state_file = strjoin("/var/lib/systemd/rfkill/", type);
194
195 if (!state_file)
196 return log_oom();
197
198 *ret = state_file;
199 return 0;
200 }
201
202 static int load_state(
203 int rfkill_fd,
204 struct udev *udev,
205 const struct rfkill_event *event) {
206
207 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
208 _cleanup_free_ char *state_file = NULL, *value = NULL;
209 struct rfkill_event we;
210 ssize_t l;
211 int b, r;
212
213 assert(rfkill_fd >= 0);
214 assert(udev);
215 assert(event);
216
217 if (shall_restore_state() == 0)
218 return 0;
219
220 r = find_device(udev, event, &device);
221 if (r < 0)
222 return r;
223
224 r = determine_state_file(udev, event, device, &state_file);
225 if (r < 0)
226 return r;
227
228 r = read_one_line_file(state_file, &value);
229 if (r == -ENOENT) {
230 /* No state file? Then save the current state */
231
232 r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
233 if (r < 0)
234 return log_error_errno(r, "Failed to write state file %s: %m", state_file);
235
236 log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
237 return 0;
238 }
239 if (r < 0)
240 return log_error_errno(r, "Failed to read state file %s: %m", state_file);
241
242 b = parse_boolean(value);
243 if (b < 0)
244 return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
245
246 we = (struct rfkill_event) {
247 .op = RFKILL_OP_CHANGE,
248 .idx = event->idx,
249 .soft = b,
250 };
251
252 l = write(rfkill_fd, &we, sizeof(we));
253 if (l < 0)
254 return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
255 if (l != sizeof(we)) {
256 log_error("Couldn't write rfkill event structure, too short.");
257 return -EIO;
258 }
259
260 log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
261 return 0;
262 }
263
264 static int save_state(
265 int rfkill_fd,
266 struct udev *udev,
267 const struct rfkill_event *event) {
268
269 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
270 _cleanup_free_ char *state_file = NULL;
271 int r;
272
273 assert(rfkill_fd >= 0);
274 assert(udev);
275 assert(event);
276
277 r = find_device(udev, event, &device);
278 if (r < 0)
279 return r;
280
281 r = determine_state_file(udev, event, device, &state_file);
282 if (r < 0)
283 return r;
284
285 r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
286 if (r < 0)
287 return log_error_errno(r, "Failed to write state file %s: %m", state_file);
288
289 log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
290 return 0;
291 }
292
293 int main(int argc, char *argv[]) {
294 _cleanup_udev_unref_ struct udev *udev = NULL;
295 _cleanup_close_ int rfkill_fd = -1;
296 bool ready = false;
297 int r, n;
298
299 if (argc > 1) {
300 log_error("This program requires no arguments.");
301 return EXIT_FAILURE;
302 }
303
304 log_set_target(LOG_TARGET_AUTO);
305 log_parse_environment();
306 log_open();
307
308 umask(0022);
309
310 udev = udev_new();
311 if (!udev) {
312 r = log_oom();
313 goto finish;
314 }
315
316 r = mkdir_p("/var/lib/systemd/rfkill", 0755);
317 if (r < 0) {
318 log_error_errno(r, "Failed to create rfkill directory: %m");
319 goto finish;
320 }
321
322 n = sd_listen_fds(false);
323 if (n < 0) {
324 r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
325 goto finish;
326 }
327 if (n > 1) {
328 log_error("Got too many file descriptors.");
329 r = -EINVAL;
330 goto finish;
331 }
332
333 if (n == 0) {
334 rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
335 if (rfkill_fd < 0) {
336 if (errno == ENOENT) {
337 log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
338 r = 0;
339 goto finish;
340 }
341
342 r = log_error_errno(errno, "Failed to open /dev/rfkill: %m");
343 goto finish;
344 }
345 } else {
346 rfkill_fd = SD_LISTEN_FDS_START;
347
348 r = fd_nonblock(rfkill_fd, 1);
349 if (r < 0) {
350 log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
351 goto finish;
352 }
353 }
354
355 for (;;) {
356 struct rfkill_event event;
357 const char *type;
358 ssize_t l;
359
360 l = read(rfkill_fd, &event, sizeof(event));
361 if (l < 0) {
362 if (errno == EAGAIN) {
363
364 if (!ready) {
365 /* Notify manager that we are
366 * now finished with
367 * processing whatever was
368 * queued */
369 (void) sd_notify(false, "READY=1");
370 ready = true;
371 }
372
373 /* Hang around for a bit, maybe there's more coming */
374
375 r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC);
376 if (r == -EINTR)
377 continue;
378 if (r < 0) {
379 log_error_errno(r, "Failed to poll() on device: %m");
380 goto finish;
381 }
382 if (r > 0)
383 continue;
384
385 log_debug("All events read and idle, exiting.");
386 break;
387 }
388
389 log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
390 }
391
392 if (l != RFKILL_EVENT_SIZE_V1) {
393 log_error("Read event structure of invalid size.");
394 r = -EIO;
395 goto finish;
396 }
397
398 type = rfkill_type_to_string(event.type);
399 if (!type) {
400 log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type);
401 continue;
402 }
403
404 switch (event.op) {
405
406 case RFKILL_OP_ADD:
407 log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
408 (void) load_state(rfkill_fd, udev, &event);
409 break;
410
411 case RFKILL_OP_DEL:
412 log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
413 break;
414
415 case RFKILL_OP_CHANGE:
416 log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
417 (void) save_state(rfkill_fd, udev, &event);
418 break;
419
420 default:
421 log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type);
422 break;
423 }
424 }
425
426 r = 0;
427
428 finish:
429 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
430 }