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