Skip to content

Commit 202cb8c

Browse files
author
Benjamin Berg
committed
rfkill: Delay writes until exit (systemd#5768)
On thinkpads there are two rfkill devices for bluetooth. The first is an ACPI switch which powers down the USB dongle and the second one is the USB dongle itself. So when userspace decides to enable rfkill on all devices systemd would randomly save the soft block state of the USB dongle. This later causes issue when re-enabling the devie as systemd-rfkill would put the USB dongle into soft block state right after the ACPI rfkill switch is unblocked by userspace. The simple way to avoid this is to not store rfkill changes for devices that disappear shortly after. That way only the "main" ACPI switch will get stored and systemd-rfkill will not end up blocking the device right after it is being added back again.
1 parent 8e70766 commit 202cb8c

File tree

1 file changed

+99
-5
lines changed

1 file changed

+99
-5
lines changed

src/rfkill/rfkill.c

Lines changed: 99 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,27 @@
3535
#include "string-util.h"
3636
#include "udev-util.h"
3737
#include "util.h"
38+
#include "list.h"
3839

40+
/* Note that any write is delayed until exit and the rfkill state will not be
41+
* stored for rfkill indices that disappear after a change. */
3942
#define EXIT_USEC (5 * USEC_PER_SEC)
4043

44+
typedef struct write_queue_item {
45+
LIST_FIELDS(struct write_queue_item, queue);
46+
int rfkill_idx;
47+
char *file;
48+
int state;
49+
} write_queue_item;
50+
51+
static void write_queue_item_free(struct write_queue_item *item)
52+
{
53+
assert(item);
54+
55+
free(item->file);
56+
free(item);
57+
}
58+
4159
static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
4260
[RFKILL_TYPE_ALL] = "all",
4361
[RFKILL_TYPE_WLAN] = "wlan",
@@ -259,12 +277,30 @@ static int load_state(
259277
return 0;
260278
}
261279

262-
static int save_state(
280+
static void save_state_queue_remove(
281+
struct write_queue_item **write_queue,
282+
int idx,
283+
char *state_file) {
284+
285+
struct write_queue_item *item, *tmp;
286+
287+
LIST_FOREACH_SAFE(queue, item, tmp, *write_queue) {
288+
if ((state_file && streq(item->file, state_file)) || idx == item->rfkill_idx) {
289+
log_debug("Canceled previous save state of '%s' to %s.", one_zero(item->state), item->file);
290+
LIST_REMOVE(queue, *write_queue, item);
291+
write_queue_item_free(item);
292+
}
293+
}
294+
}
295+
296+
static int save_state_queue(
297+
struct write_queue_item **write_queue,
263298
int rfkill_fd,
264299
struct udev *udev,
265300
const struct rfkill_event *event) {
266301

267302
_cleanup_free_ char *state_file = NULL;
303+
struct write_queue_item *item;
268304
int r;
269305

270306
assert(rfkill_fd >= 0);
@@ -274,16 +310,69 @@ static int save_state(
274310
r = determine_state_file(udev, event, &state_file);
275311
if (r < 0)
276312
return r;
313+
save_state_queue_remove(write_queue, event->idx, state_file);
277314

278-
r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
315+
item = new0(struct write_queue_item, 1);
316+
if (!item)
317+
return -ENOMEM;
318+
319+
item->file = state_file;
320+
item->rfkill_idx = event->idx;
321+
item->state = event->soft;
322+
state_file = NULL;
323+
324+
LIST_APPEND(queue, *write_queue, item);
325+
326+
return 0;
327+
}
328+
329+
static int save_state_cancel(
330+
struct write_queue_item **write_queue,
331+
int rfkill_fd,
332+
struct udev *udev,
333+
const struct rfkill_event *event) {
334+
335+
_cleanup_free_ char *state_file = NULL;
336+
int r;
337+
338+
assert(rfkill_fd >= 0);
339+
assert(udev);
340+
assert(event);
341+
342+
r = determine_state_file(udev, event, &state_file);
343+
save_state_queue_remove(write_queue, event->idx, state_file);
279344
if (r < 0)
280-
return log_error_errno(r, "Failed to write state file %s: %m", state_file);
345+
return r;
281346

282-
log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
283347
return 0;
284348
}
285349

350+
static int save_state_write(struct write_queue_item **write_queue) {
351+
struct write_queue_item *item, *tmp;
352+
int result = 0;
353+
bool error_logged = false;
354+
int r;
355+
356+
LIST_FOREACH_SAFE(queue, item, tmp, *write_queue) {
357+
r = write_string_file(item->file, one_zero(item->state), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
358+
if (r < 0) {
359+
result = r;
360+
if (!error_logged) {
361+
log_error_errno(r, "Failed to write state file %s: %m", item->file);
362+
error_logged = true;
363+
} else
364+
log_warning_errno(r, "Failed to write state file %s: %m", item->file);
365+
} else
366+
log_debug("Saved state '%s' to %s.", one_zero(item->state), item->file);
367+
368+
LIST_REMOVE(queue, *write_queue, item);
369+
write_queue_item_free(item);
370+
}
371+
return result;
372+
}
373+
286374
int main(int argc, char *argv[]) {
375+
LIST_HEAD(write_queue_item, write_queue);
287376
_cleanup_udev_unref_ struct udev *udev = NULL;
288377
_cleanup_close_ int rfkill_fd = -1;
289378
bool ready = false;
@@ -294,6 +383,8 @@ int main(int argc, char *argv[]) {
294383
return EXIT_FAILURE;
295384
}
296385

386+
LIST_HEAD_INIT(write_queue);
387+
297388
log_set_target(LOG_TARGET_AUTO);
298389
log_parse_environment();
299390
log_open();
@@ -403,11 +494,12 @@ int main(int argc, char *argv[]) {
403494

404495
case RFKILL_OP_DEL:
405496
log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
497+
(void) save_state_cancel(&write_queue, rfkill_fd, udev, &event);
406498
break;
407499

408500
case RFKILL_OP_CHANGE:
409501
log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
410-
(void) save_state(rfkill_fd, udev, &event);
502+
(void) save_state_queue(&write_queue, rfkill_fd, udev, &event);
411503
break;
412504

413505
default:
@@ -419,5 +511,7 @@ int main(int argc, char *argv[]) {
419511
r = 0;
420512

421513
finish:
514+
(void) save_state_write(&write_queue);
515+
422516
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
423517
}

0 commit comments

Comments
 (0)