]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/rfkill/rfkill.c
util-lib: move string table stuff into its own string-table.[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 "parse-util.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, NULL);
188 } else
189 state_file = strjoin("/var/lib/systemd/rfkill/", type, NULL);
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())
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 }