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 <dbus/dbus.h>
24 :
25 : #include "util/util.h"
26 : #include "sbus/sssd_dbus.h"
27 : #include "sbus/sssd_dbus_private.h"
28 : #include "sbus/sssd_dbus_meta.h"
29 :
30 : /* Types */
31 : struct dbus_ctx_list;
32 :
33 : static int sbus_auto_reconnect(struct sbus_connection *conn);
34 :
35 0 : static void sbus_dispatch(struct tevent_context *ev,
36 : struct tevent_timer *te,
37 : struct timeval tv, void *data)
38 : {
39 : struct tevent_timer *new_event;
40 : struct sbus_connection *conn;
41 : DBusConnection *dbus_conn;
42 : int ret;
43 :
44 0 : if (data == NULL) return;
45 :
46 0 : conn = talloc_get_type(data, struct sbus_connection);
47 :
48 0 : dbus_conn = conn->dbus.conn;
49 0 : DEBUG(SSSDBG_TRACE_ALL, "dbus conn: %p\n", dbus_conn);
50 :
51 0 : if (conn->retries > 0) {
52 0 : DEBUG(SSSDBG_TRACE_FUNC, "SBUS is reconnecting. Deferring.\n");
53 : /* Currently trying to reconnect, defer dispatch for 30ms */
54 0 : tv = tevent_timeval_current_ofs(0, 30);
55 0 : new_event = tevent_add_timer(ev, conn, tv, sbus_dispatch, conn);
56 0 : if (new_event == NULL) {
57 0 : DEBUG(SSSDBG_FATAL_FAILURE,"Could not defer dispatch!\n");
58 : }
59 0 : return;
60 : }
61 :
62 0 : if ((!dbus_connection_get_is_connected(dbus_conn)) &&
63 0 : (conn->max_retries != 0)) {
64 : /* Attempt to reconnect automatically */
65 0 : ret = sbus_auto_reconnect(conn);
66 0 : if (ret == EOK) {
67 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Performing auto-reconnect\n");
68 0 : return;
69 : }
70 :
71 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Cannot start auto-reconnection.\n");
72 0 : conn->reconnect_callback(conn,
73 : SBUS_RECONNECT_ERROR,
74 : conn->reconnect_pvt);
75 0 : return;
76 : }
77 :
78 0 : if ((conn->disconnect) ||
79 0 : (!dbus_connection_get_is_connected(dbus_conn))) {
80 0 : DEBUG(SSSDBG_MINOR_FAILURE,"Connection is not open for dispatching.\n");
81 : /*
82 : * Free the connection object.
83 : * This will invoke the destructor for the connection
84 : */
85 0 : talloc_free(conn);
86 0 : conn = NULL;
87 0 : return;
88 : }
89 :
90 : /* Dispatch only once each time through the mainloop to avoid
91 : * starving other features
92 : */
93 0 : ret = dbus_connection_get_dispatch_status(dbus_conn);
94 0 : if (ret != DBUS_DISPATCH_COMPLETE) {
95 0 : DEBUG(SSSDBG_TRACE_ALL,"Dispatching.\n");
96 0 : dbus_connection_dispatch(dbus_conn);
97 : }
98 :
99 : /* If other dispatches are waiting, queue up the dispatch function
100 : * for the next loop.
101 : */
102 0 : ret = dbus_connection_get_dispatch_status(dbus_conn);
103 0 : if (ret != DBUS_DISPATCH_COMPLETE) {
104 0 : new_event = tevent_add_timer(ev, conn, tv, sbus_dispatch, conn);
105 0 : if (new_event == NULL) {
106 0 : DEBUG(SSSDBG_OP_FAILURE,"Could not add dispatch event!\n");
107 :
108 : /* TODO: Calling exit here is bad */
109 0 : exit(1);
110 : }
111 : }
112 : }
113 :
114 : /* dbus_connection_wakeup_main
115 : * D-BUS makes a callback to the wakeup_main function when
116 : * it has data available for dispatching.
117 : * In order to avoid blocking, this function will create a now()
118 : * timed event to perform the dispatch during the next iteration
119 : * through the mainloop
120 : */
121 0 : static void sbus_conn_wakeup_main(void *data)
122 : {
123 : struct sbus_connection *conn;
124 : struct timeval tv;
125 : struct tevent_timer *te;
126 :
127 0 : conn = talloc_get_type(data, struct sbus_connection);
128 :
129 0 : tv = tevent_timeval_current();
130 :
131 : /* D-BUS calls this function when it is time to do a dispatch */
132 0 : te = tevent_add_timer(conn->ev, conn, tv, sbus_dispatch, conn);
133 0 : if (te == NULL) {
134 0 : DEBUG(SSSDBG_OP_FAILURE,"Could not add dispatch event!\n");
135 : /* TODO: Calling exit here is bad */
136 0 : exit(1);
137 : }
138 0 : }
139 :
140 : static int sbus_conn_set_fns(struct sbus_connection *conn);
141 :
142 : /*
143 : * integrate_connection_with_event_loop
144 : * Set up a D-BUS connection to use the libevents mainloop
145 : * for handling file descriptor and timed events
146 : */
147 0 : int sbus_init_connection(TALLOC_CTX *ctx,
148 : struct tevent_context *ev,
149 : DBusConnection *dbus_conn,
150 : int connection_type,
151 : struct sbus_connection **_conn)
152 : {
153 : struct sbus_connection *conn;
154 : dbus_bool_t dbret;
155 : int ret;
156 :
157 0 : DEBUG(SSSDBG_TRACE_FUNC,"Adding connection %p\n", dbus_conn);
158 0 : conn = talloc_zero(ctx, struct sbus_connection);
159 :
160 0 : conn->ev = ev;
161 0 : conn->type = SBUS_CONNECTION;
162 0 : conn->dbus.conn = dbus_conn;
163 0 : conn->connection_type = connection_type;
164 :
165 0 : ret = sbus_opath_hash_init(conn, conn, &conn->managed_paths);
166 0 : if (ret != EOK) {
167 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create object paths hash table\n");
168 0 : talloc_free(conn);
169 0 : return EIO;
170 : }
171 :
172 0 : ret = sbus_nodes_hash_init(conn, conn, &conn->nodes_fns);
173 0 : if (ret != EOK) {
174 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create node functions hash table\n");
175 0 : talloc_free(conn);
176 0 : return EIO;
177 : }
178 :
179 0 : ret = sbus_incoming_signal_hash_init(conn, &conn->incoming_signals);
180 0 : if (ret != EOK) {
181 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create incoming singals "
182 : "hash table\n");
183 0 : talloc_free(conn);
184 0 : return EIO;
185 : }
186 :
187 0 : ret = sss_hash_create(conn, 32, &conn->clients);
188 0 : if (ret != EOK) {
189 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create clients hash table\n");
190 0 : talloc_free(conn);
191 0 : return EIO;
192 : }
193 :
194 0 : ret = sbus_conn_set_fns(conn);
195 0 : if (ret != EOK) {
196 0 : talloc_free(conn);
197 0 : return ret;
198 : }
199 :
200 : /* Set up signal handler. */
201 0 : dbret = dbus_connection_add_filter(dbus_conn, sbus_signal_handler, conn,
202 : NULL);
203 0 : if (dbret == false) {
204 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot register signal handler\n");
205 0 : talloc_free(conn);
206 0 : return EIO;
207 : }
208 :
209 0 : sbus_register_common_signals(conn, conn);
210 :
211 0 : *_conn = conn;
212 0 : return ret;
213 : }
214 :
215 0 : static int sbus_conn_set_fns(struct sbus_connection *conn)
216 : {
217 : dbus_bool_t dbret;
218 :
219 : /* Set up DBusWatch functions */
220 0 : dbret = dbus_connection_set_watch_functions(conn->dbus.conn,
221 : sbus_add_watch,
222 : sbus_remove_watch,
223 : sbus_toggle_watch,
224 : conn, NULL);
225 0 : if (!dbret) {
226 0 : DEBUG(SSSDBG_OP_FAILURE,
227 : "Error setting up D-BUS connection watch functions\n");
228 0 : return EIO;
229 : }
230 :
231 : /* Set up DBusTimeout functions */
232 0 : dbret = dbus_connection_set_timeout_functions(conn->dbus.conn,
233 : sbus_add_timeout,
234 : sbus_remove_timeout,
235 : sbus_toggle_timeout,
236 : conn, NULL);
237 0 : if (!dbret) {
238 0 : DEBUG(SSSDBG_OP_FAILURE,
239 : "Error setting up D-BUS server timeout functions\n");
240 : /* FIXME: free resources ? */
241 0 : return EIO;
242 : }
243 :
244 : /* Set up dispatch handler */
245 0 : dbus_connection_set_wakeup_main_function(conn->dbus.conn,
246 : sbus_conn_wakeup_main,
247 : conn, NULL);
248 :
249 : /* Set up any method_contexts passed in */
250 :
251 : /* Attempt to dispatch immediately in case of opportunistic
252 : * services connecting before the handlers were all up.
253 : * If there are no messages to be dispatched, this will do
254 : * nothing.
255 : */
256 0 : sbus_conn_wakeup_main(conn);
257 :
258 0 : return EOK;
259 : }
260 :
261 0 : int sbus_new_connection(TALLOC_CTX *ctx, struct tevent_context *ev,
262 : const char *address, struct sbus_connection **_conn)
263 : {
264 : struct sbus_connection *conn;
265 : DBusConnection *dbus_conn;
266 : DBusError dbus_error;
267 : int ret;
268 :
269 0 : dbus_error_init(&dbus_error);
270 :
271 : /* Open a shared D-BUS connection to the address */
272 0 : dbus_conn = dbus_connection_open(address, &dbus_error);
273 0 : if (!dbus_conn) {
274 0 : DEBUG(SSSDBG_CRIT_FAILURE,
275 : "Failed to open connection: name=%s, message=%s\n",
276 : dbus_error.name, dbus_error.message);
277 0 : if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
278 0 : return EIO;
279 : }
280 :
281 0 : ret = sbus_init_connection(ctx, ev, dbus_conn, SBUS_CONN_TYPE_SHARED, &conn);
282 : if (ret != EOK) {
283 : /* FIXME: release resources */
284 : }
285 :
286 : /* Store the address for later reconnection */
287 0 : conn->address = talloc_strdup(conn, address);
288 :
289 0 : dbus_connection_set_exit_on_disconnect(conn->dbus.conn, FALSE);
290 :
291 0 : *_conn = conn;
292 0 : return ret;
293 : }
294 :
295 0 : static int connection_destructor(void *ctx)
296 : {
297 : struct sbus_connection *conn;
298 0 : conn = talloc_get_type(ctx, struct sbus_connection);
299 :
300 0 : DEBUG(SSSDBG_TRACE_FUNC, "Invoking default destructor on connection %p\n",
301 : conn->dbus.conn);
302 0 : if (conn->connection_type == SBUS_CONN_TYPE_PRIVATE) {
303 : /* Private connections must be closed explicitly */
304 0 : dbus_connection_close(conn->dbus.conn);
305 : }
306 0 : else if (conn->connection_type == SBUS_CONN_TYPE_SHARED ||
307 0 : conn->connection_type == SBUS_CONN_TYPE_SYSBUS) {
308 : /* Shared and system bus connections are destroyed when their last
309 : reference is removed */
310 : }
311 : else {
312 : /* Critical Error! */
313 0 : DEBUG(SSSDBG_CRIT_FAILURE,
314 : "Critical Error, connection_type is neither shared nor private!\n");
315 0 : return -1;
316 : }
317 :
318 : /* Remove object path */
319 : /* TODO: Remove object paths */
320 :
321 0 : dbus_connection_unref(conn->dbus.conn);
322 0 : return 0;
323 : }
324 :
325 : /*
326 : * sbus_get_connection
327 : * Utility function to retreive the DBusConnection object
328 : * from a sbus_connection
329 : */
330 0 : DBusConnection *sbus_get_connection(struct sbus_connection *conn)
331 : {
332 0 : return conn->dbus.conn;
333 : }
334 :
335 0 : void sbus_disconnect(struct sbus_connection *conn)
336 : {
337 0 : if (conn == NULL) {
338 0 : return;
339 : }
340 :
341 0 : DEBUG(SSSDBG_TRACE_FUNC, "Disconnecting %p\n", conn->dbus.conn);
342 :
343 : /*******************************
344 : * Referencing conn->dbus.conn */
345 0 : dbus_connection_ref(conn->dbus.conn);
346 :
347 0 : conn->disconnect = 1;
348 :
349 : /* Unregister object paths */
350 0 : talloc_zfree(conn->managed_paths);
351 :
352 : /* Disable watch functions */
353 0 : dbus_connection_set_watch_functions(conn->dbus.conn,
354 : NULL, NULL, NULL,
355 : NULL, NULL);
356 : /* Disable timeout functions */
357 0 : dbus_connection_set_timeout_functions(conn->dbus.conn,
358 : NULL, NULL, NULL,
359 : NULL, NULL);
360 :
361 : /* Disable dispatch status function */
362 0 : dbus_connection_set_dispatch_status_function(conn->dbus.conn,
363 : NULL, NULL, NULL);
364 :
365 : /* Disable wakeup main function */
366 0 : dbus_connection_set_wakeup_main_function(conn->dbus.conn,
367 : NULL, NULL, NULL);
368 :
369 : /* Finalize the connection */
370 0 : connection_destructor(conn);
371 :
372 0 : dbus_connection_unref(conn->dbus.conn);
373 : /* Unreferenced conn->dbus_conn *
374 : ******************************/
375 :
376 0 : DEBUG(SSSDBG_TRACE_FUNC ,"Disconnected %p\n", conn->dbus.conn);
377 : }
378 :
379 0 : static void sbus_reconnect(struct tevent_context *ev,
380 : struct tevent_timer *te,
381 : struct timeval tv, void *data)
382 : {
383 : struct sbus_connection *conn;
384 : DBusError dbus_error;
385 : int ret;
386 :
387 0 : conn = talloc_get_type(data, struct sbus_connection);
388 0 : dbus_error_init(&dbus_error);
389 :
390 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Making reconnection attempt %d to [%s]\n",
391 : conn->retries, conn->address);
392 0 : conn->dbus.conn = dbus_connection_open(conn->address, &dbus_error);
393 0 : if (conn->dbus.conn) {
394 : /* We successfully reconnected. Set up mainloop integration. */
395 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Reconnected to [%s]\n", conn->address);
396 0 : ret = sbus_conn_set_fns(conn);
397 0 : if (ret != EOK) {
398 0 : dbus_connection_unref(conn->dbus.conn);
399 0 : goto failed;
400 : }
401 :
402 : /* Re-register object paths */
403 0 : sbus_conn_reregister_paths(conn);
404 :
405 : /* Reset retries to 0 to resume dispatch processing */
406 0 : conn->retries = 0;
407 :
408 : /* Notify the owner of this connection that the
409 : * reconnection was successful
410 : */
411 0 : conn->reconnect_callback(conn,
412 : SBUS_RECONNECT_SUCCESS,
413 : conn->reconnect_pvt);
414 0 : return;
415 : }
416 :
417 : failed:
418 : /* Reconnection failed, try again in a few seconds */
419 0 : DEBUG(SSSDBG_CRIT_FAILURE,
420 : "Failed to open connection: name=%s, message=%s\n",
421 : dbus_error.name, dbus_error.message);
422 0 : if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
423 :
424 0 : conn->retries++;
425 :
426 : /* Check if we've passed our last chance or if we've lost track of
427 : * our retry count somehow
428 : */
429 0 : if ((conn->retries > conn->max_retries) || (conn->retries <= 0)) {
430 0 : conn->reconnect_callback(conn,
431 : SBUS_RECONNECT_EXCEEDED_RETRIES,
432 : conn->reconnect_pvt);
433 : }
434 :
435 0 : if (conn->retries == 2) {
436 : /* Wait 3 seconds before the second reconnect attempt */
437 0 : tv.tv_sec += 3;
438 : }
439 0 : else if (conn->retries == 3) {
440 : /* Wait 10 seconds before the third reconnect attempt */
441 0 : tv.tv_sec += 10;
442 : }
443 : else {
444 : /* Wait 30 seconds before all subsequent reconnect attempts */
445 0 : tv.tv_sec += 30;
446 : }
447 :
448 0 : te = tevent_add_timer(conn->ev, conn, tv, sbus_reconnect, conn);
449 0 : if (!te) {
450 0 : conn->reconnect_callback(conn,
451 : SBUS_RECONNECT_ERROR,
452 : conn->reconnect_pvt);
453 : }
454 : }
455 :
456 : /* This function will free and recreate the sbus_connection,
457 : * calling functions need to be aware of this (and whether
458 : * they have attached a talloc destructor to the
459 : * sbus_connection.
460 : */
461 0 : static int sbus_auto_reconnect(struct sbus_connection *conn)
462 : {
463 0 : struct tevent_timer *te = NULL;
464 : struct timeval tv;
465 :
466 0 : conn->retries++;
467 0 : if (conn->retries >= conn->max_retries) {
468 : /* Return EIO (to tell the calling process it
469 : * needs to create a new connection from scratch
470 : */
471 0 : return EIO;
472 : }
473 :
474 0 : gettimeofday(&tv, NULL);
475 0 : tv.tv_sec += 1; /* Wait 1 second before the first reconnect attempt */
476 0 : te = tevent_add_timer(conn->ev, conn, tv, sbus_reconnect, conn);
477 0 : if (!te) {
478 0 : return EIO;
479 : }
480 :
481 0 : return EOK;
482 : }
483 :
484 : /* Max retries */
485 0 : void sbus_reconnect_init(struct sbus_connection *conn,
486 : int max_retries,
487 : sbus_conn_reconn_callback_fn callback,
488 : void *pvt)
489 : {
490 0 : if (max_retries < 0 || callback == NULL) return;
491 :
492 0 : conn->retries = 0;
493 0 : conn->max_retries = max_retries;
494 0 : conn->reconnect_callback = callback;
495 0 : conn->reconnect_pvt = pvt;
496 : }
497 :
498 0 : bool sbus_conn_disconnecting(struct sbus_connection *conn)
499 : {
500 0 : if (conn->disconnect == 1) return true;
501 0 : return false;
502 : }
503 :
504 0 : int sss_dbus_conn_send(DBusConnection *dbus_conn,
505 : DBusMessage *msg,
506 : int timeout_ms,
507 : DBusPendingCallNotifyFunction reply_handler,
508 : void *pvt,
509 : DBusPendingCall **pending)
510 : {
511 : DBusPendingCall *pending_reply;
512 : dbus_bool_t dbret;
513 :
514 0 : dbret = dbus_connection_send_with_reply(dbus_conn, msg,
515 : &pending_reply,
516 : timeout_ms);
517 0 : if (!dbret) {
518 : /*
519 : * Critical Failure
520 : * Insufficient memory to send message
521 : */
522 0 : DEBUG(SSSDBG_FATAL_FAILURE, "D-BUS send failed.\n");
523 0 : return ENOMEM;
524 : }
525 :
526 0 : if (pending_reply) {
527 : /* Set up the reply handler */
528 0 : dbret = dbus_pending_call_set_notify(pending_reply, reply_handler,
529 : pvt, NULL);
530 0 : if (!dbret) {
531 : /*
532 : * Critical Failure
533 : * Insufficient memory to create pending call notify
534 : */
535 0 : DEBUG(SSSDBG_FATAL_FAILURE, "D-BUS send failed.\n");
536 0 : dbus_pending_call_cancel(pending_reply);
537 0 : dbus_pending_call_unref(pending_reply);
538 0 : return ENOMEM;
539 : }
540 :
541 0 : if(pending) {
542 0 : *pending = pending_reply;
543 : }
544 0 : return EOK;
545 : }
546 :
547 : /* If pending_reply is NULL, the connection was not
548 : * open for sending.
549 : */
550 :
551 : /* TODO: Create a callback into the reconnection logic so this
552 : * request is invoked when the connection is re-established
553 : */
554 0 : return EAGAIN;
555 : }
556 :
557 : /*
558 : * Send a message across the SBUS
559 : * If requested, the DBusPendingCall object will
560 : * be returned to the caller.
561 : *
562 : * This function will return EAGAIN in the event
563 : * that the connection is not open for
564 : * communication.
565 : */
566 0 : int sbus_conn_send(struct sbus_connection *conn,
567 : DBusMessage *msg,
568 : int timeout_ms,
569 : DBusPendingCallNotifyFunction reply_handler,
570 : void *pvt,
571 : DBusPendingCall **pending)
572 : {
573 : DBusConnection *dbus_conn;
574 :
575 0 : dbus_conn = sbus_get_connection(conn);
576 0 : if (!dbus_conn) {
577 0 : DEBUG(SSSDBG_CRIT_FAILURE, "D-BUS not connected\n");
578 0 : return ENOTCONN;
579 : }
580 :
581 0 : return sss_dbus_conn_send(dbus_conn, msg, timeout_ms,
582 : reply_handler, pvt, pending);
583 : }
584 :
585 0 : void sbus_conn_send_reply(struct sbus_connection *conn, DBusMessage *reply)
586 : {
587 0 : dbus_connection_send(conn->dbus.conn, reply, NULL);
588 0 : }
589 :
590 0 : dbus_bool_t is_uid_sssd_user(DBusConnection *connection,
591 : unsigned long uid,
592 : void *data)
593 : {
594 0 : uid_t sssd_user = * (uid_t *) data;
595 :
596 0 : if (uid == 0 || uid == sssd_user) {
597 0 : return TRUE;
598 : }
599 :
600 0 : return FALSE;
601 : }
602 :
603 0 : void sbus_allow_uid(struct sbus_connection *conn, uid_t *uid)
604 : {
605 0 : dbus_connection_set_unix_user_function(sbus_get_connection(conn),
606 : is_uid_sssd_user,
607 : uid, NULL);
608 0 : }
|