@@ -324,6 +324,80 @@ static int process_polkit_response(
324324
325325#endif
326326
327+ /* bus_verify_polkit_async() handles verification of D-Bus calls with polkit. Because the polkit API
328+ * is asynchronous, the whole thing is a bit complex and requires some support in the code that uses
329+ * it. It relies on sd-bus's support for interrupting the processing of a message.
330+ *
331+ * Requirements:
332+ *
333+ * * bus_verify_polkit_async() must be called before any changes to internal state.
334+ * * If bus_verify_polkit_async() has made a new polkit query (signaled by return value 0),
335+ * processing of the message should be interrupted. This is done by returning 1--which sd-bus
336+ * handles specially--and is usually accompanied by a comment. (The message will be queued for
337+ * processing again later when a reply from polkit is received.)
338+ * * The code needs to keep a hashmap, here called registry, in which bus_verify_polkit_async()
339+ * stores active queries. This hashmap's lifetime must be larger than the method handler's;
340+ * e.g., it can be a member of some "manager" object or a global variable.
341+ *
342+ * Return value:
343+ *
344+ * * 0 - a new polkit call has been made, which means the processing of the message should be
345+ * interrupted;
346+ * * 1 - the action has been allowed;
347+ * * -EACCES - the action has been denied;
348+ * * < 0 - an unspecified error.
349+ *
350+ * A step-by-step description of how it works:
351+ *
352+ * 1. A D-Bus method handler calls bus_verify_polkit_async(), passing it the D-Bus message being
353+ * processed and the polkit action to verify.
354+ * 2. bus_verify_polkit_async() checks the registry for the message and action combination. Let's
355+ * assume this is the first call, so it finds nothing.
356+ * 3. A new AsyncPolkitQuery object is created and an async. D-Bus call to polkit is made. The
357+ * function then returns 0. The method handler returns 1 to tell sd-bus that the processing of
358+ * the message has been interrupted.
359+ * 4. (Later) A reply from polkit is received and async_polkit_callback() is called.
360+ * 5. async_polkit_callback() reads the reply and stores result into the passed query.
361+ * 6. async_polkit_callback() enqueues the original message again.
362+ * 7. (Later) The same D-Bus method handler is called for the same message. It calls
363+ * bus_verify_polkit_async() again.
364+ * 8. bus_verify_polkit_async() checks the registry for the message and action combination. It finds
365+ * an existing query and returns its result.
366+ * 9. The method handler continues processing of the message.
367+ *
368+ * Memory handling:
369+ *
370+ * async_polkit_callback() registers a deferred call of async_polkit_defer() for the query, which
371+ * causes the query to be removed from the registry and freed. Deferred events are run with idle
372+ * priority, so this will happen after processing of the D-Bus message, when the query is no longer
373+ * needed.
374+ *
375+ * Schematically:
376+ *
377+ * (m - D-Bus message, a - polkit action, q - polkit query)
378+ *
379+ * -> foo_method(m)
380+ * -> bus_verify_polkit_async(m, a)
381+ * -> bus_call_method_async(q)
382+ * <- bus_verify_polkit_async(m, a) = 0
383+ * <- foo_method(m) = 1
384+ * ...
385+ * -> async_polkit_callback(q)
386+ * -> sd_event_add_defer(async_polkit_defer, q)
387+ * -> sd_bus_enqueue_for_read(m)
388+ * <- async_polkit_callback(q)
389+ * ...
390+ * -> foo_method(m)
391+ * -> bus_verify_polkit_async(m, a)
392+ * <- bus_verify_polkit_async(m, a) = 1/-EACCES/error
393+ * ...
394+ * <- foo_method(m)
395+ * ...
396+ * -> async_polkit_defer(q)
397+ * -> async_polkit_query_free(q)
398+ * <- async_polkit_defer(q)
399+ */
400+
327401int bus_verify_polkit_async (
328402 sd_bus_message * call ,
329403 int capability ,
0 commit comments