1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-03 05:18:09 +03:00

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.
This commit is contained in:
Luca Boccassi 2023-12-13 14:01:17 +00:00 committed by Luca Boccassi
parent 343e35b36d
commit 34bbda18a5
3 changed files with 228 additions and 1 deletions

View File

@ -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;
}

View File

@ -100,6 +100,21 @@
<xi:include href="libsystemd-pkgconfig.xml" /> <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> <refsect1>
<title>History</title> <title>History</title>
<para><function>sd_bus_set_watch_bind()</function> and <para><function>sd_bus_set_watch_bind()</function> and

View File

@ -3,7 +3,8 @@
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later --> <!-- 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> <refentryinfo>
<title>systemd-soft-reboot.service</title> <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 attached, if configured to remain until the very end of the shutdown process. (Also achieved via
<varname>DefaultDependencies=no</varname>, and by avoiding <varname>DefaultDependencies=no</varname>, and by avoiding
<varname>Conflicts=umount.target</varname>)</para></listitem> <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> </itemizedlist>
<para>Even though passing resources from one soft reboot cycle to the next is possible this way, we <para>Even though passing resources from one soft reboot cycle to the next is possible this way, we