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