]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/rfkill/rfkill.c
Merge pull request #12734 from keszybz/cpu-set-util-on-i386
[thirdparty/systemd.git] / src / rfkill / rfkill.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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"
7a83c3ae 19#include "main-func.h"
d35c1bb1 20#include "mkdir.h"
6bedfcbb 21#include "parse-util.h"
4e731273 22#include "proc-cmdline.h"
8b43440b 23#include "string-table.h"
07630cea 24#include "string-util.h"
ed435031 25#include "udev-util.h"
d35c1bb1 26#include "util.h"
202cb8c3 27#include "list.h"
3990f247 28
202cb8c3
BB
29/* Note that any write is delayed until exit and the rfkill state will not be
30 * stored for rfkill indices that disappear after a change. */
d35c1bb1 31#define EXIT_USEC (5 * USEC_PER_SEC)
3990f247 32
202cb8c3
BB
33typedef struct write_queue_item {
34 LIST_FIELDS(struct write_queue_item, queue);
35 int rfkill_idx;
36 char *file;
37 int state;
38} write_queue_item;
39
5f5f0afc
YW
40typedef struct Context {
41 LIST_HEAD(write_queue_item, write_queue);
42 int rfkill_fd;
43} Context;
44
3b3d1737
LP
45static struct write_queue_item* write_queue_item_free(struct write_queue_item *item) {
46 if (!item)
47 return NULL;
202cb8c3
BB
48
49 free(item->file);
3b3d1737 50 return mfree(item);
202cb8c3
BB
51}
52
d35c1bb1
LP
53static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
54 [RFKILL_TYPE_ALL] = "all",
55 [RFKILL_TYPE_WLAN] = "wlan",
56 [RFKILL_TYPE_BLUETOOTH] = "bluetooth",
57 [RFKILL_TYPE_UWB] = "uwb",
58 [RFKILL_TYPE_WIMAX] = "wimax",
59 [RFKILL_TYPE_WWAN] = "wwan",
60 [RFKILL_TYPE_GPS] = "gps",
ac9455ef
TA
61 [RFKILL_TYPE_FM] = "fm",
62 [RFKILL_TYPE_NFC] = "nfc",
d35c1bb1 63};
3990f247 64
d35c1bb1 65DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
3990f247 66
d35c1bb1 67static int find_device(
d35c1bb1 68 const struct rfkill_event *event,
21384b81
YW
69 sd_device **ret) {
70 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
d35c1bb1 71 _cleanup_free_ char *sysname = NULL;
d35c1bb1 72 const char *name;
21384b81 73 int r;
3990f247 74
d35c1bb1
LP
75 assert(event);
76 assert(ret);
3990f247 77
d35c1bb1
LP
78 if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
79 return log_oom();
80
21384b81
YW
81 r = sd_device_new_from_subsystem_sysname(&device, "rfkill", sysname);
82 if (r < 0)
83 return log_full_errno(IN_SET(r, -ENOENT, -ENXIO, -ENODEV) ? LOG_DEBUG : LOG_ERR, r,
bc5e002e 84 "Failed to open device '%s': %m", sysname);
3990f247 85
21384b81
YW
86 r = sd_device_get_sysattr_value(device, "name", &name);
87 if (r < 0)
94ad3225 88 return log_device_debug_errno(device, r, "Device has no name, ignoring: %m");
4844262f 89
94ad3225 90 log_device_debug(device, "Operating on rfkill device '%s'.", name);
4844262f 91
21384b81 92 *ret = TAKE_PTR(device);
d35c1bb1
LP
93 return 0;
94}
95
d35c1bb1 96static int determine_state_file(
d35c1bb1 97 const struct rfkill_event *event,
d35c1bb1
LP
98 char **ret) {
99
21384b81 100 _cleanup_(sd_device_unrefp) sd_device *d = NULL, *device = NULL;
d35c1bb1
LP
101 const char *path_id, *type;
102 char *state_file;
103 int r;
104
105 assert(event);
d35c1bb1
LP
106 assert(ret);
107
21384b81 108 r = find_device(event, &d);
8e707663
BB
109 if (r < 0)
110 return r;
111
1b47436e 112 r = device_wait_for_initialization(d, "rfkill", USEC_INFINITY, &device);
d35c1bb1
LP
113 if (r < 0)
114 return r;
115
116 assert_se(type = rfkill_type_to_string(event->type));
3990f247 117
21384b81 118 if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
d35c1bb1
LP
119 _cleanup_free_ char *escaped_path_id = NULL;
120
f6f738db 121 escaped_path_id = cescape(path_id);
d35c1bb1
LP
122 if (!escaped_path_id)
123 return log_oom();
f6f738db 124
605405c6 125 state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type);
f6f738db 126 } else
605405c6 127 state_file = strjoin("/var/lib/systemd/rfkill/", type);
d35c1bb1
LP
128
129 if (!state_file)
130 return log_oom();
131
132 *ret = state_file;
133 return 0;
134}
135
5f5f0afc 136static int load_state(Context *c, const struct rfkill_event *event) {
d35c1bb1
LP
137 _cleanup_free_ char *state_file = NULL, *value = NULL;
138 struct rfkill_event we;
139 ssize_t l;
140 int b, r;
f6f738db 141
5f5f0afc
YW
142 assert(c);
143 assert(c->rfkill_fd >= 0);
d35c1bb1
LP
144 assert(event);
145
934ae16b 146 if (shall_restore_state() == 0)
d35c1bb1
LP
147 return 0;
148
21384b81 149 r = determine_state_file(event, &state_file);
d35c1bb1
LP
150 if (r < 0)
151 return r;
152
153 r = read_one_line_file(state_file, &value);
a2176045
TM
154 if (IN_SET(r, -ENOENT, 0)) {
155 /* No state file or it's truncated? Then save the current state */
d35c1bb1 156
e82e549f 157 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
158 if (r < 0)
159 return log_error_errno(r, "Failed to write state file %s: %m", state_file);
160
161 log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
162 return 0;
163 }
164 if (r < 0)
165 return log_error_errno(r, "Failed to read state file %s: %m", state_file);
166
167 b = parse_boolean(value);
168 if (b < 0)
169 return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
170
171 we = (struct rfkill_event) {
172 .op = RFKILL_OP_CHANGE,
173 .idx = event->idx,
174 .soft = b,
175 };
176
5f5f0afc 177 l = write(c->rfkill_fd, &we, sizeof(we));
d35c1bb1
LP
178 if (l < 0)
179 return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
baaa35ad
ZJS
180 if (l != sizeof(we))
181 return log_error_errno(SYNTHETIC_ERRNO(EIO),
182 "Couldn't write rfkill event structure, too short.");
d35c1bb1
LP
183
184 log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
185 return 0;
186}
187
5f5f0afc 188static void save_state_queue_remove(Context *c, int idx, const char *state_file) {
202cb8c3
BB
189 struct write_queue_item *item, *tmp;
190
5f5f0afc
YW
191 assert(c);
192
193 LIST_FOREACH_SAFE(queue, item, tmp, c->write_queue) {
202cb8c3
BB
194 if ((state_file && streq(item->file, state_file)) || idx == item->rfkill_idx) {
195 log_debug("Canceled previous save state of '%s' to %s.", one_zero(item->state), item->file);
5f5f0afc 196 LIST_REMOVE(queue, c->write_queue, item);
202cb8c3
BB
197 write_queue_item_free(item);
198 }
199 }
200}
201
5f5f0afc 202static int save_state_queue(Context *c, const struct rfkill_event *event) {
d35c1bb1 203 _cleanup_free_ char *state_file = NULL;
202cb8c3 204 struct write_queue_item *item;
d35c1bb1
LP
205 int r;
206
5f5f0afc
YW
207 assert(c);
208 assert(c->rfkill_fd >= 0);
d35c1bb1
LP
209 assert(event);
210
21384b81 211 r = determine_state_file(event, &state_file);
d35c1bb1
LP
212 if (r < 0)
213 return r;
bc5e002e 214
5f5f0afc 215 save_state_queue_remove(c, event->idx, state_file);
d35c1bb1 216
202cb8c3
BB
217 item = new0(struct write_queue_item, 1);
218 if (!item)
219 return -ENOMEM;
220
1cc6c93a 221 item->file = TAKE_PTR(state_file);
202cb8c3
BB
222 item->rfkill_idx = event->idx;
223 item->state = event->soft;
202cb8c3 224
5f5f0afc 225 LIST_APPEND(queue, c->write_queue, item);
202cb8c3
BB
226
227 return 0;
228}
229
5f5f0afc 230static int save_state_cancel(Context *c, const struct rfkill_event *event) {
202cb8c3
BB
231 _cleanup_free_ char *state_file = NULL;
232 int r;
233
5f5f0afc
YW
234 assert(c);
235 assert(c->rfkill_fd >= 0);
202cb8c3
BB
236 assert(event);
237
21384b81 238 r = determine_state_file(event, &state_file);
5f5f0afc 239 save_state_queue_remove(c, event->idx, state_file);
d35c1bb1 240 if (r < 0)
202cb8c3 241 return r;
d35c1bb1 242
d35c1bb1
LP
243 return 0;
244}
245
5f5f0afc 246static int save_state_write_one(struct write_queue_item *item) {
202cb8c3
BB
247 int r;
248
e82e549f 249 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
250 if (r < 0)
251 return log_error_errno(r, "Failed to write state file %s: %m", item->file);
252
253 log_debug("Saved state '%s' to %s.", one_zero(item->state), item->file);
254 return 0;
255}
256
257static void context_save_and_clear(Context *c) {
258 struct write_queue_item *i;
259
260 assert(c);
261
262 while ((i = c->write_queue)) {
263 LIST_REMOVE(queue, c->write_queue, i);
264 (void) save_state_write_one(i);
265 write_queue_item_free(i);
202cb8c3 266 }
5f5f0afc
YW
267
268 safe_close(c->rfkill_fd);
202cb8c3
BB
269}
270
7a83c3ae 271static int run(int argc, char *argv[]) {
5f5f0afc 272 _cleanup_(context_save_and_clear) Context c = { .rfkill_fd = -1 };
d35c1bb1
LP
273 bool ready = false;
274 int r, n;
275
7a83c3ae
YW
276 if (argc > 1)
277 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires no arguments.");
3990f247 278
6bf3c61c 279 log_setup_service();
3990f247 280
d35c1bb1 281 umask(0022);
354806fb 282
d35c1bb1 283 n = sd_listen_fds(false);
7a83c3ae
YW
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.");
d35c1bb1
LP
288
289 if (n == 0) {
5f5f0afc
YW
290 c.rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
291 if (c.rfkill_fd < 0) {
d35c1bb1
LP
292 if (errno == ENOENT) {
293 log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
7a83c3ae 294 return 0;
d35c1bb1
LP
295 }
296
7a83c3ae 297 return log_error_errno(errno, "Failed to open /dev/rfkill: %m");
3990f247 298 }
d35c1bb1 299 } else {
5f5f0afc 300 c.rfkill_fd = SD_LISTEN_FDS_START;
3990f247 301
5f5f0afc 302 r = fd_nonblock(c.rfkill_fd, 1);
7a83c3ae
YW
303 if (r < 0)
304 return log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
d35c1bb1
LP
305 }
306
307 for (;;) {
308 struct rfkill_event event;
309 const char *type;
310 ssize_t l;
3990f247 311
5f5f0afc 312 l = read(c.rfkill_fd, &event, sizeof(event));
d35c1bb1
LP
313 if (l < 0) {
314 if (errno == EAGAIN) {
3990f247 315
d35c1bb1
LP
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
5f5f0afc 327 r = fd_wait_for_event(c.rfkill_fd, POLLIN, EXIT_USEC);
d35c1bb1
LP
328 if (r == -EINTR)
329 continue;
7a83c3ae
YW
330 if (r < 0)
331 return log_error_errno(r, "Failed to poll() on device: %m");
d35c1bb1
LP
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");
3990f247
LP
340 }
341
7a83c3ae
YW
342 if (l != RFKILL_EVENT_SIZE_V1)
343 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Read event structure of invalid size.");
3990f247 344
d35c1bb1
LP
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);
5f5f0afc 355 (void) load_state(&c, &event);
d35c1bb1
LP
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);
5f5f0afc 360 (void) save_state_cancel(&c, &event);
d35c1bb1
LP
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);
5f5f0afc 365 (void) save_state_queue(&c, &event);
d35c1bb1
LP
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 }
3990f247
LP
372 }
373
7a83c3ae 374 return 0;
3990f247 375}
7a83c3ae
YW
376
377DEFINE_MAIN_FUNCTION(run);