]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
man: add working example for sd_bus_set_watch_bind()
authorLuca Boccassi <bluca@debian.org>
Wed, 13 Dec 2023 14:01:17 +0000 (14:01 +0000)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 26 Jan 2024 14:41:20 +0000 (14:41 +0000)
This example is able to deal with D-Bus going away and reappearing,
like on soft-reboot, so link it in both manpages.

man/sd_bus_service_reconnect.c [new file with mode: 0644]
man/sd_bus_set_watch_bind.xml
man/systemd-soft-reboot.service.xml

diff --git a/man/sd_bus_service_reconnect.c b/man/sd_bus_service_reconnect.c
new file mode 100644 (file)
index 0000000..0844f65
--- /dev/null
@@ -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;
+}
index 34f2966c67add262a8ab4af6537a35783127d587..6619d3c9cb3b2deafbbaff8631122b254fb94f8a 100644 (file)
 
   <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
index 0a35a77b89af635e1224b5ceed01c7a3adf532a5..138c919ee73a9181a15141e8c602b339bc420e95 100644 (file)
@@ -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