]> git.proxmox.com Git - systemd.git/blame - src/rfkill/rfkill.c
New upstream version 240
[systemd.git] / src / rfkill / rfkill.c
CommitLineData
52ad194e 1/* SPDX-License-Identifier: LGPL-2.1+ */
60f067b4 2
6300502b
MP
3#include <linux/rfkill.h>
4#include <poll.h>
5
6300502b 6#include "sd-daemon.h"
6e866b33 7#include "sd-device.h"
6300502b 8
db2df898 9#include "alloc-util.h"
6e866b33 10#include "device-util.h"
db2df898
MP
11#include "escape.h"
12#include "fd-util.h"
6300502b 13#include "fileio.h"
db2df898 14#include "io-util.h"
6e866b33 15#include "main-func.h"
6300502b 16#include "mkdir.h"
db2df898
MP
17#include "parse-util.h"
18#include "proc-cmdline.h"
19#include "string-table.h"
20#include "string-util.h"
60f067b4 21#include "udev-util.h"
6300502b 22#include "util.h"
f5e65279 23#include "list.h"
60f067b4 24
f5e65279
MB
25/* Note that any write is delayed until exit and the rfkill state will not be
26 * stored for rfkill indices that disappear after a change. */
6300502b 27#define EXIT_USEC (5 * USEC_PER_SEC)
60f067b4 28
f5e65279
MB
29typedef struct write_queue_item {
30 LIST_FIELDS(struct write_queue_item, queue);
31 int rfkill_idx;
32 char *file;
33 int state;
34} write_queue_item;
35
6e866b33
MB
36typedef struct Context {
37 LIST_HEAD(write_queue_item, write_queue);
38 int rfkill_fd;
39} Context;
40
b012e921
MB
41static struct write_queue_item* write_queue_item_free(struct write_queue_item *item) {
42 if (!item)
43 return NULL;
f5e65279
MB
44
45 free(item->file);
b012e921 46 return mfree(item);
f5e65279
MB
47}
48
6300502b
MP
49static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
50 [RFKILL_TYPE_ALL] = "all",
51 [RFKILL_TYPE_WLAN] = "wlan",
52 [RFKILL_TYPE_BLUETOOTH] = "bluetooth",
53 [RFKILL_TYPE_UWB] = "uwb",
54 [RFKILL_TYPE_WIMAX] = "wimax",
55 [RFKILL_TYPE_WWAN] = "wwan",
56 [RFKILL_TYPE_GPS] = "gps",
db2df898
MP
57 [RFKILL_TYPE_FM] = "fm",
58 [RFKILL_TYPE_NFC] = "nfc",
6300502b 59};
60f067b4 60
6300502b 61DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
60f067b4 62
6300502b 63static int find_device(
6300502b 64 const struct rfkill_event *event,
6e866b33
MB
65 sd_device **ret) {
66 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
6300502b 67 _cleanup_free_ char *sysname = NULL;
6300502b 68 const char *name;
6e866b33 69 int r;
60f067b4 70
6300502b
MP
71 assert(event);
72 assert(ret);
60f067b4 73
6300502b
MP
74 if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
75 return log_oom();
76
6e866b33 77 r = sd_device_new_from_subsystem_sysname(&device, "rfkill", sysname);
6300502b 78 if (r < 0)
6e866b33 79 return log_full_errno(IN_SET(r, -ENOENT, -ENXIO, -ENODEV) ? LOG_DEBUG : LOG_ERR, r,
b012e921 80 "Failed to open device '%s': %m", sysname);
6300502b 81
6e866b33
MB
82 r = sd_device_get_sysattr_value(device, "name", &name);
83 if (r < 0)
84 return log_device_debug_errno(device, r, "Device has no name, ignoring: %m");
6300502b 85
6e866b33 86 log_device_debug(device, "Operating on rfkill device '%s'.", name);
6300502b 87
6e866b33
MB
88 *ret = TAKE_PTR(device);
89 return 0;
6300502b
MP
90}
91
92static int determine_state_file(
6300502b 93 const struct rfkill_event *event,
6300502b
MP
94 char **ret) {
95
6e866b33 96 _cleanup_(sd_device_unrefp) sd_device *d = NULL, *device = NULL;
6300502b
MP
97 const char *path_id, *type;
98 char *state_file;
99 int r;
100
101 assert(event);
6300502b
MP
102 assert(ret);
103
6e866b33 104 r = find_device(event, &d);
f5e65279
MB
105 if (r < 0)
106 return r;
107
6e866b33 108 r = device_wait_for_initialization(d, "rfkill", &device);
6300502b
MP
109 if (r < 0)
110 return r;
111
112 assert_se(type = rfkill_type_to_string(event->type));
60f067b4 113
6e866b33 114 if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
6300502b
MP
115 _cleanup_free_ char *escaped_path_id = NULL;
116
60f067b4 117 escaped_path_id = cescape(path_id);
6300502b
MP
118 if (!escaped_path_id)
119 return log_oom();
60f067b4 120
2897b343 121 state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type);
60f067b4 122 } else
2897b343 123 state_file = strjoin("/var/lib/systemd/rfkill/", type);
6300502b
MP
124
125 if (!state_file)
126 return log_oom();
127
128 *ret = state_file;
129 return 0;
130}
131
6e866b33 132static int load_state(Context *c, const struct rfkill_event *event) {
6300502b
MP
133 _cleanup_free_ char *state_file = NULL, *value = NULL;
134 struct rfkill_event we;
135 ssize_t l;
136 int b, r;
60f067b4 137
6e866b33
MB
138 assert(c);
139 assert(c->rfkill_fd >= 0);
6300502b
MP
140 assert(event);
141
db2df898 142 if (shall_restore_state() == 0)
6300502b
MP
143 return 0;
144
6e866b33 145 r = determine_state_file(event, &state_file);
6300502b
MP
146 if (r < 0)
147 return r;
148
149 r = read_one_line_file(state_file, &value);
150 if (r == -ENOENT) {
151 /* No state file? Then save the current state */
152
153 r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
154 if (r < 0)
155 return log_error_errno(r, "Failed to write state file %s: %m", state_file);
156
157 log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
158 return 0;
159 }
160 if (r < 0)
161 return log_error_errno(r, "Failed to read state file %s: %m", state_file);
162
163 b = parse_boolean(value);
164 if (b < 0)
165 return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
166
167 we = (struct rfkill_event) {
168 .op = RFKILL_OP_CHANGE,
169 .idx = event->idx,
170 .soft = b,
171 };
172
6e866b33 173 l = write(c->rfkill_fd, &we, sizeof(we));
6300502b
MP
174 if (l < 0)
175 return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
6e866b33
MB
176 if (l != sizeof(we))
177 return log_error_errno(SYNTHETIC_ERRNO(EIO),
178 "Couldn't write rfkill event structure, too short.");
6300502b
MP
179
180 log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
181 return 0;
182}
183
6e866b33 184static void save_state_queue_remove(Context *c, int idx, const char *state_file) {
f5e65279
MB
185 struct write_queue_item *item, *tmp;
186
6e866b33
MB
187 assert(c);
188
189 LIST_FOREACH_SAFE(queue, item, tmp, c->write_queue) {
f5e65279
MB
190 if ((state_file && streq(item->file, state_file)) || idx == item->rfkill_idx) {
191 log_debug("Canceled previous save state of '%s' to %s.", one_zero(item->state), item->file);
6e866b33 192 LIST_REMOVE(queue, c->write_queue, item);
f5e65279
MB
193 write_queue_item_free(item);
194 }
195 }
196}
197
6e866b33 198static int save_state_queue(Context *c, const struct rfkill_event *event) {
6300502b 199 _cleanup_free_ char *state_file = NULL;
f5e65279 200 struct write_queue_item *item;
6300502b
MP
201 int r;
202
6e866b33
MB
203 assert(c);
204 assert(c->rfkill_fd >= 0);
6300502b
MP
205 assert(event);
206
6e866b33 207 r = determine_state_file(event, &state_file);
6300502b
MP
208 if (r < 0)
209 return r;
b012e921 210
6e866b33 211 save_state_queue_remove(c, event->idx, state_file);
6300502b 212
f5e65279
MB
213 item = new0(struct write_queue_item, 1);
214 if (!item)
215 return -ENOMEM;
216
b012e921 217 item->file = TAKE_PTR(state_file);
f5e65279
MB
218 item->rfkill_idx = event->idx;
219 item->state = event->soft;
f5e65279 220
6e866b33 221 LIST_APPEND(queue, c->write_queue, item);
6300502b 222
f5e65279
MB
223 return 0;
224}
225
6e866b33 226static int save_state_cancel(Context *c, const struct rfkill_event *event) {
f5e65279
MB
227 _cleanup_free_ char *state_file = NULL;
228 int r;
229
6e866b33
MB
230 assert(c);
231 assert(c->rfkill_fd >= 0);
f5e65279
MB
232 assert(event);
233
6e866b33
MB
234 r = determine_state_file(event, &state_file);
235 save_state_queue_remove(c, event->idx, state_file);
6300502b 236 if (r < 0)
f5e65279 237 return r;
6300502b 238
6300502b
MP
239 return 0;
240}
241
6e866b33 242static int save_state_write_one(struct write_queue_item *item) {
f5e65279
MB
243 int r;
244
6e866b33
MB
245 r = write_string_file(item->file, one_zero(item->state), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
246 if (r < 0)
247 return log_error_errno(r, "Failed to write state file %s: %m", item->file);
248
249 log_debug("Saved state '%s' to %s.", one_zero(item->state), item->file);
250 return 0;
251}
252
253static void context_save_and_clear(Context *c) {
254 struct write_queue_item *i;
255
256 assert(c);
257
258 while ((i = c->write_queue)) {
259 LIST_REMOVE(queue, c->write_queue, i);
260 (void) save_state_write_one(i);
261 write_queue_item_free(i);
f5e65279 262 }
6e866b33
MB
263
264 safe_close(c->rfkill_fd);
f5e65279
MB
265}
266
6e866b33
MB
267static int run(int argc, char *argv[]) {
268 _cleanup_(context_save_and_clear) Context c = { .rfkill_fd = -1 };
6300502b
MP
269 bool ready = false;
270 int r, n;
271
6e866b33
MB
272 if (argc > 1)
273 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires no arguments.");
f5e65279 274
6e866b33 275 log_setup_service();
60f067b4 276
6300502b 277 umask(0022);
60f067b4 278
6300502b 279 r = mkdir_p("/var/lib/systemd/rfkill", 0755);
6e866b33
MB
280 if (r < 0)
281 return log_error_errno(r, "Failed to create rfkill directory: %m");
6300502b
MP
282
283 n = sd_listen_fds(false);
6e866b33
MB
284 if (n < 0)
285 return log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
286 if (n > 1)
287 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Got too many file descriptors.");
6300502b
MP
288
289 if (n == 0) {
6e866b33
MB
290 c.rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
291 if (c.rfkill_fd < 0) {
6300502b
MP
292 if (errno == ENOENT) {
293 log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
6e866b33 294 return 0;
6300502b
MP
295 }
296
6e866b33 297 return log_error_errno(errno, "Failed to open /dev/rfkill: %m");
60f067b4 298 }
6300502b 299 } else {
6e866b33 300 c.rfkill_fd = SD_LISTEN_FDS_START;
60f067b4 301
6e866b33
MB
302 r = fd_nonblock(c.rfkill_fd, 1);
303 if (r < 0)
304 return log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
6300502b
MP
305 }
306
307 for (;;) {
308 struct rfkill_event event;
309 const char *type;
310 ssize_t l;
60f067b4 311
6e866b33 312 l = read(c.rfkill_fd, &event, sizeof(event));
6300502b
MP
313 if (l < 0) {
314 if (errno == EAGAIN) {
60f067b4 315
6300502b
MP
316 if (!ready) {
317 /* Notify manager that we are
318 * now finished with
319 * processing whatever was
320 * queued */
321 (void) sd_notify(false, "READY=1");
322 ready = true;
323 }
324
325 /* Hang around for a bit, maybe there's more coming */
326
6e866b33 327 r = fd_wait_for_event(c.rfkill_fd, POLLIN, EXIT_USEC);
6300502b
MP
328 if (r == -EINTR)
329 continue;
6e866b33
MB
330 if (r < 0)
331 return log_error_errno(r, "Failed to poll() on device: %m");
6300502b
MP
332 if (r > 0)
333 continue;
334
335 log_debug("All events read and idle, exiting.");
336 break;
337 }
338
339 log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
60f067b4
JS
340 }
341
6e866b33
MB
342 if (l != RFKILL_EVENT_SIZE_V1)
343 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Read event structure of invalid size.");
60f067b4 344
6300502b
MP
345 type = rfkill_type_to_string(event.type);
346 if (!type) {
347 log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type);
348 continue;
349 }
350
351 switch (event.op) {
352
353 case RFKILL_OP_ADD:
354 log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
6e866b33 355 (void) load_state(&c, &event);
6300502b
MP
356 break;
357
358 case RFKILL_OP_DEL:
359 log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
6e866b33 360 (void) save_state_cancel(&c, &event);
6300502b
MP
361 break;
362
363 case RFKILL_OP_CHANGE:
364 log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
6e866b33 365 (void) save_state_queue(&c, &event);
6300502b
MP
366 break;
367
368 default:
369 log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type);
370 break;
371 }
60f067b4
JS
372 }
373
6e866b33 374 return 0;
60f067b4 375}
6e866b33
MB
376
377DEFINE_MAIN_FUNCTION(run);