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