]> git.ipfire.org Git - thirdparty/systemd.git/blame - man/sd_bus_service_reconnect.c
man: enchance sd_bus_set_watch_bind() example to handle one more failure
[thirdparty/systemd.git] / man / sd_bus_service_reconnect.c
CommitLineData
34bbda18
LB
1/* SPDX-License-Identifier: MIT-0 */
2
3/* Implements a D-Bus service that automatically reconnects when the system bus is restarted.
4 *
5 * Compile with 'cc sd_bus_service_reconnect.c $(pkg-config --libs --cflags libsystemd)'
6 *
7 * To allow the program to take ownership of the name 'org.freedesktop.ReconnectExample',
e07e7017
LB
8 * add the following as /etc/dbus-1/system.d/org.freedesktop.ReconnectExample.conf
9 * and then reload the broker with 'systemctl reload dbus':
34bbda18
LB
10
11<?xml version="1.0"?> <!--*-nxml-*-->
12<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
13 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
14<busconfig>
15 <policy user="root">
16 <allow own="org.freedesktop.ReconnectExample"/>
17 <allow send_destination="org.freedesktop.ReconnectExample"/>
18 <allow receive_sender="org.freedesktop.ReconnectExample"/>
19 </policy>
20
21 <policy context="default">
22 <allow send_destination="org.freedesktop.ReconnectExample"/>
23 <allow receive_sender="org.freedesktop.ReconnectExample"/>
24 </policy>
25</busconfig>
26
27 *
28 * To get the property via busctl:
29 *
30 * $ busctl --user get-property org.freedesktop.ReconnectExample \
31 * /org/freedesktop/ReconnectExample \
32 * org.freedesktop.ReconnectExample \
33 * Example
34 * s "example"
35 */
36
37#include <errno.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <systemd/sd-bus.h>
41
42#define _cleanup_(f) __attribute__((cleanup(f)))
43
44#define check(x) ({ \
45 int _r = (x); \
46 errno = _r < 0 ? -_r : 0; \
47 printf(#x ": %m\n"); \
48 if (_r < 0) \
49 return EXIT_FAILURE; \
50 })
51
52typedef struct object {
53 const char *example;
54 sd_bus **bus;
55 sd_event **event;
56} object;
57
58static int property_get(
59 sd_bus *bus,
60 const char *path,
61 const char *interface,
62 const char *property,
63 sd_bus_message *reply,
64 void *userdata,
65 sd_bus_error *error) {
66
67 object *o = userdata;
68
69 if (strcmp(property, "Example") == 0)
70 return sd_bus_message_append(reply, "s", o->example);
71
72 return sd_bus_error_setf(error,
73 SD_BUS_ERROR_UNKNOWN_PROPERTY,
74 "Unknown property '%s'",
75 property);
76}
77
78/* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html */
79static const sd_bus_vtable vtable[] = {
80 SD_BUS_VTABLE_START(0),
81 SD_BUS_PROPERTY(
82 "Example", "s",
83 property_get,
84 0,
85 SD_BUS_VTABLE_PROPERTY_CONST),
86 SD_BUS_VTABLE_END
87};
88
89static int setup(object *o);
90
91static int on_disconnect(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
92 check(setup((object *)userdata));
93 return 0;
94}
95
e07e7017
LB
96/* Ensure the event loop exits with a clear error if acquiring the well-known service name fails */
97static int request_name_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
98 if (!sd_bus_message_is_method_error(m, NULL))
99 return 1;
100
101 const sd_bus_error *error = sd_bus_message_get_error(m);
102
103 if (sd_bus_error_has_names(error, SD_BUS_ERROR_TIMEOUT, SD_BUS_ERROR_NO_REPLY))
104 return 1; /* The bus is not available, try again later */
105
106 printf("Failed to request name: %s\n", error->message);
107 object *o = userdata;
108 check(sd_event_exit(*o->event, -sd_bus_error_get_errno(error)));
109
110 return 1;
111}
112
34bbda18
LB
113static int setup(object *o) {
114 /* If we are reconnecting, then the bus object needs to be closed, detached from
115 * the event loop and recreated.
116 * https://www.freedesktop.org/software/systemd/man/sd_bus_detach_event.html
117 * https://www.freedesktop.org/software/systemd/man/sd_bus_close_unref.html
118 */
119 if (*o->bus) {
120 check(sd_bus_detach_event(*o->bus));
121 *o->bus = sd_bus_close_unref(*o->bus);
122 }
123
124 /* Set up a new bus object for the system bus, configure it to wait for D-Bus to be available
49d6e3c8 125 * instead of failing if it is not, and start it. All the following operations are asynchronous
34bbda18
LB
126 * and will not block waiting for D-Bus to be available.
127 * https://www.freedesktop.org/software/systemd/man/sd_bus_new.html
128 * https://www.freedesktop.org/software/systemd/man/sd_bus_set_address.html
129 * https://www.freedesktop.org/software/systemd/man/sd_bus_set_bus_client.html
130 * https://www.freedesktop.org/software/systemd/man/sd_bus_negotiate_creds.html
131 * https://www.freedesktop.org/software/systemd/man/sd_bus_set_watch_bind.html
132 * https://www.freedesktop.org/software/systemd/man/sd_bus_set_connected_signal.html
133 * https://www.freedesktop.org/software/systemd/man/sd_bus_start.html
134 */
135 check(sd_bus_new(o->bus));
136 check(sd_bus_set_address(*o->bus, "unix:path=/run/dbus/system_bus_socket"));
137 check(sd_bus_set_bus_client(*o->bus, 1));
138 check(sd_bus_negotiate_creds(*o->bus, 1, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS));
139 check(sd_bus_set_watch_bind(*o->bus, 1));
34bbda18
LB
140 check(sd_bus_start(*o->bus));
141
142 /* Publish an interface on the bus, specifying our well-known object access
143 * path and public interface name.
144 * https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
145 * https://dbus.freedesktop.org/doc/dbus-tutorial.html
146 */
147 check(sd_bus_add_object_vtable(*o->bus,
148 NULL,
149 "/org/freedesktop/ReconnectExample",
150 "org.freedesktop.ReconnectExample",
151 vtable,
152 o));
153 /* By default the service is only assigned an ephemeral name. Also add a well-known
154 * one, so that clients know whom to call. This needs to be asynchronous, as
e07e7017
LB
155 * D-Bus might not be yet available. The callback will check whether the error is
156 * expected or not, in case it fails.
34bbda18
LB
157 * https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html
158 */
159 check(sd_bus_request_name_async(*o->bus,
160 NULL,
161 "org.freedesktop.ReconnectExample",
162 0,
e07e7017
LB
163 request_name_callback,
164 o));
34bbda18
LB
165 /* When D-Bus is disconnected this callback will be invoked, which will
166 * set up the connection again. This needs to be asynchronous, as D-Bus might not
167 * yet be available.
168 * https://www.freedesktop.org/software/systemd/man/sd_bus_match_signal_async.html
169 */
170 check(sd_bus_match_signal_async(*o->bus,
171 NULL,
172 "org.freedesktop.DBus.Local",
173 NULL,
174 "org.freedesktop.DBus.Local",
175 "Disconnected",
176 on_disconnect,
177 NULL,
178 o));
179 /* Attach the bus object to the event loop so that calls and signals are processed.
180 * https://www.freedesktop.org/software/systemd/man/sd_bus_attach_event.html
181 */
182 check(sd_bus_attach_event(*o->bus, *o->event, 0));
183
184 return 0;
185}
186
187int main(int argc, char **argv) {
188 /* The bus should be relinquished before the program terminates. The cleanup
189 * attribute allows us to do it nicely and cleanly whenever we exit the
190 * block.
191 */
192 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
193 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
194 object o = {
195 .example = "example",
196 .bus = &bus,
197 .event = &event,
198 };
199
200 /* Create an event loop data structure, with default parameters.
201 * https://www.freedesktop.org/software/systemd/man/sd_event_default.html
202 */
203 check(sd_event_default(&event));
204
205 /* By default the event loop will terminate when all sources have disappeared, so
206 * we have to keep it 'occupied'. Register signal handling to do so.
207 * https://www.freedesktop.org/software/systemd/man/sd_event_add_signal.html
208 */
209 check(sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL));
210 check(sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL));
211
212 check(setup(&o));
213
214 /* Enter the main loop, it will exit only on sigint/sigterm.
215 * https://www.freedesktop.org/software/systemd/man/sd_event_loop.html
216 */
217 check(sd_event_loop(event));
218
219 /* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html */
220 check(sd_bus_release_name(bus, "org.freedesktop.ReconnectExample"));
221
222 return 0;
223}