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:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *
+ * To get the property via busctl:
+ *
+ * $ busctl --user get-property org.freedesktop.ReconnectExample \
+ * /org/freedesktop/ReconnectExample \
+ * org.freedesktop.ReconnectExample \
+ * Example
+ * s "example"
+ */
+
+#include
+#include
+#include
+#include
+
+#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
@@ -100,6 +100,21 @@
+
+ Example
+
+
+ Create a simple system service that publishes a property on the system bus and can reconnect
+ when D-Bus disconnects and reconnects
+
+
+
+ This is particularly useful for services that are configured to survive a soft-reboot, see
+ systemd-soft-reboot.service8
+ for more details.
+
+
+
History
sd_bus_set_watch_bind() and
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">
-
+
systemd-soft-reboot.service
@@ -136,6 +137,12 @@ ExecStart=sleep infinity
attached, if configured to remain until the very end of the shutdown process. (Also achieved via
DefaultDependencies=no, and by avoiding
Conflicts=umount.target)
+
+ 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.
+
+
Even though passing resources from one soft reboot cycle to the next is possible this way, we