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

systemd / systemd / 14872145375

06 May 2025 09:07PM UTC coverage: 72.232% (+0.02%) from 72.214%
14872145375

push

github

DaanDeMeyer
string-table: annotate _to_string and _from_string with _const_ and _pure_, respectively

Follow-up for c94f6ab1b

297286 of 411572 relevant lines covered (72.23%)

695615.99 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 <sys/types.h>
8
#include <unistd.h>
9

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

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

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

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

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

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

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

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

65
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
4✔
66

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

155
                r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
2✔
156
                if (r < 0)
1✔
157
                        return log_error_errno(r, "Failed to write state file %s: %m", state_file);
×
158

159
                log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
2✔
160
                return 0;
1✔
161
        }
162
        if (r < 0)
1✔
163
                return log_error_errno(r, "Failed to read state file %s: %m", state_file);
×
164

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

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

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

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

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

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

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

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

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

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

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

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

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

226
        return 0;
227
}
228

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

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

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

242
        return 0;
243
}
244

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

248
        r = write_string_file(item->file, one_zero(item->state), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
×
249
        if (r < 0)
×
250
                return log_error_errno(r, "Failed to write state file %s: %m", item->file);
×
251

252
        log_debug("Saved state '%s' to %s.", one_zero(item->state), item->file);
×
253
        return 0;
254
}
255

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

259
        assert(c);
1✔
260

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

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

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

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

277
        log_setup();
1✔
278

279
        umask(0022);
1✔
280

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

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

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

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

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

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

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

320
                                ready = true;
321
                        }
322

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

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

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

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

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

353
                switch (event.op) {
2✔
354

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

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

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

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

375
        return 0;
1✔
376
}
377

378
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