diff options
Diffstat (limited to 'src/rfkill')
-rw-r--r-- | src/rfkill/rfkill.c | 104 |
1 files changed, 99 insertions, 5 deletions
diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c index 0c617e38c9..3aa468f40b 100644 --- a/src/rfkill/rfkill.c +++ b/src/rfkill/rfkill.c @@ -35,9 +35,27 @@ #include "string-util.h" #include "udev-util.h" #include "util.h" +#include "list.h" +/* Note that any write is delayed until exit and the rfkill state will not be + * stored for rfkill indices that disappear after a change. */ #define EXIT_USEC (5 * USEC_PER_SEC) +typedef struct write_queue_item { + LIST_FIELDS(struct write_queue_item, queue); + int rfkill_idx; + char *file; + int state; +} write_queue_item; + +static void write_queue_item_free(struct write_queue_item *item) +{ + assert(item); + + free(item->file); + free(item); +} + static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = { [RFKILL_TYPE_ALL] = "all", [RFKILL_TYPE_WLAN] = "wlan", @@ -259,12 +277,30 @@ static int load_state( return 0; } -static int save_state( +static void save_state_queue_remove( + struct write_queue_item **write_queue, + int idx, + char *state_file) { + + struct write_queue_item *item, *tmp; + + LIST_FOREACH_SAFE(queue, item, tmp, *write_queue) { + if ((state_file && streq(item->file, state_file)) || idx == item->rfkill_idx) { + log_debug("Canceled previous save state of '%s' to %s.", one_zero(item->state), item->file); + LIST_REMOVE(queue, *write_queue, item); + write_queue_item_free(item); + } + } +} + +static int save_state_queue( + struct write_queue_item **write_queue, int rfkill_fd, struct udev *udev, const struct rfkill_event *event) { _cleanup_free_ char *state_file = NULL; + struct write_queue_item *item; int r; assert(rfkill_fd >= 0); @@ -274,16 +310,69 @@ static int save_state( r = determine_state_file(udev, event, &state_file); if (r < 0) return r; + save_state_queue_remove(write_queue, event->idx, state_file); - r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + item = new0(struct write_queue_item, 1); + if (!item) + return -ENOMEM; + + item->file = state_file; + item->rfkill_idx = event->idx; + item->state = event->soft; + state_file = NULL; + + LIST_APPEND(queue, *write_queue, item); + + return 0; +} + +static int save_state_cancel( + struct write_queue_item **write_queue, + int rfkill_fd, + struct udev *udev, + const struct rfkill_event *event) { + + _cleanup_free_ char *state_file = NULL; + int r; + + assert(rfkill_fd >= 0); + assert(udev); + assert(event); + + r = determine_state_file(udev, event, &state_file); + save_state_queue_remove(write_queue, event->idx, state_file); if (r < 0) - return log_error_errno(r, "Failed to write state file %s: %m", state_file); + return r; - log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file); return 0; } +static int save_state_write(struct write_queue_item **write_queue) { + struct write_queue_item *item, *tmp; + int result = 0; + bool error_logged = false; + int r; + + LIST_FOREACH_SAFE(queue, item, tmp, *write_queue) { + r = write_string_file(item->file, one_zero(item->state), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + if (r < 0) { + result = r; + if (!error_logged) { + log_error_errno(r, "Failed to write state file %s: %m", item->file); + error_logged = true; + } else + log_warning_errno(r, "Failed to write state file %s: %m", item->file); + } else + log_debug("Saved state '%s' to %s.", one_zero(item->state), item->file); + + LIST_REMOVE(queue, *write_queue, item); + write_queue_item_free(item); + } + return result; +} + int main(int argc, char *argv[]) { + LIST_HEAD(write_queue_item, write_queue); _cleanup_udev_unref_ struct udev *udev = NULL; _cleanup_close_ int rfkill_fd = -1; bool ready = false; @@ -294,6 +383,8 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } + LIST_HEAD_INIT(write_queue); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); @@ -403,11 +494,12 @@ int main(int argc, char *argv[]) { case RFKILL_OP_DEL: log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type); + (void) save_state_cancel(&write_queue, rfkill_fd, udev, &event); break; case RFKILL_OP_CHANGE: log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type); - (void) save_state(rfkill_fd, udev, &event); + (void) save_state_queue(&write_queue, rfkill_fd, udev, &event); break; default: @@ -419,5 +511,7 @@ int main(int argc, char *argv[]) { r = 0; finish: + (void) save_state_write(&write_queue); + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } |