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