Branch data Line data Source code
1 : : /*
2 : : Authors:
3 : : Simo Sorce <ssorce@redhat.com>
4 : : Stephen Gallagher <sgallagh@redhat.com>
5 : :
6 : : Copyright (C) 2009 Red Hat
7 : :
8 : : This program is free software; you can redistribute it and/or modify
9 : : it under the terms of the GNU General Public License as published by
10 : : the Free Software Foundation; either version 3 of the License, or
11 : : (at your option) any later version.
12 : :
13 : : This program is distributed in the hope that it will be useful,
14 : : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : : GNU General Public License for more details.
17 : :
18 : : You should have received a copy of the GNU General Public License
19 : : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : : */
21 : :
22 : : #include <sys/time.h>
23 : : #include "util/util.h"
24 : : #include "dbus/dbus.h"
25 : : #include "sbus/sssd_dbus.h"
26 : : #include "sbus/sssd_dbus_private.h"
27 : :
28 : : /* Types */
29 : : struct dbus_ctx_list;
30 : :
31 : : struct sbus_interface_p {
32 : : struct sbus_interface_p *prev, *next;
33 : : struct sbus_connection *conn;
34 : : struct sbus_interface *intf;
35 : : };
36 : :
37 : : static bool path_in_interface_list(struct sbus_interface_p *list,
38 : : const char *path);
39 : : static void sbus_unreg_object_paths(struct sbus_connection *conn);
40 : :
41 : : static int sbus_auto_reconnect(struct sbus_connection *conn);
42 : :
43 : 0 : static void sbus_dispatch(struct tevent_context *ev,
44 : : struct tevent_timer *te,
45 : : struct timeval tv, void *data)
46 : : {
47 : : struct tevent_timer *new_event;
48 : : struct sbus_connection *conn;
49 : : DBusConnection *dbus_conn;
50 : : int ret;
51 : :
52 [ # # ]: 0 : if (data == NULL) return;
53 : :
54 : 0 : conn = talloc_get_type(data, struct sbus_connection);
55 : :
56 : 0 : dbus_conn = conn->dbus.conn;
57 [ # # ][ # # ]: 0 : DEBUG(9, ("dbus conn: %lX\n", dbus_conn));
[ # # ][ # # ]
[ # # ]
58 : :
59 [ # # ]: 0 : if (conn->retries > 0) {
60 [ # # ][ # # ]: 0 : DEBUG(6, ("SBUS is reconnecting. Deferring.\n"));
[ # # ][ # # ]
[ # # ]
61 : : /* Currently trying to reconnect, defer dispatch for 30ms */
62 : 0 : tv = tevent_timeval_current_ofs(0, 30);
63 : 0 : new_event = tevent_add_timer(ev, conn, tv, sbus_dispatch, conn);
64 [ # # ]: 0 : if (new_event == NULL) {
65 [ # # ][ # # ]: 0 : DEBUG(0,("Could not defer dispatch!\n"));
[ # # ][ # # ]
[ # # ]
66 : : }
67 : : return;
68 : : }
69 : :
70 [ # # ][ # # ]: 0 : if ((!dbus_connection_get_is_connected(dbus_conn)) &&
71 : 0 : (conn->max_retries != 0)) {
72 : : /* Attempt to reconnect automatically */
73 : 0 : ret = sbus_auto_reconnect(conn);
74 [ # # ]: 0 : if (ret == EOK) {
75 [ # # ][ # # ]: 0 : DEBUG(1, ("Performing auto-reconnect\n"));
[ # # ][ # # ]
[ # # ]
76 : : return;
77 : : }
78 : :
79 [ # # ][ # # ]: 0 : DEBUG(0, ("Cannot start auto-reconnection.\n"));
[ # # ][ # # ]
[ # # ]
80 : 0 : conn->reconnect_callback(conn,
81 : : SBUS_RECONNECT_ERROR,
82 : : conn->reconnect_pvt);
83 : 0 : return;
84 : : }
85 : :
86 [ # # # # ]: 0 : if ((conn->disconnect) ||
87 : 0 : (!dbus_connection_get_is_connected(dbus_conn))) {
88 [ # # ][ # # ]: 0 : DEBUG(3,("Connection is not open for dispatching.\n"));
[ # # ][ # # ]
[ # # ]
89 : : /*
90 : : * Free the connection object.
91 : : * This will invoke the destructor for the connection
92 : : */
93 : 0 : talloc_free(conn);
94 : 0 : conn = NULL;
95 : 0 : return;
96 : : }
97 : :
98 : : /* Dispatch only once each time through the mainloop to avoid
99 : : * starving other features
100 : : */
101 : 0 : ret = dbus_connection_get_dispatch_status(dbus_conn);
102 [ # # ]: 0 : if (ret != DBUS_DISPATCH_COMPLETE) {
103 [ # # ][ # # ]: 0 : DEBUG(9,("Dispatching.\n"));
[ # # ][ # # ]
[ # # ]
104 : 0 : dbus_connection_dispatch(dbus_conn);
105 : : }
106 : :
107 : : /* If other dispatches are waiting, queue up the dispatch function
108 : : * for the next loop.
109 : : */
110 : 0 : ret = dbus_connection_get_dispatch_status(dbus_conn);
111 [ # # ]: 0 : if (ret != DBUS_DISPATCH_COMPLETE) {
112 : 0 : new_event = tevent_add_timer(ev, conn, tv, sbus_dispatch, conn);
113 [ # # ]: 0 : if (new_event == NULL) {
114 [ # # ][ # # ]: 0 : DEBUG(2,("Could not add dispatch event!\n"));
[ # # ][ # # ]
[ # # ]
115 : :
116 : : /* TODO: Calling exit here is bad */
117 : 0 : exit(1);
118 : : }
119 : : }
120 : : }
121 : :
122 : : /* dbus_connection_wakeup_main
123 : : * D-BUS makes a callback to the wakeup_main function when
124 : : * it has data available for dispatching.
125 : : * In order to avoid blocking, this function will create a now()
126 : : * timed event to perform the dispatch during the next iteration
127 : : * through the mainloop
128 : : */
129 : 0 : static void sbus_conn_wakeup_main(void *data)
130 : : {
131 : : struct sbus_connection *conn;
132 : : struct timeval tv;
133 : : struct tevent_timer *te;
134 : :
135 : 0 : conn = talloc_get_type(data, struct sbus_connection);
136 : :
137 : 0 : tv = tevent_timeval_current();
138 : :
139 : : /* D-BUS calls this function when it is time to do a dispatch */
140 : 0 : te = tevent_add_timer(conn->ev, conn, tv, sbus_dispatch, conn);
141 [ # # ]: 0 : if (te == NULL) {
142 [ # # ][ # # ]: 0 : DEBUG(2,("Could not add dispatch event!\n"));
[ # # ][ # # ]
[ # # ]
143 : : /* TODO: Calling exit here is bad */
144 : 0 : exit(1);
145 : : }
146 : 0 : }
147 : :
148 : : static int sbus_conn_set_fns(struct sbus_connection *conn);
149 : :
150 : : /*
151 : : * integrate_connection_with_event_loop
152 : : * Set up a D-BUS connection to use the libevents mainloop
153 : : * for handling file descriptor and timed events
154 : : */
155 : 0 : int sbus_init_connection(TALLOC_CTX *ctx,
156 : : struct tevent_context *ev,
157 : : DBusConnection *dbus_conn,
158 : : struct sbus_interface *intf,
159 : : int connection_type,
160 : : struct sbus_connection **_conn)
161 : : {
162 : : struct sbus_connection *conn;
163 : : int ret;
164 : :
165 [ # # ][ # # ]: 0 : DEBUG(5,("Adding connection %lX\n", dbus_conn));
[ # # ][ # # ]
[ # # ]
166 : 0 : conn = talloc_zero(ctx, struct sbus_connection);
167 : :
168 : 0 : conn->ev = ev;
169 : 0 : conn->type = SBUS_CONNECTION;
170 : 0 : conn->dbus.conn = dbus_conn;
171 : 0 : conn->connection_type = connection_type;
172 : :
173 : 0 : ret = sbus_conn_add_interface(conn, intf);
174 [ # # ]: 0 : if (ret != EOK) {
175 : 0 : talloc_free(conn);
176 : 0 : return ret;
177 : : }
178 : :
179 : 0 : ret = sbus_conn_set_fns(conn);
180 [ # # ]: 0 : if (ret != EOK) {
181 : 0 : talloc_free(conn);
182 : 0 : return ret;
183 : : }
184 : :
185 : 0 : *_conn = conn;
186 : 0 : return ret;
187 : : }
188 : :
189 : 0 : static int sbus_conn_set_fns(struct sbus_connection *conn)
190 : : {
191 : : dbus_bool_t dbret;
192 : :
193 : : /*
194 : : * Set the default destructor
195 : : * Connections can override this with
196 : : * sbus_conn_set_destructor
197 : : */
198 : 0 : sbus_conn_set_destructor(conn, NULL);
199 : :
200 : : /* Set up DBusWatch functions */
201 : 0 : dbret = dbus_connection_set_watch_functions(conn->dbus.conn,
202 : : sbus_add_watch,
203 : : sbus_remove_watch,
204 : : sbus_toggle_watch,
205 : : conn, NULL);
206 [ # # ]: 0 : if (!dbret) {
207 [ # # ][ # # ]: 0 : DEBUG(2,("Error setting up D-BUS connection watch functions\n"));
[ # # ][ # # ]
[ # # ]
208 : : return EIO;
209 : : }
210 : :
211 : : /* Set up DBusTimeout functions */
212 : 0 : dbret = dbus_connection_set_timeout_functions(conn->dbus.conn,
213 : : sbus_add_timeout,
214 : : sbus_remove_timeout,
215 : : sbus_toggle_timeout,
216 : : conn, NULL);
217 [ # # ]: 0 : if (!dbret) {
218 [ # # ][ # # ]: 0 : DEBUG(2,("Error setting up D-BUS server timeout functions\n"));
[ # # ][ # # ]
[ # # ]
219 : : /* FIXME: free resources ? */
220 : : return EIO;
221 : : }
222 : :
223 : : /* Set up dispatch handler */
224 : 0 : dbus_connection_set_wakeup_main_function(conn->dbus.conn,
225 : : sbus_conn_wakeup_main,
226 : : conn, NULL);
227 : :
228 : : /* Set up any method_contexts passed in */
229 : :
230 : : /* Attempt to dispatch immediately in case of opportunistic
231 : : * services connecting before the handlers were all up.
232 : : * If there are no messages to be dispatched, this will do
233 : : * nothing.
234 : : */
235 : 0 : sbus_conn_wakeup_main(conn);
236 : :
237 : 0 : return EOK;
238 : : }
239 : :
240 : 0 : int sbus_new_connection(TALLOC_CTX *ctx, struct tevent_context *ev,
241 : : const char *address, struct sbus_interface *intf,
242 : : struct sbus_connection **_conn)
243 : : {
244 : : struct sbus_connection *conn;
245 : : DBusConnection *dbus_conn;
246 : : DBusError dbus_error;
247 : : int ret;
248 : :
249 : 0 : dbus_error_init(&dbus_error);
250 : :
251 : : /* Open a shared D-BUS connection to the address */
252 : 0 : dbus_conn = dbus_connection_open(address, &dbus_error);
253 [ # # ]: 0 : if (!dbus_conn) {
254 [ # # ][ # # ]: 0 : DEBUG(1, ("Failed to open connection: name=%s, message=%s\n",
[ # # ][ # # ]
[ # # ]
255 : : dbus_error.name, dbus_error.message));
256 [ # # ]: 0 : if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
257 : : return EIO;
258 : : }
259 : :
260 : 0 : ret = sbus_init_connection(ctx, ev, dbus_conn, intf,
261 : : SBUS_CONN_TYPE_SHARED, &conn);
262 : : if (ret != EOK) {
263 : : /* FIXME: release resources */
264 : : }
265 : :
266 : : /* Store the address for later reconnection */
267 : 0 : conn->address = talloc_strdup(conn, address);
268 : :
269 : 0 : dbus_connection_set_exit_on_disconnect(conn->dbus.conn, FALSE);
270 : :
271 : 0 : *_conn = conn;
272 : : return ret;
273 : : }
274 : :
275 : : /*
276 : : * sbus_conn_set_destructor
277 : : * Configures a callback to clean up this connection when it
278 : : * is finalized.
279 : : * @param conn The sbus_connection created
280 : : * when this connection was established
281 : : * @param destructor The destructor function that should be
282 : : * called when the connection is finalized. If passed NULL,
283 : : * this will reset the connection to the default destructor.
284 : : */
285 : 0 : void sbus_conn_set_destructor(struct sbus_connection *conn,
286 : : sbus_conn_destructor_fn destructor)
287 : : {
288 [ # # ]: 0 : if (!conn) return;
289 : :
290 : 0 : conn->destructor = destructor;
291 : : /* TODO: Should we try to handle the talloc_destructor too? */
292 : : }
293 : :
294 : 0 : int sbus_default_connection_destructor(void *ctx)
295 : : {
296 : : struct sbus_connection *conn;
297 : 0 : conn = talloc_get_type(ctx, struct sbus_connection);
298 : :
299 [ # # ][ # # ]: 0 : DEBUG(5, ("Invoking default destructor on connection %lX\n",
[ # # ][ # # ]
[ # # ]
300 : : conn->dbus.conn));
301 [ # # ]: 0 : if (conn->connection_type == SBUS_CONN_TYPE_PRIVATE) {
302 : : /* Private connections must be closed explicitly */
303 : 0 : dbus_connection_close(conn->dbus.conn);
304 : : }
305 [ # # ]: 0 : else if (conn->connection_type == SBUS_CONN_TYPE_SHARED) {
306 : : /* Shared connections are destroyed when their last reference is removed */
307 : : }
308 : : else {
309 : : /* Critical Error! */
310 [ # # ][ # # ]: 0 : DEBUG(1,("Critical Error, connection_type is neither shared nor private!\n"));
[ # # ][ # # ]
[ # # ]
311 : : return -1;
312 : : }
313 : :
314 : : /* Remove object path */
315 : : /* TODO: Remove object paths */
316 : :
317 : 0 : dbus_connection_unref(conn->dbus.conn);
318 : 0 : return 0;
319 : : }
320 : :
321 : : /*
322 : : * sbus_get_connection
323 : : * Utility function to retreive the DBusConnection object
324 : : * from a sbus_connection
325 : : */
326 : 0 : DBusConnection *sbus_get_connection(struct sbus_connection *conn)
327 : : {
328 : 0 : return conn->dbus.conn;
329 : : }
330 : :
331 : 0 : void sbus_disconnect (struct sbus_connection *conn)
332 : : {
333 [ # # ]: 0 : if (conn == NULL) {
334 : 0 : return;
335 : : }
336 : :
337 [ # # ][ # # ]: 0 : DEBUG(5,("Disconnecting %lX\n", conn->dbus.conn));
[ # # ][ # # ]
[ # # ]
338 : :
339 : : /*******************************
340 : : * Referencing conn->dbus.conn */
341 : 0 : dbus_connection_ref(conn->dbus.conn);
342 : :
343 : 0 : conn->disconnect = 1;
344 : :
345 : : /* Invoke the custom destructor, if it exists */
346 [ # # ]: 0 : if (conn->destructor) {
347 : 0 : conn->destructor(conn);
348 : : }
349 : :
350 : : /* Unregister object paths */
351 : 0 : sbus_unreg_object_paths(conn);
352 : :
353 : : /* Disable watch functions */
354 : 0 : dbus_connection_set_watch_functions(conn->dbus.conn,
355 : : NULL, NULL, NULL,
356 : : NULL, NULL);
357 : : /* Disable timeout functions */
358 : 0 : dbus_connection_set_timeout_functions(conn->dbus.conn,
359 : : NULL, NULL, NULL,
360 : : NULL, NULL);
361 : :
362 : : /* Disable dispatch status function */
363 : 0 : dbus_connection_set_dispatch_status_function(conn->dbus.conn,
364 : : NULL, NULL, NULL);
365 : :
366 : : /* Disable wakeup main function */
367 : 0 : dbus_connection_set_wakeup_main_function(conn->dbus.conn,
368 : : NULL, NULL, NULL);
369 : :
370 : : /* Finalize the connection */
371 : 0 : sbus_default_connection_destructor(conn);
372 : :
373 : 0 : dbus_connection_unref(conn->dbus.conn);
374 : : /* Unreferenced conn->dbus_conn *
375 : : ******************************/
376 : :
377 [ # # ][ # # ]: 0 : DEBUG(5,("Disconnected %lX\n", conn->dbus.conn));
[ # # ][ # # ]
[ # # ]
378 : : }
379 : :
380 : 0 : static int sbus_reply_internal_error(DBusMessage *message,
381 : : struct sbus_connection *conn) {
382 : 0 : DBusMessage *reply = dbus_message_new_error(message, DBUS_ERROR_IO_ERROR,
383 : : "Internal Error");
384 [ # # ]: 0 : if (reply) {
385 : 0 : sbus_conn_send_reply(conn, reply);
386 : 0 : dbus_message_unref(reply);
387 : 0 : return DBUS_HANDLER_RESULT_HANDLED;
388 : : }
389 : : return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
390 : : }
391 : :
392 : : /* messsage_handler
393 : : * Receive messages and process them
394 : : */
395 : 0 : DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn,
396 : : DBusMessage *message,
397 : : void *user_data)
398 : : {
399 : : struct sbus_interface_p *intf_p;
400 : : const char *method;
401 : : const char *path;
402 : : const char *msg_interface;
403 : 0 : DBusMessage *reply = NULL;
404 : : int i, ret;
405 : : int found;
406 : :
407 [ # # ]: 0 : if (!user_data) {
408 : : return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
409 : : }
410 : 0 : intf_p = talloc_get_type(user_data, struct sbus_interface_p);
411 : :
412 : 0 : method = dbus_message_get_member(message);
413 [ # # ][ # # ]: 0 : DEBUG(9, ("Received SBUS method [%s]\n", method));
[ # # ][ # # ]
[ # # ]
414 : 0 : path = dbus_message_get_path(message);
415 : 0 : msg_interface = dbus_message_get_interface(message);
416 : :
417 [ # # ][ # # ]: 0 : if (!method || !path || !msg_interface)
418 : : return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
419 : :
420 : : /* Validate the D-BUS path */
421 [ # # ]: 0 : if (strcmp(path, intf_p->intf->path) != 0)
422 : : return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
423 : :
424 : : /* Validate the method interface */
425 [ # # ]: 0 : if (strcmp(msg_interface, intf_p->intf->interface) == 0) {
426 : : found = 0;
427 [ # # ]: 0 : for (i = 0; intf_p->intf->methods[i].method != NULL; i++) {
428 [ # # ]: 0 : if (strcmp(method, intf_p->intf->methods[i].method) == 0) {
429 : 0 : found = 1;
430 : 0 : ret = intf_p->intf->methods[i].fn(message, intf_p->conn);
431 [ # # ]: 0 : if (ret != EOK) {
432 : 0 : return sbus_reply_internal_error(message, intf_p->conn);
433 : : }
434 : : break;
435 : : }
436 : : }
437 : :
438 [ # # ]: 0 : if (!found) {
439 : : /* Reply DBUS_ERROR_UNKNOWN_METHOD */
440 [ # # ][ # # ]: 0 : DEBUG(1, ("No matching method found for %s.\n", method));
[ # # ][ # # ]
[ # # ]
441 : 0 : reply = dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD, NULL);
442 : 0 : sbus_conn_send_reply(intf_p->conn, reply);
443 : 0 : dbus_message_unref(reply);
444 : : }
445 : : }
446 : : else {
447 : : /* Special case: check for Introspection request
448 : : * This is usually only useful for system bus connections
449 : : */
450 [ # # ][ # # ]: 0 : if (strcmp(msg_interface, DBUS_INTROSPECT_INTERFACE) == 0 &&
451 : 0 : strcmp(method, DBUS_INTROSPECT_METHOD) == 0)
452 : : {
453 [ # # ]: 0 : if (intf_p->intf->introspect_fn) {
454 : : /* If we have been asked for introspection data and we have
455 : : * an introspection function registered, user that.
456 : : */
457 : 0 : ret = intf_p->intf->introspect_fn(message, intf_p->conn);
458 [ # # ]: 0 : if (ret != EOK) {
459 : 0 : return sbus_reply_internal_error(message, intf_p->conn);
460 : : }
461 : : }
462 : : }
463 : : else
464 : : return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
465 : : }
466 : :
467 : : return DBUS_HANDLER_RESULT_HANDLED;
468 : : }
469 : :
470 : : /* Adds a new D-BUS path message handler to the connection
471 : : * Note: this must be a unique path.
472 : : */
473 : 0 : int sbus_conn_add_interface(struct sbus_connection *conn,
474 : : struct sbus_interface *intf)
475 : : {
476 : : struct sbus_interface_p *intf_p;
477 : : dbus_bool_t dbret;
478 : : const char *path;
479 : :
480 [ # # ][ # # ]: 0 : if (!conn || !intf || !intf->vtable.message_function) {
481 : : return EINVAL;
482 : : }
483 : :
484 : 0 : path = intf->path;
485 : :
486 [ # # ]: 0 : if (path_in_interface_list(conn->intf_list, path)) {
487 [ # # ][ # # ]: 0 : DEBUG(0, ("Cannot add method context with identical path.\n"));
[ # # ][ # # ]
[ # # ]
488 : : return EINVAL;
489 : : }
490 : :
491 : 0 : intf_p = talloc_zero(conn, struct sbus_interface_p);
492 [ # # ]: 0 : if (!intf_p) {
493 : : return ENOMEM;
494 : : }
495 : 0 : intf_p->conn = conn;
496 : 0 : intf_p->intf = intf;
497 : :
498 [ # # ]: 0 : DLIST_ADD(conn->intf_list, intf_p);
499 : :
500 : 0 : dbret = dbus_connection_register_object_path(conn->dbus.conn,
501 : 0 : path, &intf->vtable, intf_p);
502 [ # # ]: 0 : if (!dbret) {
503 [ # # ][ # # ]: 0 : DEBUG(0, ("Could not register object path to the connection.\n"));
[ # # ][ # # ]
[ # # ]
504 : : return ENOMEM;
505 : : }
506 : :
507 : : return EOK;
508 : : }
509 : :
510 : 0 : static bool path_in_interface_list(struct sbus_interface_p *list,
511 : : const char *path)
512 : : {
513 : : struct sbus_interface_p *iter;
514 : :
515 [ # # ]: 0 : if (!list || !path) {
516 : : return false;
517 : : }
518 : :
519 : : iter = list;
520 [ # # ]: 0 : while (iter != NULL) {
521 [ # # ]: 0 : if (strcmp(iter->intf->path, path) == 0) {
522 : : return true;
523 : : }
524 : 0 : iter = iter->next;
525 : : }
526 : :
527 : : return false;
528 : : }
529 : :
530 : 0 : static void sbus_unreg_object_paths(struct sbus_connection *conn)
531 : : {
532 : 0 : struct sbus_interface_p *iter = conn->intf_list;
533 : :
534 [ # # ]: 0 : while (iter != NULL) {
535 : 0 : dbus_connection_unregister_object_path(conn->dbus.conn,
536 : 0 : iter->intf->path);
537 : 0 : iter = iter->next;
538 : : }
539 : 0 : }
540 : :
541 : 0 : void sbus_conn_set_private_data(struct sbus_connection *conn, void *pvt_data)
542 : : {
543 : 0 : conn->pvt_data = pvt_data;
544 : 0 : }
545 : :
546 : 0 : void *sbus_conn_get_private_data(struct sbus_connection *conn)
547 : : {
548 : 0 : return conn->pvt_data;
549 : : }
550 : :
551 : 0 : static void sbus_reconnect(struct tevent_context *ev,
552 : : struct tevent_timer *te,
553 : : struct timeval tv, void *data)
554 : : {
555 : : struct sbus_connection *conn;
556 : : struct sbus_interface_p *iter;
557 : : DBusError dbus_error;
558 : : dbus_bool_t dbret;
559 : : int ret;
560 : :
561 : 0 : conn = talloc_get_type(data, struct sbus_connection);
562 : 0 : dbus_error_init(&dbus_error);
563 : :
564 [ # # ][ # # ]: 0 : DEBUG(3, ("Making reconnection attempt %d to [%s]\n",
[ # # ][ # # ]
[ # # ]
565 : : conn->retries, conn->address));
566 : 0 : conn->dbus.conn = dbus_connection_open(conn->address, &dbus_error);
567 [ # # ]: 0 : if (conn->dbus.conn) {
568 : : /* We successfully reconnected. Set up mainloop integration. */
569 [ # # ][ # # ]: 0 : DEBUG(3, ("Reconnected to [%s]\n", conn->address));
[ # # ][ # # ]
[ # # ]
570 : 0 : ret = sbus_conn_set_fns(conn);
571 [ # # ]: 0 : if (ret != EOK) {
572 : 0 : dbus_connection_unref(conn->dbus.conn);
573 : : goto failed;
574 : : }
575 : :
576 : : /* Re-register object paths */
577 : 0 : iter = conn->intf_list;
578 [ # # ]: 0 : while (iter) {
579 : 0 : dbret = dbus_connection_register_object_path(conn->dbus.conn,
580 : : iter->intf->path,
581 : 0 : &iter->intf->vtable,
582 : : iter);
583 [ # # ]: 0 : if (!dbret) {
584 [ # # ][ # # ]: 0 : DEBUG(0, ("Could not register object path.\n"));
[ # # ][ # # ]
[ # # ]
585 : 0 : dbus_connection_unref(conn->dbus.conn);
586 : : goto failed;
587 : : }
588 : 0 : iter = iter->next;
589 : : }
590 : :
591 : : /* Reset retries to 0 to resume dispatch processing */
592 : 0 : conn->retries = 0;
593 : :
594 : : /* Notify the owner of this connection that the
595 : : * reconnection was successful
596 : : */
597 : 0 : conn->reconnect_callback(conn,
598 : : SBUS_RECONNECT_SUCCESS,
599 : : conn->reconnect_pvt);
600 : 0 : return;
601 : : }
602 : :
603 : : failed:
604 : : /* Reconnection failed, try again in a few seconds */
605 [ # # ][ # # ]: 0 : DEBUG(1, ("Failed to open connection: name=%s, message=%s\n",
[ # # ][ # # ]
[ # # ]
606 : : dbus_error.name, dbus_error.message));
607 [ # # ]: 0 : if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
608 : :
609 : 0 : conn->retries++;
610 : :
611 : : /* Check if we've passed our last chance or if we've lost track of
612 : : * our retry count somehow
613 : : */
614 [ # # ][ # # ]: 0 : if ((conn->retries > conn->max_retries) || (conn->retries <= 0)) {
615 : 0 : conn->reconnect_callback(conn,
616 : : SBUS_RECONNECT_EXCEEDED_RETRIES,
617 : : conn->reconnect_pvt);
618 : : }
619 : :
620 [ # # ]: 0 : if (conn->retries == 2) {
621 : : /* Wait 3 seconds before the second reconnect attempt */
622 : 0 : tv.tv_sec += 3;
623 : : }
624 [ # # ]: 0 : else if (conn->retries == 3) {
625 : : /* Wait 10 seconds before the third reconnect attempt */
626 : 0 : tv.tv_sec += 10;
627 : : }
628 : : else {
629 : : /* Wait 30 seconds before all subsequent reconnect attempts */
630 : 0 : tv.tv_sec += 30;
631 : : }
632 : :
633 : 0 : te = tevent_add_timer(conn->ev, conn, tv, sbus_reconnect, conn);
634 [ # # ]: 0 : if (!te) {
635 : 0 : conn->reconnect_callback(conn,
636 : : SBUS_RECONNECT_ERROR,
637 : : conn->reconnect_pvt);
638 : : }
639 : : }
640 : :
641 : : /* This function will free and recreate the sbus_connection,
642 : : * calling functions need to be aware of this (and whether
643 : : * they have attached a talloc destructor to the
644 : : * sbus_connection.
645 : : */
646 : 0 : static int sbus_auto_reconnect(struct sbus_connection *conn)
647 : : {
648 : 0 : struct tevent_timer *te = NULL;
649 : : struct timeval tv;
650 : :
651 : 0 : conn->retries++;
652 [ # # ]: 0 : if (conn->retries >= conn->max_retries) {
653 : : /* Return EIO (to tell the calling process it
654 : : * needs to create a new connection from scratch
655 : : */
656 : : return EIO;
657 : : }
658 : :
659 : 0 : gettimeofday(&tv, NULL);
660 : 0 : tv.tv_sec += 1; /* Wait 1 second before the first reconnect attempt */
661 : 0 : te = tevent_add_timer(conn->ev, conn, tv, sbus_reconnect, conn);
662 [ # # ]: 0 : if (!te) {
663 : : return EIO;
664 : : }
665 : :
666 : : return EOK;
667 : : }
668 : :
669 : : /* Max retries */
670 : 0 : void sbus_reconnect_init(struct sbus_connection *conn,
671 : : int max_retries,
672 : : sbus_conn_reconn_callback_fn callback,
673 : : void *pvt)
674 : : {
675 [ # # ]: 0 : if (max_retries < 0 || callback == NULL) return;
676 : :
677 : 0 : conn->retries = 0;
678 : 0 : conn->max_retries = max_retries;
679 : 0 : conn->reconnect_callback = callback;
680 : 0 : conn->reconnect_pvt = pvt;
681 : : }
682 : :
683 : 0 : bool sbus_conn_disconnecting(struct sbus_connection *conn)
684 : : {
685 [ # # ]: 0 : if (conn->disconnect == 1) return true;
686 : 0 : return false;
687 : : }
688 : :
689 : : /*
690 : : * Send a message across the SBUS
691 : : * If requested, the DBusPendingCall object will
692 : : * be returned to the caller.
693 : : *
694 : : * This function will return EAGAIN in the event
695 : : * that the connection is not open for
696 : : * communication.
697 : : */
698 : 0 : int sbus_conn_send(struct sbus_connection *conn,
699 : : DBusMessage *msg,
700 : : int timeout_ms,
701 : : DBusPendingCallNotifyFunction reply_handler,
702 : : void *pvt,
703 : : DBusPendingCall **pending)
704 : : {
705 : : DBusPendingCall *pending_reply;
706 : : DBusConnection *dbus_conn;
707 : : dbus_bool_t dbret;
708 : :
709 : 0 : dbus_conn = sbus_get_connection(conn);
710 [ # # ]: 0 : if (!dbus_conn) {
711 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE, ("D-BUS not connected\n"));
[ # # ][ # # ]
[ # # ]
712 : : return ENOTCONN;
713 : : }
714 : :
715 : 0 : dbret = dbus_connection_send_with_reply(dbus_conn, msg,
716 : : &pending_reply,
717 : : timeout_ms);
718 [ # # ]: 0 : if (!dbret) {
719 : : /*
720 : : * Critical Failure
721 : : * Insufficient memory to send message
722 : : */
723 [ # # ][ # # ]: 0 : DEBUG(0, ("D-BUS send failed.\n"));
[ # # ][ # # ]
[ # # ]
724 : : return ENOMEM;
725 : : }
726 : :
727 [ # # ]: 0 : if (pending_reply) {
728 : : /* Set up the reply handler */
729 : 0 : dbret = dbus_pending_call_set_notify(pending_reply, reply_handler,
730 : : pvt, NULL);
731 [ # # ]: 0 : if (!dbret) {
732 : : /*
733 : : * Critical Failure
734 : : * Insufficient memory to create pending call notify
735 : : */
736 [ # # ][ # # ]: 0 : DEBUG(0, ("D-BUS send failed.\n"));
[ # # ][ # # ]
[ # # ]
737 : 0 : dbus_pending_call_cancel(pending_reply);
738 : 0 : dbus_pending_call_unref(pending_reply);
739 : : return ENOMEM;
740 : : }
741 : :
742 [ # # ]: 0 : if(pending) {
743 : 0 : *pending = pending_reply;
744 : : }
745 : : return EOK;
746 : : }
747 : :
748 : : /* If pending_reply is NULL, the connection was not
749 : : * open for sending.
750 : : */
751 : :
752 : : /* TODO: Create a callback into the reconnection logic so this
753 : : * request is invoked when the connection is re-established
754 : : */
755 : : return EAGAIN;
756 : : }
757 : :
758 : 0 : void sbus_conn_send_reply(struct sbus_connection *conn, DBusMessage *reply)
759 : : {
760 : 0 : dbus_connection_send(conn->dbus.conn, reply, NULL);
761 : 0 : }
762 : :
|