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