]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
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
4f5dd394 28#include "escape.h"
d35c1bb1
LP
29#include "fileio.h"
30#include "mkdir.h"
07630cea 31#include "string-util.h"
3990f247 32#include "udev-util.h"
d35c1bb1 33#include "util.h"
3990f247 34
d35c1bb1 35#define EXIT_USEC (5 * USEC_PER_SEC)
3990f247 36
d35c1bb1
LP
37static 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",
ac9455ef
TA
45 [RFKILL_TYPE_FM] = "fm",
46 [RFKILL_TYPE_NFC] = "nfc",
d35c1bb1 47};
3990f247 48
d35c1bb1 49DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
3990f247 50
d35c1bb1
LP
51static int find_device(
52 struct udev *udev,
53 const struct rfkill_event *event,
54 struct udev_device **ret) {
3990f247 55
d35c1bb1
LP
56 _cleanup_free_ char *sysname = NULL;
57 struct udev_device *device;
58 const char *name;
3990f247 59
d35c1bb1
LP
60 assert(udev);
61 assert(event);
62 assert(ret);
3990f247 63
d35c1bb1
LP
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");
3990f247
LP
70
71 name = udev_device_get_sysattr_value(device, "name");
72 if (!name) {
d35c1bb1
LP
73 log_debug("Device has no name, ignoring.");
74 udev_device_unref(device);
75 return -ENOENT;
4844262f
LP
76 }
77
78 log_debug("Operating on rfkill device '%s'.", name);
79
d35c1bb1
LP
80 *ret = device;
81 return 0;
82}
83
84static 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;
3990f247
LP
101 }
102
d35c1bb1
LP
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 }
3990f247 151 }
d35c1bb1
LP
152}
153
154static 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));
3990f247 174
f6f738db
LP
175 path_id = udev_device_get_property_value(device, "ID_PATH");
176 if (path_id) {
d35c1bb1
LP
177 _cleanup_free_ char *escaped_path_id = NULL;
178
f6f738db 179 escaped_path_id = cescape(path_id);
d35c1bb1
LP
180 if (!escaped_path_id)
181 return log_oom();
f6f738db 182
d35c1bb1 183 state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL);
f6f738db 184 } else
d35c1bb1
LP
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
194static 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;
f6f738db 204
d35c1bb1
LP
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
256static 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
285int 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.");
3990f247
LP
293 return EXIT_FAILURE;
294 }
295
d35c1bb1
LP
296 log_set_target(LOG_TARGET_AUTO);
297 log_parse_environment();
298 log_open();
3990f247 299
d35c1bb1 300 umask(0022);
354806fb 301
d35c1bb1
LP
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;
3990f247 336 }
d35c1bb1
LP
337 } else {
338 rfkill_fd = SD_LISTEN_FDS_START;
3990f247 339
d35c1bb1 340 r = fd_nonblock(rfkill_fd, 1);
3990f247 341 if (r < 0) {
d35c1bb1
LP
342 log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
343 goto finish;
3990f247 344 }
d35c1bb1
LP
345 }
346
347 for (;;) {
348 struct rfkill_event event;
349 const char *type;
350 ssize_t l;
3990f247 351
d35c1bb1
LP
352 l = read(rfkill_fd, &event, sizeof(event));
353 if (l < 0) {
354 if (errno == EAGAIN) {
3990f247 355
d35c1bb1
LP
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");
3990f247
LP
382 }
383
d35c1bb1
LP
384 if (l != RFKILL_EVENT_SIZE_V1) {
385 log_error("Read event structure of invalid size.");
386 r = -EIO;
387 goto finish;
3990f247
LP
388 }
389
d35c1bb1
LP
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 }
3990f247
LP
416 }
417
d35c1bb1
LP
418 r = 0;
419
420finish:
421 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
3990f247 422}