]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/rfkill/rfkill.c
tree-wide: use -EBADF more
[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
ZJS
6#include <sys/stat.h>
7#include <sys/types.h>
8#include <unistd.h>
d35c1bb1 9
d35c1bb1 10#include "sd-daemon.h"
21384b81 11#include "sd-device.h"
d35c1bb1 12
b5efdb8a 13#include "alloc-util.h"
94ad3225 14#include "device-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"
d35c1bb1 21#include "mkdir.h"
6bedfcbb 22#include "parse-util.h"
2bfa8466 23#include "reboot-util.h"
8b43440b 24#include "string-table.h"
07630cea 25#include "string-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
934ae16b 143 if (shall_restore_state() == 0)
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
260 while ((i = c->write_queue)) {
261 LIST_REMOVE(queue, c->write_queue, i);
262 (void) save_state_write_one(i);
263 write_queue_item_free(i);
202cb8c3 264 }
5f5f0afc
YW
265
266 safe_close(c->rfkill_fd);
202cb8c3
BB
267}
268
7a83c3ae 269static int run(int argc, char *argv[]) {
5bb1d7fb 270 _cleanup_(context_save_and_clear) Context c = { .rfkill_fd = -EBADF };
d35c1bb1
LP
271 bool ready = false;
272 int r, n;
273
7a83c3ae
YW
274 if (argc > 1)
275 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires no arguments.");
3990f247 276
d2acb93d 277 log_setup();
3990f247 278
d35c1bb1 279 umask(0022);
354806fb 280
d35c1bb1 281 n = sd_listen_fds(false);
7a83c3ae
YW
282 if (n < 0)
283 return log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
284 if (n > 1)
285 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Got too many file descriptors.");
d35c1bb1
LP
286
287 if (n == 0) {
5f5f0afc
YW
288 c.rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
289 if (c.rfkill_fd < 0) {
d35c1bb1
LP
290 if (errno == ENOENT) {
291 log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
7a83c3ae 292 return 0;
d35c1bb1
LP
293 }
294
7a83c3ae 295 return log_error_errno(errno, "Failed to open /dev/rfkill: %m");
3990f247 296 }
d35c1bb1 297 } else {
5f5f0afc 298 c.rfkill_fd = SD_LISTEN_FDS_START;
3990f247 299
5f5f0afc 300 r = fd_nonblock(c.rfkill_fd, 1);
7a83c3ae
YW
301 if (r < 0)
302 return log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
d35c1bb1
LP
303 }
304
305 for (;;) {
a71c0968 306 struct rfkill_event event = {};
3990f247 307
6c7afdea 308 ssize_t l = read(c.rfkill_fd, &event, sizeof event);
d35c1bb1 309 if (l < 0) {
6c7afdea
ZJS
310 if (errno != EAGAIN)
311 return log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
312
313 if (!ready) {
314 /* Notify manager that we are now finished with processing whatever was
315 * queued */
4bf4f50f
ZJS
316 r = sd_notify(false, "READY=1");
317 if (r < 0)
318 log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
319
6c7afdea 320 ready = true;
d35c1bb1
LP
321 }
322
6c7afdea
ZJS
323 /* Hang around for a bit, maybe there's more coming */
324
325 r = fd_wait_for_event(c.rfkill_fd, POLLIN, EXIT_USEC);
326 if (r == -EINTR)
327 continue;
328 if (r < 0)
329 return log_error_errno(r, "Failed to poll() on device: %m");
330 if (r > 0)
331 continue;
332
333 log_debug("All events read and idle, exiting.");
334 break;
3990f247
LP
335 }
336
ab1aa636
LB
337 if ((size_t)l < RFKILL_EVENT_SIZE_V1) /* l cannot be < 0 here. Cast to fix -Werror=sign-compare */
338 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read of struct rfkill_event: (%zd < %zu)",
339 l, (size_t) RFKILL_EVENT_SIZE_V1); /* Casting necessary to make compiling with different kernel versions happy */
a71c0968
ZJS
340 log_debug("Reading struct rfkill_event: got %zd bytes.", l);
341
342 /* The event structure has more fields. We only care about the first few, so it's OK if we
343 * don't read the full structure. */
344 assert_cc(offsetof(struct rfkill_event, op) < RFKILL_EVENT_SIZE_V1);
345 assert_cc(offsetof(struct rfkill_event, type) < RFKILL_EVENT_SIZE_V1);
3990f247 346
6c7afdea 347 const char *type = rfkill_type_to_string(event.type);
d35c1bb1 348 if (!type) {
c0f86d66 349 log_debug("An rfkill device of unknown type %u discovered, ignoring.", event.type);
d35c1bb1
LP
350 continue;
351 }
352
353 switch (event.op) {
354
355 case RFKILL_OP_ADD:
c0f86d66 356 log_debug("A new rfkill device has been added with index %u and type %s.", event.idx, type);
5f5f0afc 357 (void) load_state(&c, &event);
d35c1bb1
LP
358 break;
359
360 case RFKILL_OP_DEL:
c0f86d66 361 log_debug("An rfkill device has been removed with index %u and type %s", event.idx, type);
5f5f0afc 362 (void) save_state_cancel(&c, &event);
d35c1bb1
LP
363 break;
364
365 case RFKILL_OP_CHANGE:
c0f86d66 366 log_debug("An rfkill device has changed state with index %u and type %s", event.idx, type);
5f5f0afc 367 (void) save_state_queue(&c, &event);
d35c1bb1
LP
368 break;
369
370 default:
c0f86d66 371 log_debug("Unknown event %u from /dev/rfkill for index %u and type %s, ignoring.", event.op, event.idx, type);
d35c1bb1
LP
372 break;
373 }
3990f247
LP
374 }
375
7a83c3ae 376 return 0;
3990f247 377}
7a83c3ae
YW
378
379DEFINE_MAIN_FUNCTION(run);