]> git.ipfire.org Git - thirdparty/systemd.git/blob - man/sd_bus_service_reconnect.c
systemctl: do not fall back to StartUnit automatically for sleep operations
[thirdparty/systemd.git] / man / sd_bus_service_reconnect.c
1 /* SPDX-License-Identifier: MIT-0 */
2
3 /* A D-Bus service that automatically reconnects when the system bus is
4 * restarted.
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',
9 * add the following as /etc/dbus-1/system.d/org.freedesktop.ReconnectExample.conf
10 * and then reload the broker with 'systemctl reload dbus':
11
12 <?xml version="1.0"?> <!--*-nxml-*-->
13 <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
14 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
15 <busconfig>
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>
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
35 * s "example"
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 static int log_error(int r, const char *str) {
46 fprintf(stderr, "%s failed: %s\n", str, strerror(-r));
47 return r;
48 }
49
50 typedef struct object {
51 const char *example;
52 sd_bus **bus;
53 sd_event **event;
54 } object;
55
56 static int property_get(
57 sd_bus *bus,
58 const char *path,
59 const char *interface,
60 const char *property,
61 sd_bus_message *reply,
62 void *userdata,
63 sd_bus_error *error) {
64
65 object *o = userdata;
66
67 if (strcmp(property, "Example") == 0)
68 return sd_bus_message_append(reply, "s", o->example);
69
70 return sd_bus_error_setf(error,
71 SD_BUS_ERROR_UNKNOWN_PROPERTY,
72 "Unknown property '%s'",
73 property);
74 }
75
76 /* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html */
77 static const sd_bus_vtable vtable[] = {
78 SD_BUS_VTABLE_START(0),
79 SD_BUS_PROPERTY(
80 "Example", "s",
81 property_get,
82 0,
83 SD_BUS_VTABLE_PROPERTY_CONST),
84 SD_BUS_VTABLE_END
85 };
86
87 static int setup(object *o);
88
89 static int on_disconnect(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
90 int r;
91
92 r = setup((object *)userdata);
93 if (r < 0) {
94 object *o = userdata;
95 r = sd_event_exit(*o->event, r);
96 if (r < 0)
97 return log_error(r, "sd_event_exit()");
98 }
99
100 return 1;
101 }
102
103 /* Ensure the event loop exits with a clear error if acquiring the well-known
104 * service name fails */
105 static int request_name_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
106 int r;
107
108 if (!sd_bus_message_is_method_error(m, NULL))
109 return 1;
110
111 const sd_bus_error *error = sd_bus_message_get_error(m);
112
113 if (sd_bus_error_has_names(error, SD_BUS_ERROR_TIMEOUT, SD_BUS_ERROR_NO_REPLY))
114 return 1; /* The bus is not available, try again later */
115
116 fprintf(stderr, "Failed to request name: %s\n", error->message);
117 object *o = userdata;
118 r = sd_event_exit(*o->event, -sd_bus_error_get_errno(error));
119 if (r < 0)
120 return log_error(r, "sd_event_exit()");
121
122 return 1;
123 }
124
125 static int setup(object *o) {
126 int r;
127
128 /* If we are reconnecting, then the bus object needs to be closed, detached
129 * from the event loop and recreated.
130 * https://www.freedesktop.org/software/systemd/man/sd_bus_detach_event.html
131 * https://www.freedesktop.org/software/systemd/man/sd_bus_close_unref.html
132 */
133 if (*o->bus) {
134 r = sd_bus_detach_event(*o->bus);
135 if (r < 0)
136 return log_error(r, "sd_bus_detach_event()");
137 *o->bus = sd_bus_close_unref(*o->bus);
138 }
139
140 /* Set up a new bus object for the system bus, configure it to wait for D-Bus
141 * to be available instead of failing if it is not, and start it. All the
142 * following operations are asynchronous and will not block waiting for D-Bus
143 * to be available.
144 * https://www.freedesktop.org/software/systemd/man/sd_bus_new.html
145 * https://www.freedesktop.org/software/systemd/man/sd_bus_set_address.html
146 * https://www.freedesktop.org/software/systemd/man/sd_bus_set_bus_client.html
147 * https://www.freedesktop.org/software/systemd/man/sd_bus_negotiate_creds.html
148 * https://www.freedesktop.org/software/systemd/man/sd_bus_set_watch_bind.html
149 * https://www.freedesktop.org/software/systemd/man/sd_bus_set_connected_signal.html
150 * https://www.freedesktop.org/software/systemd/man/sd_bus_start.html
151 */
152 r = sd_bus_new(o->bus);
153 if (r < 0)
154 return log_error(r, "sd_bus_new()");
155 r = sd_bus_set_address(*o->bus, "unix:path=/run/dbus/system_bus_socket");
156 if (r < 0)
157 return log_error(r, "sd_bus_set_address()");
158 r = sd_bus_set_bus_client(*o->bus, 1);
159 if (r < 0)
160 return log_error(r, "sd_bus_set_bus_client()");
161 r = sd_bus_negotiate_creds(*o->bus, 1, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS);
162 if (r < 0)
163 return log_error(r, "sd_bus_negotiate_creds()");
164 r = sd_bus_set_watch_bind(*o->bus, 1);
165 if (r < 0)
166 return log_error(r, "sd_bus_set_watch_bind()");
167 r = sd_bus_start(*o->bus);
168 if (r < 0)
169 return log_error(r, "sd_bus_start()");
170
171 /* Publish an interface on the bus, specifying our well-known object access
172 * path and public interface name.
173 * https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
174 * https://dbus.freedesktop.org/doc/dbus-tutorial.html
175 */
176 r = sd_bus_add_object_vtable(*o->bus,
177 NULL,
178 "/org/freedesktop/ReconnectExample",
179 "org.freedesktop.ReconnectExample",
180 vtable,
181 o);
182 if (r < 0)
183 return log_error(r, "sd_bus_add_object_vtable()");
184 /* By default the service is only assigned an ephemeral name. Also add a
185 * well-known one, so that clients know whom to call. This needs to be
186 * asynchronous, as D-Bus might not be yet available. The callback will check
187 * whether the error is expected or not, in case it fails.
188 * https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html
189 */
190 r = sd_bus_request_name_async(*o->bus,
191 NULL,
192 "org.freedesktop.ReconnectExample",
193 0,
194 request_name_callback,
195 o);
196 if (r < 0)
197 return log_error(r, "sd_bus_request_name_async()");
198 /* When D-Bus is disconnected this callback will be invoked, which will set up
199 * the connection again. This needs to be asynchronous, as D-Bus might not yet
200 * be available.
201 * https://www.freedesktop.org/software/systemd/man/sd_bus_match_signal_async.html
202 */
203 r = sd_bus_match_signal_async(*o->bus,
204 NULL,
205 "org.freedesktop.DBus.Local",
206 NULL,
207 "org.freedesktop.DBus.Local",
208 "Disconnected",
209 on_disconnect,
210 NULL,
211 o);
212 if (r < 0)
213 return log_error(r, "sd_bus_match_signal_async()");
214 /* Attach the bus object to the event loop so that calls and signals are
215 * processed.
216 * https://www.freedesktop.org/software/systemd/man/sd_bus_attach_event.html
217 */
218 r = sd_bus_attach_event(*o->bus, *o->event, 0);
219 if (r < 0)
220 return log_error(r, "sd_bus_attach_event()");
221
222 return 0;
223 }
224
225 int main(int argc, char **argv) {
226 /* The bus should be relinquished before the program terminates. The cleanup
227 * attribute allows us to do it nicely and cleanly whenever we exit the block.
228 */
229 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
230 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
231 object o = {
232 .example = "example",
233 .bus = &bus,
234 .event = &event,
235 };
236 int r;
237
238 /* Create an event loop data structure, with default parameters.
239 * https://www.freedesktop.org/software/systemd/man/sd_event_default.html
240 */
241 r = sd_event_default(&event);
242 if (r < 0)
243 return log_error(r, "sd_event_default()");
244
245 /* By default the event loop will terminate when all sources have disappeared,
246 * so we have to keep it 'occupied'. Register signal handling to do so.
247 * https://www.freedesktop.org/software/systemd/man/sd_event_add_signal.html
248 */
249 r = sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
250 if (r < 0)
251 return log_error(r, "sd_event_add_signal(SIGINT)");
252
253 r = sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
254 if (r < 0)
255 return log_error(r, "sd_event_add_signal(SIGTERM)");
256
257 r = setup(&o);
258 if (r < 0)
259 return EXIT_FAILURE;
260
261 /* Enter the main loop, it will exit only on sigint/sigterm.
262 * https://www.freedesktop.org/software/systemd/man/sd_event_loop.html
263 */
264 r = sd_event_loop(event);
265 if (r < 0)
266 return log_error(r, "sd_event_loop()");
267
268 /* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html */
269 r = sd_bus_release_name(bus, "org.freedesktop.ReconnectExample");
270 if (r < 0)
271 return log_error(r, "sd_bus_release_name()");
272
273 return 0;
274 }