diff options
author | Luca Boccassi <bluca@debian.org> | 2023-12-13 15:01:17 +0100 |
---|---|---|
committer | Luca Boccassi <luca.boccassi@gmail.com> | 2024-01-26 15:41:20 +0100 |
commit | 34bbda18a5f07fa5a52e8d85d20637ce1c00c4ec (patch) | |
tree | 9c965f706a5764cfb29d6d3dd72b45527f0c25c5 | |
parent | core: add SYSTEMD_VERITY_SHARING env var for local development (diff) | |
download | systemd-34bbda18a5f07fa5a52e8d85d20637ce1c00c4ec.tar.xz systemd-34bbda18a5f07fa5a52e8d85d20637ce1c00c4ec.zip |
man: add working example for sd_bus_set_watch_bind()
This example is able to deal with D-Bus going away and reappearing,
like on soft-reboot, so link it in both manpages.
-rw-r--r-- | man/sd_bus_service_reconnect.c | 205 | ||||
-rw-r--r-- | man/sd_bus_set_watch_bind.xml | 15 | ||||
-rw-r--r-- | man/systemd-soft-reboot.service.xml | 9 |
3 files changed, 228 insertions, 1 deletions
diff --git a/man/sd_bus_service_reconnect.c b/man/sd_bus_service_reconnect.c new file mode 100644 index 0000000000..0844f65114 --- /dev/null +++ b/man/sd_bus_service_reconnect.c @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: MIT-0 */ + +/* Implements a D-Bus service that automatically reconnects when the system bus is restarted. + * + * Compile with 'cc sd_bus_service_reconnect.c $(pkg-config --libs --cflags libsystemd)' + * + * To allow the program to take ownership of the name 'org.freedesktop.ReconnectExample', + * add the following as /etc/dbus-1/system.d/org.freedesktop.ReconnectExample.conf: + +<?xml version="1.0"?> <!--*-nxml-*--> +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy user="root"> + <allow own="org.freedesktop.ReconnectExample"/> + <allow send_destination="org.freedesktop.ReconnectExample"/> + <allow receive_sender="org.freedesktop.ReconnectExample"/> + </policy> + + <policy context="default"> + <allow send_destination="org.freedesktop.ReconnectExample"/> + <allow receive_sender="org.freedesktop.ReconnectExample"/> + </policy> +</busconfig> + + * + * To get the property via busctl: + * + * $ busctl --user get-property org.freedesktop.ReconnectExample \ + * /org/freedesktop/ReconnectExample \ + * org.freedesktop.ReconnectExample \ + * Example + * s "example" + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <systemd/sd-bus.h> + +#define _cleanup_(f) __attribute__((cleanup(f))) + +#define check(x) ({ \ + int _r = (x); \ + errno = _r < 0 ? -_r : 0; \ + printf(#x ": %m\n"); \ + if (_r < 0) \ + return EXIT_FAILURE; \ + }) + +typedef struct object { + const char *example; + sd_bus **bus; + sd_event **event; +} object; + +static int property_get( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + object *o = userdata; + + if (strcmp(property, "Example") == 0) + return sd_bus_message_append(reply, "s", o->example); + + return sd_bus_error_setf(error, + SD_BUS_ERROR_UNKNOWN_PROPERTY, + "Unknown property '%s'", + property); +} + +/* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html */ +static const sd_bus_vtable vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY( + "Example", "s", + property_get, + 0, + SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_VTABLE_END +}; + +static int setup(object *o); + +static int on_disconnect(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { + check(setup((object *)userdata)); + return 0; +} + +static int setup(object *o) { + /* If we are reconnecting, then the bus object needs to be closed, detached from + * the event loop and recreated. + * https://www.freedesktop.org/software/systemd/man/sd_bus_detach_event.html + * https://www.freedesktop.org/software/systemd/man/sd_bus_close_unref.html + */ + if (*o->bus) { + check(sd_bus_detach_event(*o->bus)); + *o->bus = sd_bus_close_unref(*o->bus); + } + + /* Set up a new bus object for the system bus, configure it to wait for D-Bus to be available + * instead of failing if it is not, and start it. All the following operations are asyncronous + * and will not block waiting for D-Bus to be available. + * https://www.freedesktop.org/software/systemd/man/sd_bus_new.html + * https://www.freedesktop.org/software/systemd/man/sd_bus_set_address.html + * https://www.freedesktop.org/software/systemd/man/sd_bus_set_bus_client.html + * https://www.freedesktop.org/software/systemd/man/sd_bus_negotiate_creds.html + * https://www.freedesktop.org/software/systemd/man/sd_bus_set_watch_bind.html + * https://www.freedesktop.org/software/systemd/man/sd_bus_set_connected_signal.html + * https://www.freedesktop.org/software/systemd/man/sd_bus_start.html + */ + check(sd_bus_new(o->bus)); + check(sd_bus_set_address(*o->bus, "unix:path=/run/dbus/system_bus_socket")); + check(sd_bus_set_bus_client(*o->bus, 1)); + check(sd_bus_negotiate_creds(*o->bus, 1, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS)); + check(sd_bus_set_watch_bind(*o->bus, 1)); + check(sd_bus_set_connected_signal(*o->bus, 1)); + check(sd_bus_start(*o->bus)); + + /* Publish an interface on the bus, specifying our well-known object access + * path and public interface name. + * https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html + * https://dbus.freedesktop.org/doc/dbus-tutorial.html + */ + check(sd_bus_add_object_vtable(*o->bus, + NULL, + "/org/freedesktop/ReconnectExample", + "org.freedesktop.ReconnectExample", + vtable, + o)); + /* By default the service is only assigned an ephemeral name. Also add a well-known + * one, so that clients know whom to call. This needs to be asynchronous, as + * D-Bus might not be yet available. + * https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html + */ + check(sd_bus_request_name_async(*o->bus, + NULL, + "org.freedesktop.ReconnectExample", + 0, + NULL, + NULL)); + /* When D-Bus is disconnected this callback will be invoked, which will + * set up the connection again. This needs to be asynchronous, as D-Bus might not + * yet be available. + * https://www.freedesktop.org/software/systemd/man/sd_bus_match_signal_async.html + */ + check(sd_bus_match_signal_async(*o->bus, + NULL, + "org.freedesktop.DBus.Local", + NULL, + "org.freedesktop.DBus.Local", + "Disconnected", + on_disconnect, + NULL, + o)); + /* Attach the bus object to the event loop so that calls and signals are processed. + * https://www.freedesktop.org/software/systemd/man/sd_bus_attach_event.html + */ + check(sd_bus_attach_event(*o->bus, *o->event, 0)); + + return 0; +} + +int main(int argc, char **argv) { + /* The bus should be relinquished before the program terminates. The cleanup + * attribute allows us to do it nicely and cleanly whenever we exit the + * block. + */ + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + object o = { + .example = "example", + .bus = &bus, + .event = &event, + }; + + /* Create an event loop data structure, with default parameters. + * https://www.freedesktop.org/software/systemd/man/sd_event_default.html + */ + check(sd_event_default(&event)); + + /* By default the event loop will terminate when all sources have disappeared, so + * we have to keep it 'occupied'. Register signal handling to do so. + * https://www.freedesktop.org/software/systemd/man/sd_event_add_signal.html + */ + check(sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL)); + check(sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL)); + + check(setup(&o)); + + /* Enter the main loop, it will exit only on sigint/sigterm. + * https://www.freedesktop.org/software/systemd/man/sd_event_loop.html + */ + check(sd_event_loop(event)); + + /* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html */ + check(sd_bus_release_name(bus, "org.freedesktop.ReconnectExample")); + + return 0; +} diff --git a/man/sd_bus_set_watch_bind.xml b/man/sd_bus_set_watch_bind.xml index 34f2966c67..6619d3c9cb 100644 --- a/man/sd_bus_set_watch_bind.xml +++ b/man/sd_bus_set_watch_bind.xml @@ -101,6 +101,21 @@ <xi:include href="libsystemd-pkgconfig.xml" /> <refsect1> + <title>Example</title> + + <example> + <title>Create a simple system service that publishes a property on the system bus and can reconnect + when D-Bus disconnects and reconnects</title> + + <programlisting><xi:include href="sd_bus_service_reconnect.c" parse="text"/></programlisting> + + <para>This is particularly useful for services that are configured to survive a soft-reboot, see + <citerefentry><refentrytitle>systemd-soft-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> + for more details.</para> + </example> + </refsect1> + + <refsect1> <title>History</title> <para><function>sd_bus_set_watch_bind()</function> and <function>sd_bus_get_watch_bind()</function> were added in version 237.</para> diff --git a/man/systemd-soft-reboot.service.xml b/man/systemd-soft-reboot.service.xml index 0a35a77b89..138c919ee7 100644 --- a/man/systemd-soft-reboot.service.xml +++ b/man/systemd-soft-reboot.service.xml @@ -3,7 +3,8 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> <!-- SPDX-License-Identifier: LGPL-2.1-or-later --> -<refentry id="systemd-soft-reboot.service"> +<refentry id="systemd-soft-reboot.service" + xmlns:xi="http://www.w3.org/2001/XInclude"> <refentryinfo> <title>systemd-soft-reboot.service</title> @@ -136,6 +137,12 @@ ExecStart=sleep infinity attached, if configured to remain until the very end of the shutdown process. (Also achieved via <varname>DefaultDependencies=no</varname>, and by avoiding <varname>Conflicts=umount.target</varname>)</para></listitem> + + <listitem><para>If the unit publishes a service over D-Bus, the connection needs to be re-established + after soft-reboot as the D-Bus broker will be stopped and then started again. When using the sd-bus + library this can be achieved by adapting the following example. + <programlisting><xi:include href="sd_bus_service_reconnect.c" parse="text"/></programlisting> + </para></listitem> </itemizedlist> <para>Even though passing resources from one soft reboot cycle to the next is possible this way, we |