sssd_dbus_connection.c revision d87e960c17d7598781cf032d06ba03a3ecadbfa2
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest/*
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest Authors:
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest Simo Sorce <ssorce@redhat.com>
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest Stephen Gallagher <sgallagh@redhat.com>
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest Copyright (C) 2009 Red Hat
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley This program is free software; you can redistribute it and/or modify
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest it under the terms of the GNU General Public License as published by
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest the Free Software Foundation; either version 3 of the License, or
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest (at your option) any later version.
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley This program is distributed in the hope that it will be useful,
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest but WITHOUT ANY WARRANTY; without even the implied warranty of
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley GNU General Public License for more details.
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley You should have received a copy of the GNU General Public License
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley along with this program. If not, see <http://www.gnu.org/licenses/>.
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley*/
919ca0cd7bf1dee3c8021e1c80bd699c7a386001Andrew Forrest
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley#include <sys/time.h>
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley#include <dbus/dbus.h>
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura#include "util/util.h"
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley#include "sbus/sssd_dbus.h"
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley#include "sbus/sssd_dbus_private.h"
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley#include "sbus/sssd_dbus_meta.h"
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley/* Types */
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Rileystruct dbus_ctx_list;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Rileystatic int sbus_auto_reconnect(struct sbus_connection *conn);
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Rileystatic void sbus_dispatch(struct tevent_context *ev,
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley struct tevent_timer *te,
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley struct timeval tv, void *data)
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley{
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley struct tevent_timer *new_event;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley struct sbus_connection *conn;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley DBusConnection *dbus_conn;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley int ret;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley if (data == NULL) return;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley conn = talloc_get_type(data, struct sbus_connection);
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley dbus_conn = conn->dbus.conn;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley DEBUG(SSSDBG_TRACE_ALL, "dbus conn: %p\n", dbus_conn);
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley if (conn->retries > 0) {
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley DEBUG(SSSDBG_TRACE_FUNC, "SBUS is reconnecting. Deferring.\n");
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley /* Currently trying to reconnect, defer dispatch for 30ms */
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura tv = tevent_timeval_current_ofs(0, 30);
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura new_event = tevent_add_timer(ev, conn, tv, sbus_dispatch, conn);
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura if (new_event == NULL) {
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura DEBUG(SSSDBG_FATAL_FAILURE,"Could not defer dispatch!\n");
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura }
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley return;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley }
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley if ((!dbus_connection_get_is_connected(dbus_conn)) &&
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley (conn->max_retries != 0)) {
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley /* Attempt to reconnect automatically */
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley ret = sbus_auto_reconnect(conn);
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley if (ret == EOK) {
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley DEBUG(SSSDBG_CRIT_FAILURE, "Performing auto-reconnect\n");
8cabece6bea817ce5b6370e695ac57dbb7242790Rich Riley return;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley }
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura DEBUG(SSSDBG_FATAL_FAILURE, "Cannot start auto-reconnection.\n");
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura conn->reconnect_callback(conn,
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura SBUS_RECONNECT_ERROR,
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley conn->reconnect_pvt);
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley return;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley }
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley if ((conn->disconnect) ||
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley (!dbus_connection_get_is_connected(dbus_conn))) {
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley DEBUG(SSSDBG_MINOR_FAILURE,"Connection is not open for dispatching.\n");
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley /*
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley * Free the connection object.
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley * This will invoke the destructor for the connection
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura */
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura talloc_free(conn);
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura conn = NULL;
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura return;
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley }
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley /* Dispatch only once each time through the mainloop to avoid
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley * starving other features
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley */
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley ret = dbus_connection_get_dispatch_status(dbus_conn);
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley if (ret != DBUS_DISPATCH_COMPLETE) {
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley DEBUG(SSSDBG_TRACE_ALL,"Dispatching.\n");
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley dbus_connection_dispatch(dbus_conn);
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura }
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley /* If other dispatches are waiting, queue up the dispatch function
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley * for the next loop.
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley */
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley ret = dbus_connection_get_dispatch_status(dbus_conn);
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley if (ret != DBUS_DISPATCH_COMPLETE) {
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley new_event = tevent_add_timer(ev, conn, tv, sbus_dispatch, conn);
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley if (new_event == NULL) {
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley DEBUG(SSSDBG_OP_FAILURE,"Could not add dispatch event!\n");
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley /* TODO: Calling exit here is bad */
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura exit(1);
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley }
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura }
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura}
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley/* dbus_connection_wakeup_main
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley * D-BUS makes a callback to the wakeup_main function when
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura * it has data available for dispatching.
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley * In order to avoid blocking, this function will create a now()
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura * timed event to perform the dispatch during the next iteration
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley * through the mainloop
36a02738d49e7ba01c12b9e37beb841cb277417dKohei Tamura */
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Rileystatic void sbus_conn_wakeup_main(void *data)
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley{
3b96b9949ee573b761bdb4e5cc8cf7582c2747c2Rich Riley struct sbus_connection *conn;
struct timeval tv;
struct tevent_timer *te;
conn = talloc_get_type(data, struct sbus_connection);
tv = tevent_timeval_current();
/* D-BUS calls this function when it is time to do a dispatch */
te = tevent_add_timer(conn->ev, conn, tv, sbus_dispatch, conn);
if (te == NULL) {
DEBUG(SSSDBG_OP_FAILURE,"Could not add dispatch event!\n");
/* TODO: Calling exit here is bad */
exit(1);
}
}
static int sbus_conn_set_fns(struct sbus_connection *conn);
/*
* integrate_connection_with_event_loop
* Set up a D-BUS connection to use the libevents mainloop
* for handling file descriptor and timed events
*/
int sbus_init_connection(TALLOC_CTX *ctx,
struct tevent_context *ev,
DBusConnection *dbus_conn,
int connection_type,
struct sbus_connection **_conn)
{
struct sbus_connection *conn;
int ret;
DEBUG(SSSDBG_TRACE_FUNC,"Adding connection %p\n", dbus_conn);
conn = talloc_zero(ctx, struct sbus_connection);
conn->ev = ev;
conn->type = SBUS_CONNECTION;
conn->dbus.conn = dbus_conn;
conn->connection_type = connection_type;
ret = sss_hash_create(conn, 32, &conn->clients);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create clients hash table\n");
talloc_free(conn);
return EIO;
}
ret = sbus_conn_set_fns(conn);
if (ret != EOK) {
talloc_free(conn);
return ret;
}
*_conn = conn;
return ret;
}
static int sbus_conn_set_fns(struct sbus_connection *conn)
{
dbus_bool_t dbret;
/* Set up DBusWatch functions */
dbret = dbus_connection_set_watch_functions(conn->dbus.conn,
sbus_add_watch,
sbus_remove_watch,
sbus_toggle_watch,
conn, NULL);
if (!dbret) {
DEBUG(SSSDBG_OP_FAILURE,
"Error setting up D-BUS connection watch functions\n");
return EIO;
}
/* Set up DBusTimeout functions */
dbret = dbus_connection_set_timeout_functions(conn->dbus.conn,
sbus_add_timeout,
sbus_remove_timeout,
sbus_toggle_timeout,
conn, NULL);
if (!dbret) {
DEBUG(SSSDBG_OP_FAILURE,
"Error setting up D-BUS server timeout functions\n");
/* FIXME: free resources ? */
return EIO;
}
/* Set up dispatch handler */
dbus_connection_set_wakeup_main_function(conn->dbus.conn,
sbus_conn_wakeup_main,
conn, NULL);
/* Set up any method_contexts passed in */
/* Attempt to dispatch immediately in case of opportunistic
* services connecting before the handlers were all up.
* If there are no messages to be dispatched, this will do
* nothing.
*/
sbus_conn_wakeup_main(conn);
return EOK;
}
int sbus_new_connection(TALLOC_CTX *ctx, struct tevent_context *ev,
const char *address, struct sbus_connection **_conn)
{
struct sbus_connection *conn;
DBusConnection *dbus_conn;
DBusError dbus_error;
int ret;
dbus_error_init(&dbus_error);
/* Open a shared D-BUS connection to the address */
dbus_conn = dbus_connection_open(address, &dbus_error);
if (!dbus_conn) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to open connection: name=%s, message=%s\n",
dbus_error.name, dbus_error.message);
if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
return EIO;
}
ret = sbus_init_connection(ctx, ev, dbus_conn, SBUS_CONN_TYPE_SHARED, &conn);
if (ret != EOK) {
/* FIXME: release resources */
}
/* Store the address for later reconnection */
conn->address = talloc_strdup(conn, address);
dbus_connection_set_exit_on_disconnect(conn->dbus.conn, FALSE);
*_conn = conn;
return ret;
}
static int connection_destructor(void *ctx)
{
struct sbus_connection *conn;
conn = talloc_get_type(ctx, struct sbus_connection);
DEBUG(SSSDBG_TRACE_FUNC, "Invoking default destructor on connection %p\n",
conn->dbus.conn);
if (conn->connection_type == SBUS_CONN_TYPE_PRIVATE) {
/* Private connections must be closed explicitly */
dbus_connection_close(conn->dbus.conn);
}
else if (conn->connection_type == SBUS_CONN_TYPE_SHARED ||
conn->connection_type == SBUS_CONN_TYPE_SYSBUS) {
/* Shared and system bus connections are destroyed when their last
reference is removed */
}
else {
/* Critical Error! */
DEBUG(SSSDBG_CRIT_FAILURE,
"Critical Error, connection_type is neither shared nor private!\n");
return -1;
}
/* Remove object path */
/* TODO: Remove object paths */
dbus_connection_unref(conn->dbus.conn);
return 0;
}
/*
* sbus_get_connection
* Utility function to retreive the DBusConnection object
* from a sbus_connection
*/
DBusConnection *sbus_get_connection(struct sbus_connection *conn)
{
return conn->dbus.conn;
}
void sbus_disconnect(struct sbus_connection *conn)
{
if (conn == NULL) {
return;
}
DEBUG(SSSDBG_TRACE_FUNC, "Disconnecting %p\n", conn->dbus.conn);
/*******************************
* Referencing conn->dbus.conn */
dbus_connection_ref(conn->dbus.conn);
conn->disconnect = 1;
/* Unregister object paths */
sbus_unreg_object_paths(conn);
/* Disable watch functions */
dbus_connection_set_watch_functions(conn->dbus.conn,
NULL, NULL, NULL,
NULL, NULL);
/* Disable timeout functions */
dbus_connection_set_timeout_functions(conn->dbus.conn,
NULL, NULL, NULL,
NULL, NULL);
/* Disable dispatch status function */
dbus_connection_set_dispatch_status_function(conn->dbus.conn,
NULL, NULL, NULL);
/* Disable wakeup main function */
dbus_connection_set_wakeup_main_function(conn->dbus.conn,
NULL, NULL, NULL);
/* Finalize the connection */
connection_destructor(conn);
dbus_connection_unref(conn->dbus.conn);
/* Unreferenced conn->dbus_conn *
******************************/
DEBUG(SSSDBG_TRACE_FUNC ,"Disconnected %p\n", conn->dbus.conn);
}
static void sbus_handler_got_caller_id(struct tevent_req *req);
/* messsage_handler
* Receive messages and process them
*/
DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn,
DBusMessage *message,
void *user_data)
{
struct sbus_interface_p *intf_p;
const char *msg_method;
const char *path;
const char *msg_interface;
const char *sender;
struct sbus_request *dbus_req = NULL;
struct tevent_req *req;
if (!user_data) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
intf_p = talloc_get_type(user_data, struct sbus_interface_p);
msg_method = dbus_message_get_member(message);
DEBUG(SSSDBG_TRACE_ALL, "Received SBUS method [%s]\n", msg_method);
path = dbus_message_get_path(message);
msg_interface = dbus_message_get_interface(message);
sender = dbus_message_get_sender(message);
if (!msg_method || !path || !msg_interface) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
/* Validate the D-BUS path */
if (!sbus_iface_handles_path(intf_p, path)) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
/* Validate the method interface */
if (strcmp(msg_interface, intf_p->intf->vtable->meta->name) == 0 ||
strcmp(msg_interface, DBUS_PROPERTIES_INTERFACE) == 0 ||
(strcmp(msg_interface, DBUS_INTROSPECT_INTERFACE) == 0 &&
strcmp(msg_method, DBUS_INTROSPECT_METHOD) == 0)) {
/* OK, this message for us. Get the sender ID if applicable */
dbus_req = sbus_new_request(intf_p->conn, intf_p->intf, message);
if (dbus_req == NULL) {
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
req = sbus_get_sender_id_send(dbus_req, dbus_req->conn->ev,
dbus_req->conn, sender);
if (req == NULL) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
tevent_req_set_callback(req, sbus_handler_got_caller_id, dbus_req);
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static void sbus_handler_got_caller_id(struct tevent_req *req)
{
struct sbus_request *dbus_req = \
tevent_req_callback_data(req,
struct sbus_request);
errno_t ret;
DBusError *dberr;
DBusMessage *reply = NULL;
const struct sbus_method_meta *method;
const struct sbus_interface_meta *interface;
const char *msg_method;
const char *msg_interface;
sbus_msg_handler_fn handler_fn = NULL;
void *handler_data = NULL; /* Must be a talloc pointer! */
struct sbus_introspect_ctx *ictx = NULL;
const char *dbus_error = NULL;
ret = sbus_get_sender_id_recv(req, &dbus_req->client);
if (ret != EOK) {
dberr = sbus_error_new(dbus_req,
DBUS_ERROR_FAILED,
"Failed to retrieve called ID: %s\n",
sss_strerror(ret));
sbus_request_fail_and_finish(dbus_req, dberr);
return;
}
msg_method = dbus_message_get_member(dbus_req->message);
msg_interface = dbus_message_get_interface(dbus_req->message);
DEBUG(SSSDBG_TRACE_ALL, "Received SBUS method [%s]\n", msg_method);
/* Prepare the handler */
interface = dbus_req->intf->vtable->meta;
if (strcmp(msg_interface, interface->name) == 0) {
method = sbus_meta_find_method(interface, msg_method);
if (method && method->vtable_offset)
handler_fn = VTABLE_FUNC(dbus_req->intf->vtable,
method->vtable_offset);
if (!method) {
/* Reply DBUS_ERROR_UNKNOWN_METHOD */
DEBUG(SSSDBG_CRIT_FAILURE,
"No matching method found for %s.\n", msg_method);
dbus_error = DBUS_ERROR_UNKNOWN_METHOD;
goto fail;
} else if (!handler_fn) {
/* Reply DBUS_ERROR_NOT_SUPPORTED */
DEBUG(SSSDBG_CRIT_FAILURE,
"No handler provided found for %s.\n", msg_method);
dbus_error = DBUS_ERROR_NOT_SUPPORTED;
goto fail;
}
} else if (strcmp(msg_interface, DBUS_INTROSPECT_INTERFACE) == 0 &&
strcmp(msg_method, DBUS_INTROSPECT_METHOD) == 0) {
DEBUG(SSSDBG_TRACE_LIBS, "Got introspection request\n");
ictx = talloc(dbus_req->conn, struct sbus_introspect_ctx);
if (ictx == NULL) {
dbus_error = DBUS_ERROR_NO_MEMORY;
goto fail;
}
handler_fn = sbus_introspect;
ictx->iface = interface;
handler_data = ictx;
method = &introspect_method;
} else if (strcmp(msg_interface, DBUS_PROPERTIES_INTERFACE) == 0) {
ret = sbus_properties_dispatch(dbus_req);
if (ret == ERR_SBUS_NOSUP) {
/* No known method matched */
dbus_error = DBUS_ERROR_NOT_SUPPORTED;
goto fail;
}
/* sbus_properties_dispatch handles all other errors
* or success internally
*/
return;
}
if (handler_fn == NULL) {
DEBUG(SSSDBG_MINOR_FAILURE, "No handler matched!\n");
dbus_error = DBUS_ERROR_NOT_SUPPORTED;
goto fail;
}
dbus_req->method = method;
if (handler_data) {
/* If the handler uses private instance data, make
* sure they go away when the request does
*/
talloc_steal(dbus_req, handler_data);
} else {
/* If no custom handler data is set, pass on the
* interface data
*/
handler_data = dbus_req->intf->instance_data;
}
sbus_request_invoke_or_finish(dbus_req,
handler_fn,
handler_data,
method->invoker);
return;
fail:
reply = dbus_message_new_error(dbus_req->message,
dbus_error ? dbus_error : DBUS_ERROR_FAILED,
NULL);
sbus_request_finish(dbus_req, reply);
}
static void sbus_reconnect(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval tv, void *data)
{
struct sbus_connection *conn;
struct sbus_interface_p *iter;
DBusError dbus_error;
dbus_bool_t dbret;
int ret;
conn = talloc_get_type(data, struct sbus_connection);
dbus_error_init(&dbus_error);
DEBUG(SSSDBG_MINOR_FAILURE, "Making reconnection attempt %d to [%s]\n",
conn->retries, conn->address);
conn->dbus.conn = dbus_connection_open(conn->address, &dbus_error);
if (conn->dbus.conn) {
/* We successfully reconnected. Set up mainloop integration. */
DEBUG(SSSDBG_MINOR_FAILURE, "Reconnected to [%s]\n", conn->address);
ret = sbus_conn_set_fns(conn);
if (ret != EOK) {
dbus_connection_unref(conn->dbus.conn);
goto failed;
}
/* Re-register object paths */
iter = conn->intf_list;
while (iter) {
dbret = dbus_connection_register_object_path(conn->dbus.conn,
iter->intf->path,
&dbus_object_path_vtable,
iter);
if (!dbret) {
DEBUG(SSSDBG_FATAL_FAILURE,
"Could not register object path.\n");
dbus_connection_unref(conn->dbus.conn);
goto failed;
}
iter = iter->next;
}
/* Reset retries to 0 to resume dispatch processing */
conn->retries = 0;
/* Notify the owner of this connection that the
* reconnection was successful
*/
conn->reconnect_callback(conn,
SBUS_RECONNECT_SUCCESS,
conn->reconnect_pvt);
return;
}
failed:
/* Reconnection failed, try again in a few seconds */
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to open connection: name=%s, message=%s\n",
dbus_error.name, dbus_error.message);
if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
conn->retries++;
/* Check if we've passed our last chance or if we've lost track of
* our retry count somehow
*/
if ((conn->retries > conn->max_retries) || (conn->retries <= 0)) {
conn->reconnect_callback(conn,
SBUS_RECONNECT_EXCEEDED_RETRIES,
conn->reconnect_pvt);
}
if (conn->retries == 2) {
/* Wait 3 seconds before the second reconnect attempt */
tv.tv_sec += 3;
}
else if (conn->retries == 3) {
/* Wait 10 seconds before the third reconnect attempt */
tv.tv_sec += 10;
}
else {
/* Wait 30 seconds before all subsequent reconnect attempts */
tv.tv_sec += 30;
}
te = tevent_add_timer(conn->ev, conn, tv, sbus_reconnect, conn);
if (!te) {
conn->reconnect_callback(conn,
SBUS_RECONNECT_ERROR,
conn->reconnect_pvt);
}
}
/* This function will free and recreate the sbus_connection,
* calling functions need to be aware of this (and whether
* they have attached a talloc destructor to the
* sbus_connection.
*/
static int sbus_auto_reconnect(struct sbus_connection *conn)
{
struct tevent_timer *te = NULL;
struct timeval tv;
conn->retries++;
if (conn->retries >= conn->max_retries) {
/* Return EIO (to tell the calling process it
* needs to create a new connection from scratch
*/
return EIO;
}
gettimeofday(&tv, NULL);
tv.tv_sec += 1; /* Wait 1 second before the first reconnect attempt */
te = tevent_add_timer(conn->ev, conn, tv, sbus_reconnect, conn);
if (!te) {
return EIO;
}
return EOK;
}
/* Max retries */
void sbus_reconnect_init(struct sbus_connection *conn,
int max_retries,
sbus_conn_reconn_callback_fn callback,
void *pvt)
{
if (max_retries < 0 || callback == NULL) return;
conn->retries = 0;
conn->max_retries = max_retries;
conn->reconnect_callback = callback;
conn->reconnect_pvt = pvt;
}
bool sbus_conn_disconnecting(struct sbus_connection *conn)
{
if (conn->disconnect == 1) return true;
return false;
}
int sss_dbus_conn_send(DBusConnection *dbus_conn,
DBusMessage *msg,
int timeout_ms,
DBusPendingCallNotifyFunction reply_handler,
void *pvt,
DBusPendingCall **pending)
{
DBusPendingCall *pending_reply;
dbus_bool_t dbret;
dbret = dbus_connection_send_with_reply(dbus_conn, msg,
&pending_reply,
timeout_ms);
if (!dbret) {
/*
* Critical Failure
* Insufficient memory to send message
*/
DEBUG(SSSDBG_FATAL_FAILURE, "D-BUS send failed.\n");
return ENOMEM;
}
if (pending_reply) {
/* Set up the reply handler */
dbret = dbus_pending_call_set_notify(pending_reply, reply_handler,
pvt, NULL);
if (!dbret) {
/*
* Critical Failure
* Insufficient memory to create pending call notify
*/
DEBUG(SSSDBG_FATAL_FAILURE, "D-BUS send failed.\n");
dbus_pending_call_cancel(pending_reply);
dbus_pending_call_unref(pending_reply);
return ENOMEM;
}
if(pending) {
*pending = pending_reply;
}
return EOK;
}
/* If pending_reply is NULL, the connection was not
* open for sending.
*/
/* TODO: Create a callback into the reconnection logic so this
* request is invoked when the connection is re-established
*/
return EAGAIN;
}
/*
* Send a message across the SBUS
* If requested, the DBusPendingCall object will
* be returned to the caller.
*
* This function will return EAGAIN in the event
* that the connection is not open for
* communication.
*/
int sbus_conn_send(struct sbus_connection *conn,
DBusMessage *msg,
int timeout_ms,
DBusPendingCallNotifyFunction reply_handler,
void *pvt,
DBusPendingCall **pending)
{
DBusConnection *dbus_conn;
dbus_conn = sbus_get_connection(conn);
if (!dbus_conn) {
DEBUG(SSSDBG_CRIT_FAILURE, "D-BUS not connected\n");
return ENOTCONN;
}
return sss_dbus_conn_send(dbus_conn, msg, timeout_ms,
reply_handler, pvt, pending);
}
void sbus_conn_send_reply(struct sbus_connection *conn, DBusMessage *reply)
{
dbus_connection_send(conn->dbus.conn, reply, NULL);
}
dbus_bool_t is_uid_sssd_user(DBusConnection *connection,
unsigned long uid,
void *data)
{
uid_t sssd_user = * (uid_t *) data;
if (uid == 0 || uid == sssd_user) {
return TRUE;
}
return FALSE;
}
void sbus_allow_uid(struct sbus_connection *conn, uid_t *uid)
{
dbus_connection_set_unix_user_function(sbus_get_connection(conn),
is_uid_sssd_user,
uid, NULL);
}