summaryrefslogtreecommitdiffstats
path: root/src/sbus
diff options
context:
space:
mode:
Diffstat (limited to 'src/sbus')
-rw-r--r--src/sbus/sbus_client.c57
-rw-r--r--src/sbus/sbus_client.h36
-rw-r--r--src/sbus/sssd_dbus.h153
-rw-r--r--src/sbus/sssd_dbus_common.c444
-rw-r--r--src/sbus/sssd_dbus_connection.c692
-rw-r--r--src/sbus/sssd_dbus_private.h98
-rw-r--r--src/sbus/sssd_dbus_server.c171
7 files changed, 1651 insertions, 0 deletions
diff --git a/src/sbus/sbus_client.c b/src/sbus/sbus_client.c
new file mode 100644
index 000000000..df5c07120
--- /dev/null
+++ b/src/sbus/sbus_client.c
@@ -0,0 +1,57 @@
+/*
+ SSSD
+
+ Data Provider Helpers
+
+ Copyright (C) Stephen Gallagher <sgallagh@redhat.com> 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "talloc.h"
+#include "sbus_client.h"
+
+int sbus_client_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_address,
+ struct sbus_interface *intf,
+ struct sbus_connection **_conn,
+ sbus_conn_destructor_fn destructor,
+ void *conn_pvt_data)
+{
+ struct sbus_connection *conn = NULL;
+ int ret;
+
+ /* Validate input */
+ if (server_address == NULL) {
+ return EINVAL;
+ }
+
+ ret = sbus_new_connection(mem_ctx, ev, server_address, intf, &conn);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ /* Set connection destructor and private data */
+ sbus_conn_set_destructor(conn, destructor);
+ sbus_conn_set_private_data(conn, conn_pvt_data);
+
+ *_conn = conn;
+ return EOK;
+
+fail:
+ talloc_free(conn);
+ return ret;
+}
diff --git a/src/sbus/sbus_client.h b/src/sbus/sbus_client.h
new file mode 100644
index 000000000..742f8a10c
--- /dev/null
+++ b/src/sbus/sbus_client.h
@@ -0,0 +1,36 @@
+/*
+ SSSD
+
+ Data Provider Helpers
+
+ Copyright (C) Stephen Gallagher <sgallagh@redhat.com> 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SBUS_CLIENT_H_
+#define SBUS_CLIENT_H_
+
+#include "tevent.h"
+#include "sbus/sssd_dbus.h"
+
+int sbus_client_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_address,
+ struct sbus_interface *intf,
+ struct sbus_connection **_conn,
+ sbus_conn_destructor_fn destructor,
+ void *conn_pvt_data);
+
+#endif /* SBUS_CLIENT_H_ */
diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h
new file mode 100644
index 000000000..ac02c4440
--- /dev/null
+++ b/src/sbus/sssd_dbus.h
@@ -0,0 +1,153 @@
+/*
+ SSSD
+
+ SSSD - D-BUS interface
+
+ Copyright (C) Stephen Gallagher 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSSD_DBUS_H_
+#define _SSSD_DBUS_H_
+
+struct sbus_connection;
+
+#include "dbus/dbus.h"
+
+typedef int (*sbus_msg_handler_fn)(DBusMessage *, struct sbus_connection *);
+
+/*
+ * sbus_conn_destructor_fn
+ * Function to be called when a connection is finalized
+ */
+typedef int (*sbus_conn_destructor_fn)(void *);
+
+typedef void (*sbus_conn_reconn_callback_fn)(struct sbus_connection *, int, void *);
+
+/*
+ * sbus_server_conn_init_fn
+ * Set up function for connection-specific activities
+ * This function should define the sbus_conn_destructor_fn
+ * for this connection at a minimum
+ */
+typedef int (*sbus_server_conn_init_fn)(struct sbus_connection *, void *);
+
+enum {
+ SBUS_CONN_TYPE_PRIVATE = 1,
+ SBUS_CONN_TYPE_SHARED
+};
+
+enum {
+ SBUS_RECONNECT_SUCCESS = 1,
+ SBUS_RECONNECT_EXCEEDED_RETRIES,
+ SBUS_RECONNECT_ERROR
+};
+
+/* Special interface and method for D-BUS introspection */
+#define DBUS_INTROSPECT_INTERFACE "org.freedesktop.DBus.Introspectable"
+#define DBUS_INTROSPECT_METHOD "Introspect"
+
+#define SBUS_DEFAULT_VTABLE { NULL, sbus_message_handler, NULL, NULL, NULL, NULL }
+
+struct sbus_method {
+ const char *method;
+ sbus_msg_handler_fn fn;
+};
+
+struct sbus_interface {
+ const char *interface;
+ const char *path;
+ DBusObjectPathVTable vtable;
+ struct sbus_method *methods;
+ sbus_msg_handler_fn introspect_fn;
+};
+
+/* Server Functions */
+int sbus_new_server(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *address,
+ struct sbus_interface *intf,
+ struct sbus_connection **server,
+ sbus_server_conn_init_fn init_fn, void *init_pvt_data);
+
+/* Connection Functions */
+
+/* sbus_new_connection
+ * Use this function when connecting a new process to
+ * the standard SSSD interface.
+ * This will connect to the address specified and then
+ * call sbus_add_connection to integrate with the main
+ * loop.
+ */
+int sbus_new_connection(TALLOC_CTX *ctx,
+ struct tevent_context *ev,
+ const char *address,
+ struct sbus_interface *intf,
+ struct sbus_connection **conn);
+
+/* sbus_add_connection
+ * Integrates a D-BUS connection with the TEvent main
+ * loop. Use this function when you already have a
+ * DBusConnection object (for example from dbus_bus_get)
+ * Connection type can be either:
+ * SBUS_CONN_TYPE_PRIVATE: Used only from within a D-BUS
+ * server such as the Monitor in the
+ * new_connection_callback
+ * SBUS_CONN_TYPE_SHARED: Used for all D-BUS client
+ * connections, including those retrieved from
+ * dbus_bus_get
+ */
+int sbus_init_connection(TALLOC_CTX *ctx,
+ struct tevent_context *ev,
+ DBusConnection *dbus_conn,
+ struct sbus_interface *intf,
+ int connection_type,
+ struct sbus_connection **_conn);
+
+void sbus_conn_set_destructor(struct sbus_connection *conn,
+ sbus_conn_destructor_fn destructor);
+
+int sbus_default_connection_destructor(void *ctx);
+
+DBusConnection *sbus_get_connection(struct sbus_connection *conn);
+void sbus_disconnect(struct sbus_connection *conn);
+void sbus_conn_set_private_data(struct sbus_connection *conn, void *pvt_data);
+void *sbus_conn_get_private_data(struct sbus_connection *conn);
+int sbus_conn_add_interface(struct sbus_connection *conn,
+ struct sbus_interface *intf);
+bool sbus_conn_disconnecting(struct sbus_connection *conn);
+
+/* max_retries < 0: retry forever
+ * max_retries = 0: never retry (why are you calling this function?)
+ * max_retries > 0: obvious
+ */
+void sbus_reconnect_init(struct sbus_connection *conn,
+ int max_retries,
+ sbus_conn_reconn_callback_fn callback,
+ void *pvt);
+
+/* Default message handler
+ * Should be usable for most cases */
+DBusHandlerResult sbus_message_handler(DBusConnection *conn,
+ DBusMessage *message,
+ void *user_data);
+
+void sbus_conn_send_reply(struct sbus_connection *conn,
+ DBusMessage *reply);
+
+int sbus_is_dbus_fixed_type(int dbus_type);
+int sbus_is_dbus_string_type(int dbus_type);
+size_t sbus_get_dbus_type_size(int dbus_type);
+#endif /* _SSSD_DBUS_H_*/
diff --git a/src/sbus/sssd_dbus_common.c b/src/sbus/sssd_dbus_common.c
new file mode 100644
index 000000000..d446632d2
--- /dev/null
+++ b/src/sbus/sssd_dbus_common.c
@@ -0,0 +1,444 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/time.h>
+#include "tevent.h"
+#include "dbus/dbus.h"
+#include "util/util.h"
+#include "sbus/sssd_dbus.h"
+#include "sbus/sssd_dbus_private.h"
+
+/* =Watches=============================================================== */
+
+/* DBUS may ask us to add a watch to a file descriptor that already had a watch
+ * associated. Need to check if that's the case */
+static struct sbus_watch_ctx *fd_to_watch(struct sbus_watch_ctx *list, int fd)
+{
+ struct sbus_watch_ctx *watch_iter;
+
+ watch_iter = list;
+ while (watch_iter != NULL) {
+ if (watch_iter->fd == fd) {
+ return watch_iter;
+ }
+
+ watch_iter = watch_iter->next;
+ }
+
+ return NULL;
+}
+
+static int watch_destructor(void *mem)
+{
+ struct sbus_watch_ctx *watch;
+
+ watch = talloc_get_type(mem, struct sbus_watch_ctx);
+ DLIST_REMOVE(watch->conn->watch_list, watch);
+
+ return 0;
+}
+
+/*
+ * watch_handler
+ * Callback for D-BUS to handle messages on a file-descriptor
+ */
+static void sbus_watch_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *data)
+{
+ struct sbus_watch_ctx *watch = talloc_get_type(data,
+ struct sbus_watch_ctx);
+ enum dbus_conn_type type;
+ union dbus_conn_pointer dbus_p;
+
+ /* conn may get freed inside a handle, save the data we need for later */
+ type = watch->conn->type;
+ dbus_p = watch->conn->dbus;
+
+ /* Take a reference while handling watch */
+ if (type == SBUS_SERVER) {
+ dbus_server_ref(dbus_p.server);
+ } else {
+ dbus_connection_ref(dbus_p.conn);
+ }
+
+ /* Fire if readable */
+ if (flags & TEVENT_FD_READ) {
+ if (watch->dbus_read_watch) {
+ dbus_watch_handle(watch->dbus_read_watch, DBUS_WATCH_READABLE);
+ }
+ }
+
+ /* Fire if writeable */
+ if (flags & TEVENT_FD_WRITE) {
+ if (watch->dbus_write_watch) {
+ dbus_watch_handle(watch->dbus_write_watch, DBUS_WATCH_WRITABLE);
+ }
+ }
+
+ /* Release reference once done */
+ if (type == SBUS_SERVER) {
+ dbus_server_unref(dbus_p.server);
+ } else {
+ dbus_connection_unref(dbus_p.conn);
+ }
+}
+
+/*
+ * add_watch
+ * Set up hooks into the libevents mainloop for
+ * D-BUS to add file descriptor-based events
+ */
+dbus_bool_t sbus_add_watch(DBusWatch *dbus_watch, void *data)
+{
+ unsigned int flags;
+ uint16_t event_flags;
+ struct sbus_connection *conn;
+ struct sbus_watch_ctx *watch;
+ dbus_bool_t enabled;
+ int fd;
+
+ conn = talloc_get_type(data, struct sbus_connection);
+
+#ifdef HAVE_DBUS_WATCH_GET_UNIX_FD
+ fd = dbus_watch_get_unix_fd(dbus_watch);
+#else
+ fd = dbus_watch_get_fd(dbus_watch);
+#endif
+
+ watch = fd_to_watch(conn->watch_list, fd);
+ if (!watch) {
+ /* does not exist, allocate new one */
+ watch = talloc_zero(conn, struct sbus_watch_ctx);
+ if (!watch) {
+ DEBUG(0, ("Out of Memory!\n"));
+ return FALSE;
+ }
+ watch->conn = conn;
+ watch->fd = fd;
+ }
+
+ enabled = dbus_watch_get_enabled(dbus_watch);
+ flags = dbus_watch_get_flags(dbus_watch);
+
+ /* Save the event to the watch object so it can be found later */
+ if (flags & DBUS_WATCH_READABLE) {
+ watch->dbus_read_watch = dbus_watch;
+ }
+ if (flags & DBUS_WATCH_WRITABLE) {
+ watch->dbus_write_watch = dbus_watch;
+ }
+ dbus_watch_set_data(dbus_watch, watch, NULL);
+
+ if (watch->fde) {
+ /* pre-existing event, just toggle flags */
+ sbus_toggle_watch(dbus_watch, data);
+ return TRUE;
+ }
+
+ event_flags = 0;
+ if (enabled) {
+ if (flags & DBUS_WATCH_READABLE) {
+ event_flags |= TEVENT_FD_READ;
+ }
+ if (flags & DBUS_WATCH_WRITABLE) {
+ event_flags |= TEVENT_FD_WRITE;
+ }
+ }
+
+ /* Add the file descriptor to the event loop */
+ watch->fde = tevent_add_fd(conn->ev,
+ watch, fd, event_flags,
+ sbus_watch_handler, watch);
+ if (!watch->fde) {
+ DEBUG(0, ("Failed to set up fd event!\n"));
+ talloc_zfree(watch);
+ return FALSE;
+ }
+
+ DLIST_ADD(conn->watch_list, watch);
+ talloc_set_destructor((TALLOC_CTX *)watch, watch_destructor);
+
+ DEBUG(8, ("%p/%p (%d), %s/%s (%s)\n",
+ watch, dbus_watch, fd,
+ ((flags & DBUS_WATCH_READABLE)?"R":"-"),
+ ((flags & DBUS_WATCH_WRITABLE)?"W":"-"),
+ enabled?"enabled":"disabled"));
+
+ return TRUE;
+}
+
+/*
+ * toggle_watch
+ * Hook for D-BUS to toggle the enabled/disabled state of
+ * an event in the mainloop
+ */
+void sbus_toggle_watch(DBusWatch *dbus_watch, void *data)
+{
+ struct sbus_watch_ctx *watch;
+ unsigned int flags;
+ dbus_bool_t enabled;
+ void *watch_data;
+ int fd;
+
+ enabled = dbus_watch_get_enabled(dbus_watch);
+ flags = dbus_watch_get_flags(dbus_watch);
+
+ watch_data = dbus_watch_get_data(dbus_watch);
+ watch = talloc_get_type(watch_data, struct sbus_watch_ctx);
+ if (!watch) {
+ DEBUG(2, ("[%p] does not carry watch context?!\n", dbus_watch));
+ /* abort ? */
+ return;
+ }
+
+ if (enabled) {
+ if (flags & DBUS_WATCH_READABLE) {
+ TEVENT_FD_READABLE(watch->fde);
+ }
+ if (flags & DBUS_WATCH_WRITABLE) {
+ TEVENT_FD_WRITEABLE(watch->fde);
+ }
+ } else {
+ if (flags & DBUS_WATCH_READABLE) {
+ TEVENT_FD_NOT_READABLE(watch->fde);
+ }
+ if (flags & DBUS_WATCH_WRITABLE) {
+ TEVENT_FD_NOT_WRITEABLE(watch->fde);
+ }
+ }
+
+ if (debug_level >= 8) {
+#ifdef HAVE_DBUS_WATCH_GET_UNIX_FD
+ fd = dbus_watch_get_unix_fd(dbus_watch);
+#else
+ fd = dbus_watch_get_fd(dbus_watch);
+#endif
+ }
+ DEBUG(8, ("%p/%p (%d), %s/%s (%s)\n",
+ watch, dbus_watch, fd,
+ ((flags & DBUS_WATCH_READABLE)?"R":"-"),
+ ((flags & DBUS_WATCH_WRITABLE)?"W":"-"),
+ enabled?"enabled":"disabled"));
+}
+
+/*
+ * sbus_remove_watch
+ * Hook for D-BUS to remove file descriptor-based events
+ * from the libevents mainloop
+ */
+void sbus_remove_watch(DBusWatch *dbus_watch, void *data)
+{
+ struct sbus_watch_ctx *watch;
+ void *watch_data;
+
+ watch_data = dbus_watch_get_data(dbus_watch);
+ watch = talloc_get_type(watch_data, struct sbus_watch_ctx);
+
+ DEBUG(8, ("%p/%p\n", watch, dbus_watch));
+
+ if (!watch) {
+ DEBUG(2, ("DBUS trying to remove unknown watch!\n"));
+ return;
+ }
+
+ /* remove dbus watch data */
+ dbus_watch_set_data(dbus_watch, NULL, NULL);
+
+ /* check which watch to remove, or free if none left */
+ if (watch->dbus_read_watch == dbus_watch) {
+ watch->dbus_read_watch = NULL;
+ }
+ if (watch->dbus_write_watch == dbus_watch) {
+ watch->dbus_write_watch = NULL;
+ }
+ if (!watch->dbus_read_watch && !watch->dbus_write_watch) {
+ talloc_free(watch);
+ }
+}
+
+/* =Timeouts============================================================== */
+
+static struct timeval _get_interval_tv(int interval) {
+ struct timeval tv;
+ struct timeval rightnow;
+
+ gettimeofday(&rightnow,NULL);
+
+ tv.tv_sec = interval / 1000 + rightnow.tv_sec;
+ tv.tv_usec = (interval % 1000) * 1000 + rightnow.tv_usec;
+ return tv;
+}
+
+/*
+ * timeout_handler
+ * Callback for D-BUS to handle timed events
+ */
+static void sbus_timeout_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t, void *data)
+{
+ struct sbus_timeout_ctx *timeout;
+ timeout = talloc_get_type(data, struct sbus_timeout_ctx);
+
+ dbus_timeout_handle(timeout->dbus_timeout);
+}
+
+/*
+ * add_timeout
+ * Hook for D-BUS to add time-based events to the mainloop
+ */
+dbus_bool_t sbus_add_timeout(DBusTimeout *dbus_timeout, void *data)
+{
+ struct sbus_connection *conn;
+ struct sbus_timeout_ctx *timeout;
+ struct timeval tv;
+
+ DEBUG(8, ("%p\n", dbus_timeout));
+
+ if (!dbus_timeout_get_enabled(dbus_timeout)) {
+ return TRUE;
+ }
+
+ conn = talloc_get_type(data, struct sbus_connection);
+
+ timeout = talloc_zero(conn, struct sbus_timeout_ctx);
+ if (!timeout) {
+ DEBUG(0, ("Out of Memory!\n"));
+ return FALSE;
+ }
+ timeout->dbus_timeout = dbus_timeout;
+
+ tv = _get_interval_tv(dbus_timeout_get_interval(dbus_timeout));
+ timeout->te = tevent_add_timer(conn->ev, timeout, tv,
+ sbus_timeout_handler, timeout);
+ if (!timeout->te) {
+ DEBUG(0, ("Failed to set up timeout event!\n"));
+ return FALSE;
+ }
+
+ /* Save the event to the watch object so it can be removed later */
+ dbus_timeout_set_data(timeout->dbus_timeout, timeout, NULL);
+
+ return TRUE;
+}
+
+/*
+ * sbus_toggle_timeout
+ * Hook for D-BUS to toggle the enabled/disabled state of a mainloop
+ * event
+ */
+void sbus_toggle_timeout(DBusTimeout *dbus_timeout, void *data)
+{
+ DEBUG(8, ("%p\n", dbus_timeout));
+
+ if (dbus_timeout_get_enabled(dbus_timeout)) {
+ sbus_add_timeout(dbus_timeout, data);
+ } else {
+ sbus_remove_timeout(dbus_timeout, data);
+ }
+}
+
+/*
+ * sbus_remove_timeout
+ * Hook for D-BUS to remove time-based events from the mainloop
+ */
+void sbus_remove_timeout(DBusTimeout *dbus_timeout, void *data)
+{
+ void *timeout;
+
+ DEBUG(8, ("%p\n", dbus_timeout));
+
+ timeout = dbus_timeout_get_data(dbus_timeout);
+
+ /* remove dbus timeout data */
+ dbus_timeout_set_data(dbus_timeout, NULL, NULL);
+
+ /* Freeing the event object will remove it from the event loop */
+ talloc_free(timeout);
+
+}
+
+/* =Helpers=============================================================== */
+
+int sbus_is_dbus_fixed_type(int dbus_type)
+{
+ switch (dbus_type) {
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ return true;
+ }
+ return false;
+}
+
+int sbus_is_dbus_string_type(int dbus_type)
+{
+ switch(dbus_type) {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ return true;
+ }
+ return false;
+}
+
+size_t sbus_get_dbus_type_size(int dbus_type)
+{
+ size_t ret;
+
+ switch(dbus_type) {
+ /* 1-byte types */
+ case DBUS_TYPE_BYTE:
+ ret = 1;
+ break;
+
+ /* 2-byte types */
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ ret = 2;
+ break;
+
+ /* 4-byte types */
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ ret = 4;
+ break;
+
+ /* 8-byte types */
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ ret = 8;
+ break;
+
+ default:
+ ret = 0;
+ }
+ return ret;
+}
diff --git a/src/sbus/sssd_dbus_connection.c b/src/sbus/sssd_dbus_connection.c
new file mode 100644
index 000000000..38ccc6ab1
--- /dev/null
+++ b/src/sbus/sssd_dbus_connection.c
@@ -0,0 +1,692 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/time.h>
+#include "util/util.h"
+#include "dbus/dbus.h"
+#include "sbus/sssd_dbus.h"
+#include "sbus/sssd_dbus_private.h"
+
+/* Types */
+struct dbus_ctx_list;
+
+struct sbus_interface_p {
+ struct sbus_interface_p *prev, *next;
+ struct sbus_connection *conn;
+ struct sbus_interface *intf;
+};
+
+static bool path_in_interface_list(struct sbus_interface_p *list,
+ const char *path);
+static void sbus_unreg_object_paths(struct sbus_connection *conn);
+
+static int sbus_auto_reconnect(struct sbus_connection *conn);
+
+static void sbus_dispatch(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *data)
+{
+ struct tevent_timer *new_event;
+ struct sbus_connection *conn;
+ DBusConnection *dbus_conn;
+ int ret;
+
+ if (data == NULL) return;
+
+ conn = talloc_get_type(data, struct sbus_connection);
+
+ dbus_conn = conn->dbus.conn;
+ DEBUG(9, ("dbus conn: %lX\n", dbus_conn));
+
+ if (conn->retries > 0) {
+ DEBUG(6, ("SBUS is reconnecting. Deferring.\n"));
+ /* Currently trying to reconnect, defer dispatch */
+ new_event = tevent_add_timer(ev, conn, tv, sbus_dispatch, conn);
+ if (new_event == NULL) {
+ DEBUG(0,("Could not defer dispatch!\n"));
+ }
+ return;
+ }
+
+ if ((!dbus_connection_get_is_connected(dbus_conn)) &&
+ (conn->max_retries != 0)) {
+ /* Attempt to reconnect automatically */
+ ret = sbus_auto_reconnect(conn);
+ if (ret == EOK) {
+ DEBUG(1, ("Performing auto-reconnect\n"));
+ return;
+ }
+
+ DEBUG(0, ("Cannot start auto-reconnection.\n"));
+ conn->reconnect_callback(conn,
+ SBUS_RECONNECT_ERROR,
+ conn->reconnect_pvt);
+ return;
+ }
+
+ if ((conn->disconnect) ||
+ (!dbus_connection_get_is_connected(dbus_conn))) {
+ DEBUG(3,("Connection is not open for dispatching.\n"));
+ /*
+ * Free the connection object.
+ * This will invoke the destructor for the connection
+ */
+ talloc_free(conn);
+ conn = NULL;
+ return;
+ }
+
+ /* Dispatch only once each time through the mainloop to avoid
+ * starving other features
+ */
+ ret = dbus_connection_get_dispatch_status(dbus_conn);
+ if (ret != DBUS_DISPATCH_COMPLETE) {
+ DEBUG(9,("Dispatching.\n"));
+ dbus_connection_dispatch(dbus_conn);
+ }
+
+ /* If other dispatches are waiting, queue up the dispatch function
+ * for the next loop.
+ */
+ ret = dbus_connection_get_dispatch_status(dbus_conn);
+ if (ret != DBUS_DISPATCH_COMPLETE) {
+ new_event = tevent_add_timer(ev, conn, tv, sbus_dispatch, conn);
+ if (new_event == NULL) {
+ DEBUG(2,("Could not add dispatch event!\n"));
+
+ /* TODO: Calling exit here is bad */
+ exit(1);
+ }
+ }
+}
+
+/* dbus_connection_wakeup_main
+ * D-BUS makes a callback to the wakeup_main function when
+ * it has data available for dispatching.
+ * In order to avoid blocking, this function will create a now()
+ * timed event to perform the dispatch during the next iteration
+ * through the mainloop
+ */
+static void sbus_conn_wakeup_main(void *data)
+{
+ 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(2,("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,
+ struct sbus_interface *intf,
+ int connection_type,
+ struct sbus_connection **_conn)
+{
+ struct sbus_connection *conn;
+ int ret;
+
+ DEBUG(5,("Adding connection %lX\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 = sbus_conn_add_interface(conn, intf);
+ if (ret != EOK) {
+ talloc_free(conn);
+ return ret;
+ }
+
+ 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 the default destructor
+ * Connections can override this with
+ * sbus_conn_set_destructor
+ */
+ sbus_conn_set_destructor(conn, NULL);
+
+ /* 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(2,("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(2,("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_interface *intf,
+ 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(1, ("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, intf,
+ 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;
+}
+
+/*
+ * sbus_conn_set_destructor
+ * Configures a callback to clean up this connection when it
+ * is finalized.
+ * @param conn The sbus_connection created
+ * when this connection was established
+ * @param destructor The destructor function that should be
+ * called when the connection is finalized. If passed NULL,
+ * this will reset the connection to the default destructor.
+ */
+void sbus_conn_set_destructor(struct sbus_connection *conn,
+ sbus_conn_destructor_fn destructor)
+{
+ if (!conn) return;
+
+ conn->destructor = destructor;
+ /* TODO: Should we try to handle the talloc_destructor too? */
+}
+
+int sbus_default_connection_destructor(void *ctx)
+{
+ struct sbus_connection *conn;
+ conn = talloc_get_type(ctx, struct sbus_connection);
+
+ DEBUG(5, ("Invoking default destructor on connection %lX\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) {
+ /* Shared connections are destroyed when their last reference is removed */
+ }
+ else {
+ /* Critical Error! */
+ DEBUG(1,("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(5,("Disconnecting %lX\n", conn->dbus.conn));
+
+ /*******************************
+ * Referencing conn->dbus.conn */
+ dbus_connection_ref(conn->dbus.conn);
+
+ conn->disconnect = 1;
+
+ /* Invoke the custom destructor, if it exists */
+ if (conn->destructor) {
+ conn->destructor(conn);
+ }
+
+ /* 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 */
+ sbus_default_connection_destructor(conn);
+
+ dbus_connection_unref(conn->dbus.conn);
+ /* Unreferenced conn->dbus_conn *
+ ******************************/
+
+ DEBUG(5,("Disconnected %lX\n", conn->dbus.conn));
+}
+
+static int sbus_reply_internal_error(DBusMessage *message,
+ struct sbus_connection *conn) {
+ DBusMessage *reply = dbus_message_new_error(message, DBUS_ERROR_IO_ERROR,
+ "Internal Error");
+ if (reply) {
+ sbus_conn_send_reply(conn, reply);
+ dbus_message_unref(reply);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/* 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 *method;
+ const char *path;
+ const char *msg_interface;
+ DBusMessage *reply = NULL;
+ int i, ret;
+ int found;
+
+ if (!user_data) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ intf_p = talloc_get_type(user_data, struct sbus_interface_p);
+
+ method = dbus_message_get_member(message);
+ DEBUG(9, ("Received SBUS method [%s]\n", method));
+ path = dbus_message_get_path(message);
+ msg_interface = dbus_message_get_interface(message);
+
+ if (!method || !path || !msg_interface)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ /* Validate the D-BUS path */
+ if (strcmp(path, intf_p->intf->path) != 0)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ /* Validate the method interface */
+ if (strcmp(msg_interface, intf_p->intf->interface) == 0) {
+ found = 0;
+ for (i = 0; intf_p->intf->methods[i].method != NULL; i++) {
+ if (strcmp(method, intf_p->intf->methods[i].method) == 0) {
+ found = 1;
+ ret = intf_p->intf->methods[i].fn(message, intf_p->conn);
+ if (ret != EOK) {
+ return sbus_reply_internal_error(message, intf_p->conn);
+ }
+ break;
+ }
+ }
+
+ if (!found) {
+ /* Reply DBUS_ERROR_UNKNOWN_METHOD */
+ DEBUG(1, ("No matching method found for %s.\n", method));
+ reply = dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD, NULL);
+ sbus_conn_send_reply(intf_p->conn, reply);
+ dbus_message_unref(reply);
+ }
+ }
+ else {
+ /* Special case: check for Introspection request
+ * This is usually only useful for system bus connections
+ */
+ if (strcmp(msg_interface, DBUS_INTROSPECT_INTERFACE) == 0 &&
+ strcmp(method, DBUS_INTROSPECT_METHOD) == 0)
+ {
+ if (intf_p->intf->introspect_fn) {
+ /* If we have been asked for introspection data and we have
+ * an introspection function registered, user that.
+ */
+ ret = intf_p->intf->introspect_fn(message, intf_p->conn);
+ if (ret != EOK) {
+ return sbus_reply_internal_error(message, intf_p->conn);
+ }
+ }
+ }
+ else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/* Adds a new D-BUS path message handler to the connection
+ * Note: this must be a unique path.
+ */
+int sbus_conn_add_interface(struct sbus_connection *conn,
+ struct sbus_interface *intf)
+{
+ struct sbus_interface_p *intf_p;
+ dbus_bool_t dbret;
+ const char *path;
+
+ if (!conn || !intf || !intf->vtable.message_function) {
+ return EINVAL;
+ }
+
+ path = intf->path;
+
+ if (path_in_interface_list(conn->intf_list, path)) {
+ DEBUG(0, ("Cannot add method context with identical path.\n"));
+ return EINVAL;
+ }
+
+ intf_p = talloc_zero(conn, struct sbus_interface_p);
+ if (!intf_p) {
+ return ENOMEM;
+ }
+ intf_p->conn = conn;
+ intf_p->intf = intf;
+
+ DLIST_ADD(conn->intf_list, intf_p);
+
+ dbret = dbus_connection_register_object_path(conn->dbus.conn,
+ path, &intf->vtable, intf_p);
+ if (!dbret) {
+ DEBUG(0, ("Could not register object path to the connection.\n"));
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static bool path_in_interface_list(struct sbus_interface_p *list,
+ const char *path)
+{
+ struct sbus_interface_p *iter;
+
+ if (!list || !path) {
+ return false;
+ }
+
+ iter = list;
+ while (iter != NULL) {
+ if (strcmp(iter->intf->path, path) == 0) {
+ return true;
+ }
+ iter = iter->next;
+ }
+
+ return false;
+}
+
+static void sbus_unreg_object_paths(struct sbus_connection *conn)
+{
+ struct sbus_interface_p *iter = conn->intf_list;
+
+ while (iter != NULL) {
+ dbus_connection_unregister_object_path(conn->dbus.conn,
+ iter->intf->path);
+ iter = iter->next;
+ }
+}
+
+void sbus_conn_set_private_data(struct sbus_connection *conn, void *pvt_data)
+{
+ conn->pvt_data = pvt_data;
+}
+
+void *sbus_conn_get_private_data(struct sbus_connection *conn)
+{
+ return conn->pvt_data;
+}
+
+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(3, ("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(3, ("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,
+ &iter->intf->vtable,
+ iter);
+ if (!dbret) {
+ DEBUG(0, ("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(1, ("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;
+}
+
+void sbus_conn_send_reply(struct sbus_connection *conn, DBusMessage *reply)
+{
+ dbus_connection_send(conn->dbus.conn, reply, NULL);
+}
+
diff --git a/src/sbus/sssd_dbus_private.h b/src/sbus/sssd_dbus_private.h
new file mode 100644
index 000000000..78a2fe5c1
--- /dev/null
+++ b/src/sbus/sssd_dbus_private.h
@@ -0,0 +1,98 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSSD_DBUS_PRIVATE_H_
+#define _SSSD_DBUS_PRIVATE_H_
+
+union dbus_conn_pointer {
+ DBusServer *server;
+ DBusConnection *conn;
+};
+enum dbus_conn_type {
+ SBUS_SERVER,
+ SBUS_CONNECTION
+};
+
+struct sbus_interface_p;
+struct sbus_watch_ctx;
+
+struct sbus_connection {
+ struct tevent_context *ev;
+
+ enum dbus_conn_type type;
+ union dbus_conn_pointer dbus;
+
+ char *address;
+ int connection_type;
+ int disconnect;
+
+ sbus_conn_destructor_fn destructor;
+ void *pvt_data; /* Private data for this connection */
+
+ /* dbus tables and handlers */
+ struct sbus_interface_p *intf_list;
+
+ /* reconnect settings */
+ int retries;
+ int max_retries;
+ sbus_conn_reconn_callback_fn reconnect_callback;
+ /* Private data needed to reinit after reconnection */
+ void *reconnect_pvt;
+
+ /* server related stuff */
+ struct sbus_interface *server_intf;
+ sbus_server_conn_init_fn srv_init_fn;
+ void *srv_init_data;
+
+ /* watches list */
+ struct sbus_watch_ctx *watch_list;
+};
+
+/* =Watches=============================================================== */
+
+struct sbus_watch_ctx {
+ struct sbus_watch_ctx *prev, *next;
+
+ struct sbus_connection *conn;
+
+ struct tevent_fd *fde;
+ int fd;
+
+ DBusWatch *dbus_read_watch;
+ DBusWatch *dbus_write_watch;
+};
+
+dbus_bool_t sbus_add_watch(DBusWatch *watch, void *data);
+void sbus_toggle_watch(DBusWatch *watch, void *data);
+void sbus_remove_watch(DBusWatch *watch, void *data);
+
+/* =Timeouts============================================================== */
+
+struct sbus_timeout_ctx {
+ DBusTimeout *dbus_timeout;
+ struct tevent_timer *te;
+};
+
+dbus_bool_t sbus_add_timeout(DBusTimeout *dbus_timeout, void *data);
+void sbus_toggle_timeout(DBusTimeout *dbus_timeout, void *data);
+void sbus_remove_timeout(DBusTimeout *dbus_timeout, void *data);
+
+#endif /* _SSSD_DBUS_PRIVATE_H_ */
diff --git a/src/sbus/sssd_dbus_server.c b/src/sbus/sssd_dbus_server.c
new file mode 100644
index 000000000..a859cbabc
--- /dev/null
+++ b/src/sbus/sssd_dbus_server.c
@@ -0,0 +1,171 @@
+/*
+ SSSD
+
+ Service monitor - D-BUS features
+
+ Copyright (C) Stephen Gallagher 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <sys/time.h>
+#include "tevent.h"
+#include "util/util.h"
+#include "dbus/dbus.h"
+#include "sbus/sssd_dbus.h"
+#include "sbus/sssd_dbus_private.h"
+
+static int sbus_server_destructor(void *ctx);
+
+/*
+ * new_connection_callback
+ * Actions to be run upon each new client connection
+ * Must either perform dbus_connection_ref() on the
+ * new connection or else close the connection with
+ * dbus_connection_close()
+ */
+static void sbus_server_init_new_connection(DBusServer *dbus_server,
+ DBusConnection *dbus_conn,
+ void *data)
+{
+ struct sbus_connection *server;
+ struct sbus_connection *conn;
+ int ret;
+
+ DEBUG(5,("Entering.\n"));
+ server = talloc_get_type(data, struct sbus_connection);
+ if (!server) {
+ return;
+ }
+
+ DEBUG(5,("Adding connection %p.\n", dbus_conn));
+ ret = sbus_init_connection(server, server->ev,
+ dbus_conn, server->server_intf,
+ SBUS_CONN_TYPE_PRIVATE, &conn);
+ if (ret != 0) {
+ dbus_connection_close(dbus_conn);
+ DEBUG(5,("Closing connection (failed setup)"));
+ return;
+ }
+
+ dbus_connection_ref(dbus_conn);
+
+ DEBUG(5,("Got a connection\n"));
+
+ /*
+ * Initialize connection-specific features
+ * This may set a more detailed destructor, but
+ * the default destructor will always be chained
+ * to handle connection cleanup.
+ * This function (or its callbacks) should also
+ * set up connection-specific methods.
+ */
+ ret = server->srv_init_fn(conn, server->srv_init_data);
+ if (ret != EOK) {
+ DEBUG(1,("Initialization failed!\n"));
+ dbus_connection_close(dbus_conn);
+ talloc_zfree(conn);
+ }
+}
+
+/*
+ * dbus_new_server
+ * Set up a D-BUS server, integrate with the event loop
+ * for handling file descriptor and timed events
+ */
+int sbus_new_server(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *address,
+ struct sbus_interface *intf,
+ struct sbus_connection **_server,
+ sbus_server_conn_init_fn init_fn, void *init_pvt_data)
+{
+ struct sbus_connection *server;
+ DBusServer *dbus_server;
+ DBusError dbus_error;
+ dbus_bool_t dbret;
+ char *tmp;
+
+ *_server = NULL;
+
+ /* Set up D-BUS server */
+ dbus_error_init(&dbus_error);
+ dbus_server = dbus_server_listen(address, &dbus_error);
+ if (!dbus_server) {
+ DEBUG(1,("dbus_server_listen failed! (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;
+ }
+
+ tmp = dbus_server_get_address(dbus_server);
+ DEBUG(3, ("D-BUS Server listening on %s\n", tmp));
+ free(tmp);
+
+ server = talloc_zero(mem_ctx, struct sbus_connection);
+ if (!server) {
+ return ENOMEM;
+ }
+
+ server->ev = ev;
+ server->type = SBUS_SERVER;
+ server->dbus.server = dbus_server;
+ server->server_intf = intf;
+ server->srv_init_fn = init_fn;
+ server->srv_init_data = init_pvt_data;
+
+ talloc_set_destructor((TALLOC_CTX *)server, sbus_server_destructor);
+
+ /* Set up D-BUS new connection handler */
+ dbus_server_set_new_connection_function(server->dbus.server,
+ sbus_server_init_new_connection,
+ server, NULL);
+
+ /* Set up DBusWatch functions */
+ dbret = dbus_server_set_watch_functions(server->dbus.server,
+ sbus_add_watch,
+ sbus_remove_watch,
+ sbus_toggle_watch,
+ server, NULL);
+ if (!dbret) {
+ DEBUG(4, ("Error setting up D-BUS server watch functions"));
+ talloc_free(server);
+ return EIO;
+ }
+
+ /* Set up DBusTimeout functions */
+ dbret = dbus_server_set_timeout_functions(server->dbus.server,
+ sbus_add_timeout,
+ sbus_remove_timeout,
+ sbus_toggle_timeout,
+ server, NULL);
+ if (!dbret) {
+ DEBUG(4,("Error setting up D-BUS server timeout functions"));
+ dbus_server_set_watch_functions(server->dbus.server,
+ NULL, NULL, NULL, NULL, NULL);
+ talloc_free(server);
+ return EIO;
+ }
+
+ *_server = server;
+ return EOK;
+}
+
+static int sbus_server_destructor(void *ctx)
+{
+ struct sbus_connection *server;
+
+ server = talloc_get_type(ctx, struct sbus_connection);
+ dbus_server_disconnect(server->dbus.server);
+ return 0;
+}