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