summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/shared/bus-util.c74
1 files changed, 50 insertions, 24 deletions
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index a4f2deba31..58a9203a41 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -319,10 +319,10 @@ int bus_test_polkit(
typedef struct AsyncPolkitQuery {
sd_bus_message *request, *reply;
- sd_bus_message_handler_t callback;
- void *userdata;
sd_bus_slot *slot;
+
Hashmap *registry;
+ sd_event_source *defer_event_source;
} AsyncPolkitQuery;
static void async_polkit_query_free(AsyncPolkitQuery *q) {
@@ -338,9 +338,22 @@ static void async_polkit_query_free(AsyncPolkitQuery *q) {
sd_bus_message_unref(q->request);
sd_bus_message_unref(q->reply);
+ sd_event_source_disable_unref(q->defer_event_source);
free(q);
}
+static int async_polkit_defer(sd_event_source *s, void *userdata) {
+ AsyncPolkitQuery *q = userdata;
+
+ assert(s);
+
+ /* This is called as idle event source after we processed the async polkit reply, hopefully after the
+ * method call we re-enqueued has been properly processed. */
+
+ async_polkit_query_free(q);
+ return 0;
+}
+
static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
AsyncPolkitQuery *q = userdata;
@@ -349,19 +362,45 @@ static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_e
assert(reply);
assert(q);
+ assert(q->slot);
q->slot = sd_bus_slot_unref(q->slot);
+
+ assert(!q->reply);
q->reply = sd_bus_message_ref(reply);
+ /* Now, let's dispatch the original message a second time be re-enqueing. This will then traverse the
+ * whole message processing again, and thus re-validating and re-retrieving the "userdata" field
+ * again.
+ *
+ * We install an idle event loop event to clean-up the PolicyKit request data when we are idle again,
+ * i.e. after the second time the message is processed is complete. */
+
+ assert(!q->defer_event_source);
+ r = sd_event_add_defer(sd_bus_get_event(sd_bus_message_get_bus(reply)), &q->defer_event_source, async_polkit_defer, q);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_enabled(q->defer_event_source, SD_EVENT_ONESHOT);
+ if (r < 0)
+ goto fail;
+
r = sd_bus_message_rewind(q->request, true);
- if (r < 0) {
- r = sd_bus_reply_method_errno(q->request, r, NULL);
- goto finish;
- }
+ if (r < 0)
+ goto fail;
- r = q->callback(q->request, q->userdata, &error_buffer);
- r = bus_maybe_reply_error(q->request, r, &error_buffer);
+ r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request);
+ if (r < 0)
+ goto fail;
-finish:
+ return 1;
+
+fail:
+ log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
+ (void) sd_bus_reply_method_errno(q->request, r, NULL);
async_polkit_query_free(q);
return r;
@@ -382,11 +421,9 @@ int bus_verify_polkit_async(
#if ENABLE_POLKIT
_cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
AsyncPolkitQuery *q;
- const char *sender, **k, **v;
- sd_bus_message_handler_t callback;
- void *userdata;
int c;
#endif
+ const char *sender, **k, **v;
int r;
assert(call);
@@ -444,20 +481,11 @@ int bus_verify_polkit_async(
else if (r > 0)
return 1;
-#if ENABLE_POLKIT
- if (sd_bus_get_current_message(call->bus) != call)
- return -EINVAL;
-
- callback = sd_bus_get_current_handler(call->bus);
- if (!callback)
- return -EINVAL;
-
- userdata = sd_bus_get_current_userdata(call->bus);
-
sender = sd_bus_message_get_sender(call);
if (!sender)
return -EBADMSG;
+#if ENABLE_POLKIT
c = sd_bus_message_get_allow_interactive_authorization(call);
if (c < 0)
return c;
@@ -509,8 +537,6 @@ int bus_verify_polkit_async(
return -ENOMEM;
q->request = sd_bus_message_ref(call);
- q->callback = callback;
- q->userdata = userdata;
r = hashmap_put(*registry, call, q);
if (r < 0) {