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