SD_BUS_CALL(3)

πŸ’… SD_BUS_CALL(3) β€” aka sd_bus_call, sd_bus_call_async

okay so bestie, welcome to sd_bus_call(3), a page straight out of systemd and honestly it's giving core D-Bus communication energy. this is the page where you learn how to actually invoke a D-Bus method call β€” sync or async, your choice, main character energy either way πŸ’…βœ¨


🌸 NAME 🌸

sd_bus_call, sd_bus_call_async β€” literally just "Invoke a D-Bus method call". that's it. that's the whole vibe. πŸŽ€


✨ SYNOPSIS ✨

#include <systemd/sd-bus.h>

typedef int (*sd_bus_message_handler_t)(sd_bus_message *m,
                                        void *userdata,
                                        sd_bus_error *ret_error);

int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec,
                sd_bus_error *ret_error, sd_bus_message **reply);

int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot,
                      sd_bus_message *m,
                      sd_bus_message_handler_t callback, void *userdata,
                      uint64_t usec);

yes, that typedef is a function pointer and yes it's iconic. don't be scared of it πŸ¦‹


πŸ’– DESCRIPTION πŸ’–

so sd_bus_call() takes a complete bus message object and calls the corresponding D-Bus method. on success, the response gets stored in reply. πŸŽ‰ usec is your timeout, measured in microseconds (yes microseconds, not milliseconds, read it again bestie). and if ret_error is not NULL and sd_bus_call() fails β€” whether because of an internal error or because it received a D-Bus error reply β€” then ret_error gets initialized to an instance of sd_bus_error describing the error. no cap. 🫢

sd_bus_call_async() is like sd_bus_call() but it works asynchronously β€” she doesn't wait around, she's got things to do. the callback tells it which function to call when the response arrives. the userdata pointer gets passed to the callback function, and you may choose it freely, it's your little gift to your future self 🎁. if slot is not NULL and sd_bus_call_async() succeeds, slot is set to a slot object which can be used to cancel the method call at a later time using sd_bus_slot_unref(3). if slot is NULL, the lifetime of the method call is bound to the lifetime of the bus object itself, and it cannot be cancelled independently β€” so like, it's stuck there, attached, lowkey clingy. see sd_bus_slot_set_floating(3) for the details on that. πŸͺ„

callback is called when a reply arrives, with the reply, userdata, and an sd_bus_error output parameter as its arguments. and here's the plot twist 🫒 β€” unlike sd_bus_call(), the sd_bus_error output parameter passed to the callback will be empty. so how do you know if the call actually worked? use sd_bus_message_is_method_error(3) on the reply message passed to the callback instead. that's the vibe check. ⭐

now the handler-chaining rules, pay attention:

  • if the callback returns zero and the sd_bus_error output parameter is still empty when the callback finishes, other handlers registered with functions such as sd_bus_add_filter(3) or sd_bus_add_match(3) are given a chance to process the message πŸ™Œ
  • if the callback returns a non-zero value, or the sd_bus_error output parameter is not empty when the callback finishes, no further processing of the message is done. done. periodt. πŸ’€

generally you want to return zero from the callback to give other registered handlers a chance to process the reply as well. sharing is caring fr fr πŸ’œ

πŸ™ˆ lil note that trips everyone up: the sd_bus_error parameter is an output parameter of the callback function, not an input parameter. it can be used to propagate errors from the callback handler β€” it will not receive any error that was received as method reply. iykyk.

the message m passed to the callback is only borrowed β€” meaning the callback should not call sd_bus_message_unref(3) on it, ok? hands off, it's not yours 🚫. if the callback wants to hold on to the message beyond the lifetime of the callback, it needs to call sd_bus_message_ref(3) to create a new reference. that's how you keep it πŸ’…

and if usec is zero, the default D-Bus method call timeout is used. see sd_bus_get_method_call_timeout(3) 🌈


πŸ”₯ RETURN VALUE πŸ”₯

on success, these functions return a non-negative integer. on failure, they return a negative errno-style error code. simple as that, bestie πŸ‘‘

😭 Errors 😭

when sd_bus_call() internally receives a D-Bus error reply, it will set ret_error if it is not NULL, and will return a negative value mapped from the error reply β€” see sd_bus_error_get_errno(3).

returned errors may indicate the following problems:

  • -EINVAL β€” one of these situations, and there are a bunch 🫠:
    • the input parameter m is NULL.
    • the input parameter m is not a D-Bus method call. to create a new D-Bus method call, use sd_bus_message_new_method_call(3).
    • the input parameter m has the BUS_MESSAGE_NO_REPLY_EXPECTED flag set.
    • the input parameter error is non-NULL but was not set to SD_BUS_ERROR_NULL.
  • -ECHILD β€” the bus connection was allocated in a parent process and is being reused in a child process after fork(). awkward 😬
  • -ENOTCONN β€” the input parameter bus is NULL, or the bus is just not connected. she's offline πŸ’€
  • -ECONNRESET β€” the bus connection was closed while waiting for the response. ghosted πŸ₯€
  • -ETIMEDOUT β€” a response was not received within the given timeout. the silence was loud 😭
  • -ELOOP β€” the message m is addressed to its own client. bestie called herself πŸ“ž
  • -ENOMEM β€” memory allocation failed. the storage said no πŸ™ˆ

🧚 NOTES 🧚

the functions described here are available as a shared library, which can be compiled against and linked to with the libsystemd pkg-config(1) file. compile and link, that's the whole ritual ✨

all functions listed here are thread-agnostic and only a single thread may operate on a given object at any given time. different threads may access the same object at different times. multiple independent objects may be used from different threads in parallel. so like, take turns, it's not that hard πŸ’…

the code described here uses getenv(3), which is declared to be not multi-thread-safe. this means that the code calling the functions described here must not call setenv(3) from a parallel thread. it is recommended to only do calls to setenv() from an early phase of the program when no other threads have been started. do the env stuff first, then party πŸŽ€


⭐ HISTORY ⭐

sd_bus_call() and sd_bus_call_async() were added in version 221. vintage but make it fashion πŸ’–


🌈 SEE ALSO 🌈

systemd(1), sd-bus(3), sd_bus_call_method(3), sd_bus_call_method_async(3), sd_bus_message_new_method_call(3), sd_bus_message_append(3), sd_bus_error(3)


and that's the whole page bestie β€” you ate that πŸ’…πŸ‘‘ this cutie was brought to you by systemd 261.3, manual page SD_BUS_CALL(3), and it's signing off with a little sparkle πŸ¦‹βœ¨πŸŽ€

πŸ“¦ where this page comes from