• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 15057632786

15 May 2025 09:01PM UTC coverage: 72.267% (+0.02%) from 72.244%
15057632786

push

github

bluca
man: document how to hook stuff into system wakeup

Fixes: #6364

298523 of 413084 relevant lines covered (72.27%)

738132.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.85
/src/rfkill/rfkill.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <linux/rfkill.h>
5
#include <poll.h>
6
#include <sys/stat.h>
7
#include <unistd.h>
8

9
#include "sd-daemon.h"
10
#include "sd-device.h"
11

12
#include "alloc-util.h"
13
#include "device-util.h"
14
#include "errno-util.h"
15
#include "escape.h"
16
#include "fd-util.h"
17
#include "fileio.h"
18
#include "io-util.h"
19
#include "list.h"
20
#include "main-func.h"
21
#include "parse-util.h"
22
#include "reboot-util.h"
23
#include "string-table.h"
24
#include "string-util.h"
25
#include "time-util.h"
26
#include "udev-util.h"
27

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. */
30
#define EXIT_USEC (5 * USEC_PER_SEC)
31

32
typedef 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

39
typedef struct Context {
40
        LIST_HEAD(write_queue_item, write_queue);
41
        int rfkill_fd;
42
} Context;
43

44
static struct write_queue_item* write_queue_item_free(struct write_queue_item *item) {
×
45
        if (!item)
×
46
                return NULL;
47

48
        free(item->file);
×
49
        return mfree(item);
×
50
}
51

52
static 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",
60
        [RFKILL_TYPE_FM] = "fm",
61
        [RFKILL_TYPE_NFC] = "nfc",
62
};
63

64
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
4✔
65

66
static int find_device(
2✔
67
                const struct rfkill_event *event,
68
                sd_device **ret) {
69
        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
2✔
70
        _cleanup_free_ char *sysname = NULL;
2✔
71
        const char *name;
2✔
72
        int r;
2✔
73

74
        assert(event);
2✔
75
        assert(ret);
2✔
76

77
        if (asprintf(&sysname, "rfkill%u", event->idx) < 0)
2✔
78
                return log_oom();
×
79

80
        r = sd_device_new_from_subsystem_sysname(&device, "rfkill", sysname);
2✔
81
        if (r < 0)
2✔
82
                return log_full_errno(ERRNO_IS_DEVICE_ABSENT(r) ? LOG_DEBUG : LOG_ERR, r,
×
83
                                      "Failed to open device '%s': %m", sysname);
84

85
        r = sd_device_get_sysattr_value(device, "name", &name);
2✔
86
        if (r < 0)
2✔
87
                return log_device_debug_errno(device, r, "Device has no name, ignoring: %m");
×
88

89
        log_device_debug(device, "Operating on rfkill device '%s'.", name);
2✔
90

91
        *ret = TAKE_PTR(device);
2✔
92
        return 0;
2✔
93
}
94

95
static int determine_state_file(
2✔
96
                const struct rfkill_event *event,
97
                char **ret) {
98

99
        _cleanup_(sd_device_unrefp) sd_device *d = NULL, *device = NULL;
4✔
100
        const char *path_id, *type;
2✔
101
        char *state_file;
2✔
102
        int r;
2✔
103

104
        assert(event);
2✔
105
        assert(ret);
2✔
106

107
        r = find_device(event, &d);
2✔
108
        if (r < 0)
2✔
109
                return r;
110

111
        r = device_wait_for_initialization(d, "rfkill", USEC_INFINITY, &device);
2✔
112
        if (r < 0)
2✔
113
                return r;
114

115
        assert_se(type = rfkill_type_to_string(event->type));
2✔
116

117
        if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
2✔
118
                _cleanup_free_ char *escaped_path_id = NULL;
×
119

120
                escaped_path_id = cescape(path_id);
×
121
                if (!escaped_path_id)
×
122
                        return log_oom();
×
123

124
                state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type);
×
125
        } else
126
                state_file = strjoin("/var/lib/systemd/rfkill/", type);
2✔
127

128
        if (!state_file)
2✔
129
                return log_oom();
×
130

131
        *ret = state_file;
2✔
132
        return 0;
2✔
133
}
134

135
static int load_state(Context *c, const struct rfkill_event *event) {
2✔
136
        _cleanup_free_ char *state_file = NULL, *value = NULL;
2✔
137
        int b, r;
2✔
138

139
        assert(c);
2✔
140
        assert(c->rfkill_fd >= 0);
2✔
141
        assert(event);
2✔
142

143
        if (!shall_restore_state())
2✔
144
                return 0;
145

146
        r = determine_state_file(event, &state_file);
2✔
147
        if (r < 0)
2✔
148
                return r;
149

150
        r = read_one_line_file(state_file, &value);
2✔
151
        if (IN_SET(r, -ENOENT, 0)) {
2✔
152
                /* No state file or it's truncated? Then save the current state */
153

154
                r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
2✔
155
                if (r < 0)
1✔
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);
2✔
159
                return 0;
1✔
160
        }
161
        if (r < 0)
1✔
162
                return log_error_errno(r, "Failed to read state file %s: %m", state_file);
×
163

164
        b = parse_boolean(value);
1✔
165
        if (b < 0)
1✔
166
                return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
×
167

168
        struct rfkill_event we = {
1✔
169
                .idx = event->idx,
1✔
170
                .op = RFKILL_OP_CHANGE,
171
                .soft = b,
172
        };
173
        assert_cc(offsetof(struct rfkill_event, op) < RFKILL_EVENT_SIZE_V1);
1✔
174
        assert_cc(offsetof(struct rfkill_event, soft) < RFKILL_EVENT_SIZE_V1);
1✔
175

176
        ssize_t l = write(c->rfkill_fd, &we, sizeof we);
1✔
177
        if (l < 0)
1✔
178
                return log_error_errno(errno, "Failed to restore rfkill state for %u: %m", event->idx);
×
179
        if ((size_t)l < RFKILL_EVENT_SIZE_V1) /* l cannot be < 0 here. Cast to fix -Werror=sign-compare */
1✔
180
                return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
181
                                       "Couldn't write rfkill event structure, too short (wrote %zd of %zu bytes).",
182
                                       l, sizeof we);
183
        log_debug("Writing struct rfkill_event successful (%zd of %zu bytes).", l, sizeof we);
1✔
184

185
        log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
2✔
186
        return 0;
187
}
188

189
static void save_state_queue_remove(Context *c, int idx, const char *state_file) {
×
190
        assert(c);
×
191

192
        LIST_FOREACH(queue, item, c->write_queue)
×
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);
×
195
                        LIST_REMOVE(queue, c->write_queue, item);
×
196
                        write_queue_item_free(item);
×
197
                }
198
}
×
199

200
static int save_state_queue(Context *c, const struct rfkill_event *event) {
×
201
        _cleanup_free_ char *state_file = NULL;
×
202
        struct write_queue_item *item;
×
203
        int r;
×
204

205
        assert(c);
×
206
        assert(c->rfkill_fd >= 0);
×
207
        assert(event);
×
208

209
        r = determine_state_file(event, &state_file);
×
210
        if (r < 0)
×
211
                return r;
212

213
        save_state_queue_remove(c, event->idx, state_file);
×
214

215
        item = new0(struct write_queue_item, 1);
×
216
        if (!item)
×
217
                return -ENOMEM;
218

219
        item->file = TAKE_PTR(state_file);
×
220
        item->rfkill_idx = event->idx;
×
221
        item->state = event->soft;
×
222

223
        LIST_APPEND(queue, c->write_queue, item);
×
224

225
        return 0;
226
}
227

228
static int save_state_cancel(Context *c, const struct rfkill_event *event) {
×
229
        _cleanup_free_ char *state_file = NULL;
×
230
        int r;
×
231

232
        assert(c);
×
233
        assert(c->rfkill_fd >= 0);
×
234
        assert(event);
×
235

236
        r = determine_state_file(event, &state_file);
×
237
        save_state_queue_remove(c, event->idx, state_file);
×
238
        if (r < 0)
×
239
                return r;
×
240

241
        return 0;
242
}
243

244
static int save_state_write_one(struct write_queue_item *item) {
×
245
        int r;
×
246

247
        r = write_string_file(item->file, one_zero(item->state), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
×
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

255
static void context_save_and_clear(Context *c) {
1✔
256
        struct write_queue_item *i;
1✔
257

258
        assert(c);
1✔
259

260
        while ((i = LIST_POP(queue, c->write_queue))) {
1✔
261
                (void) save_state_write_one(i);
×
262
                write_queue_item_free(i);
×
263
        }
264

265
        safe_close(c->rfkill_fd);
1✔
266
}
1✔
267

268
static int run(int argc, char *argv[]) {
1✔
269
        _cleanup_(context_save_and_clear) Context c = { .rfkill_fd = -EBADF };
1✔
270
        bool ready = false;
1✔
271
        int r, n;
1✔
272

273
        if (argc > 1)
1✔
274
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires no arguments.");
×
275

276
        log_setup();
1✔
277

278
        umask(0022);
1✔
279

280
        n = sd_listen_fds(false);
1✔
281
        if (n < 0)
1✔
282
                return log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
×
283
        if (n > 1)
1✔
284
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Got too many file descriptors.");
×
285

286
        if (n == 0) {
1✔
287
                c.rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
×
288
                if (c.rfkill_fd < 0) {
×
289
                        if (errno == ENOENT) {
×
290
                                log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
×
291
                                return 0;
×
292
                        }
293

294
                        return log_error_errno(errno, "Failed to open /dev/rfkill: %m");
×
295
                }
296
        } else {
297
                c.rfkill_fd = SD_LISTEN_FDS_START;
1✔
298

299
                r = fd_nonblock(c.rfkill_fd, 1);
1✔
300
                if (r < 0)
1✔
301
                        return log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
×
302
        }
303

304
        for (;;) {
3✔
305
                struct rfkill_event event = {};
3✔
306

307
                ssize_t l = read(c.rfkill_fd, &event, sizeof event);
3✔
308
                if (l < 0) {
3✔
309
                        if (errno != EAGAIN)
1✔
310
                                return log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
×
311

312
                        if (!ready) {
1✔
313
                                /* Notify manager that we are now finished with processing whatever was
314
                                 * queued */
315
                                r = sd_notify(false, "READY=1");
1✔
316
                                if (r < 0)
1✔
317
                                        log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
×
318

319
                                ready = true;
320
                        }
321

322
                        /* Hang around for a bit, maybe there's more coming */
323

324
                        r = fd_wait_for_event(c.rfkill_fd, POLLIN, EXIT_USEC);
1✔
325
                        if (r == -EINTR)
1✔
326
                                continue;
×
327
                        if (r < 0)
1✔
328
                                return log_error_errno(r, "Failed to poll() on device: %m");
×
329
                        if (r > 0)
1✔
330
                                continue;
×
331

332
                        log_debug("All events read and idle, exiting.");
1✔
333
                        break;
1✔
334
                }
335

336
                if ((size_t)l < RFKILL_EVENT_SIZE_V1) /* l cannot be < 0 here. Cast to fix -Werror=sign-compare */
2✔
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 */
339
                log_debug("Reading struct rfkill_event: got %zd bytes.", l);
2✔
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);
2✔
344
                assert_cc(offsetof(struct rfkill_event, type) < RFKILL_EVENT_SIZE_V1);
2✔
345

346
                const char *type = rfkill_type_to_string(event.type);
2✔
347
                if (!type) {
2✔
348
                        log_debug("An rfkill device of unknown type %u discovered, ignoring.", event.type);
×
349
                        continue;
×
350
                }
351

352
                switch (event.op) {
2✔
353

354
                case RFKILL_OP_ADD:
355
                        log_debug("A new rfkill device has been added with index %u and type %s.", event.idx, type);
2✔
356
                        (void) load_state(&c, &event);
2✔
357
                        break;
358

359
                case RFKILL_OP_DEL:
360
                        log_debug("An rfkill device has been removed with index %u and type %s", event.idx, type);
×
361
                        (void) save_state_cancel(&c, &event);
×
362
                        break;
363

364
                case RFKILL_OP_CHANGE:
365
                        log_debug("An rfkill device has changed state with index %u and type %s", event.idx, type);
×
366
                        (void) save_state_queue(&c, &event);
×
367
                        break;
368

369
                default:
370
                        log_debug("Unknown event %u from /dev/rfkill for index %u and type %s, ignoring.", event.op, event.idx, type);
×
371
                }
372
        }
373

374
        return 0;
1✔
375
}
376

377
DEFINE_MAIN_FUNCTION(run);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc