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