summaryrefslogtreecommitdiffstats
path: root/server/sbus
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2008-11-03 09:50:19 -0500
committerSimo Sorce <idra@samba.org>2008-11-03 10:12:29 -0500
commit73f7183c9d244e7cdbe5c7ec91677b64a5f08e8e (patch)
tree5c54276a07dd0e15f6438d9019d9e03619aa7a0f /server/sbus
parent8b8210c79b297b87e56d068e8839f7fe6755ab15 (diff)
downloadsssd-73f7183c9d244e7cdbe5c7ec91677b64a5f08e8e.tar.gz
sssd-73f7183c9d244e7cdbe5c7ec91677b64a5f08e8e.tar.xz
sssd-73f7183c9d244e7cdbe5c7ec91677b64a5f08e8e.zip
Renaming sssd/server/dbus to sssd/server/sbus. Making necessary changes to header includes and makefiles.
Diffstat (limited to 'server/sbus')
-rw-r--r--server/sbus/sssd_dbus.h86
-rw-r--r--server/sbus/sssd_dbus_common.c45
-rw-r--r--server/sbus/sssd_dbus_connection.c576
-rw-r--r--server/sbus/sssd_dbus_private.h14
-rw-r--r--server/sbus/sssd_dbus_server.c316
-rw-r--r--server/sbus/tests/test_client.c223
6 files changed, 1260 insertions, 0 deletions
diff --git a/server/sbus/sssd_dbus.h b/server/sbus/sssd_dbus.h
new file mode 100644
index 000000000..1bd062d42
--- /dev/null
+++ b/server/sbus/sssd_dbus.h
@@ -0,0 +1,86 @@
+/*
+ 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_conn_ctx;
+typedef int (*sbus_msg_handler_fn)(DBusMessage *msg, void *data,
+ DBusMessage **reply);
+
+/*
+ * sbus_conn_destructor_fn
+ * Function to be called when a connection is finalized
+ */
+typedef int (*sbus_conn_destructor_fn)(
+ void *ctx);
+
+/*
+ * 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_conn_ctx *dct_ctx);
+
+enum {
+ SBUS_CONN_TYPE_PRIVATE = 1,
+ SBUS_CONN_TYPE_SHARED
+};
+
+struct sbus_method {
+ const char *method;
+ sbus_msg_handler_fn fn;
+};
+
+struct sbus_method_ctx {
+ struct sbus_method_ctx *prev, *next;
+ /*struct event_context *ev;*/
+ char *interface;
+ char *path;
+
+ /* If a non-default message_handler is desired, set it in this
+ * object before calling sbus_conn_add_method_ctx()
+ * Otherwise it will default to message_handler() in
+ * sssd_dbus_connection.c
+ */
+ DBusObjectPathMessageFunction message_handler;
+ struct sbus_method *methods;
+};
+
+/* Server Functions */
+int sbus_new_server(struct event_context *ev, struct sbus_method_ctx *ctx, const char *address, sbus_server_conn_init_fn init_fn);
+
+/* Connection Functions */
+int sbus_new_connection(TALLOC_CTX *ctx, struct event_context *ev, const char *address,
+ struct sbus_conn_ctx **dct_ctx,
+ sbus_conn_destructor_fn destructor);
+
+void sbus_conn_set_destructor(struct sbus_conn_ctx *dct_ctx,
+ sbus_conn_destructor_fn destructor);
+int sbus_default_connection_destructor(void *ctx);
+
+DBusConnection *sbus_get_connection(struct sbus_conn_ctx *dct_ctx);
+void sbus_disconnect (struct sbus_conn_ctx *dct_ctx);
+void sbus_conn_set_private_data(struct sbus_conn_ctx *dct_ctx, void *private);
+int sbus_conn_add_method_ctx(struct sbus_conn_ctx *dct_ctx, struct sbus_method_ctx *method_ctx);
+
+#endif /* _SSSD_DBUS_H_*/
diff --git a/server/sbus/sssd_dbus_common.c b/server/sbus/sssd_dbus_common.c
new file mode 100644
index 000000000..0ea66ccb3
--- /dev/null
+++ b/server/sbus/sssd_dbus_common.c
@@ -0,0 +1,45 @@
+#include <sys/time.h>
+#include "events.h"
+#include "dbus/dbus.h"
+#include "util/util.h"
+
+struct timeval _dbus_timeout_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;
+}
+
+/*
+ * sbus_remove_watch
+ * Hook for D-BUS to remove file descriptor-based events
+ * from the libevents mainloop
+ */
+void sbus_remove_watch(DBusWatch *watch, void *data) {
+ struct fd_event *fde;
+
+ DEBUG(2, ("%lX\n", watch));
+ fde = talloc_get_type(dbus_watch_get_data(watch), struct fd_event);
+
+ /* Freeing the event object will remove it from the event loop */
+ talloc_free(fde);
+ dbus_watch_set_data(watch, NULL, NULL);
+}
+
+
+/*
+ * sbus_remove_timeout
+ * Hook for D-BUS to remove time-based events from the mainloop
+ */
+void sbus_remove_timeout(DBusTimeout *timeout, void *data) {
+ struct timed_event *te;
+ te = talloc_get_type(dbus_timeout_get_data(timeout), struct timed_event);
+
+ /* Freeing the event object will remove it from the event loop */
+ talloc_free(te);
+ dbus_timeout_set_data(timeout, NULL, NULL);
+}
diff --git a/server/sbus/sssd_dbus_connection.c b/server/sbus/sssd_dbus_connection.c
new file mode 100644
index 000000000..bd95c0abe
--- /dev/null
+++ b/server/sbus/sssd_dbus_connection.c
@@ -0,0 +1,576 @@
+#include <sys/time.h>
+#include "events.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_conn_ctx {
+ DBusConnection *conn;
+ struct event_context *ev;
+ int connection_type;
+ int disconnect;
+ struct sbus_method_ctx *method_ctx_list;
+ sbus_conn_destructor_fn destructor;
+ void *private; /* Private data for this connection */
+};
+
+struct sbus_conn_watch_ctx {
+ DBusWatch *watch;
+ int fd;
+ struct fd_event *fde;
+ struct sbus_conn_ctx *top;
+};
+
+struct sbus_conn_timeout_ctx {
+ DBusTimeout *timeout;
+ struct timed_event *te;
+ struct sbus_conn_ctx *top;
+};
+
+static int _method_list_contains_path(struct sbus_method_ctx *list, struct sbus_method_ctx *method);
+static void sbus_unreg_object_paths(struct sbus_conn_ctx *dct_ctx);
+
+static void sbus_dispatch(struct event_context *ev,
+ struct timed_event *te,
+ struct timeval tv, void *data)
+{
+ struct timed_event *new_event;
+ struct sbus_conn_ctx *dct_ctx;
+ DBusConnection *conn;
+ int ret;
+
+ if (data == NULL) {
+ return;
+ }
+
+ dct_ctx = talloc_get_type(data, struct sbus_conn_ctx);
+
+ conn = dct_ctx->conn;
+ DEBUG(3, ("conn: %lX\n", conn));
+
+ if((dct_ctx->disconnect) || (!dbus_connection_get_is_connected(conn))) {
+ DEBUG(0,("Connection is not open for dispatching.\n"));
+ /*
+ * Free the connection object.
+ * This will invoke the destructor for the connection
+ */
+ talloc_free(dct_ctx);
+ dct_ctx = NULL;
+ return;
+ }
+
+ /* Dispatch only once each time through the mainloop to avoid
+ * starving other features
+ */
+ ret = dbus_connection_get_dispatch_status(conn);
+ if (ret != DBUS_DISPATCH_COMPLETE) {
+ DEBUG(2,("Dispatching.\n"));
+ dbus_connection_dispatch(conn);
+ }
+
+ /* If other dispatches are waiting, queue up the do_dispatch function
+ * for the next loop.
+ */
+ ret = dbus_connection_get_dispatch_status(conn);
+ if (ret != DBUS_DISPATCH_COMPLETE) {
+ new_event = event_add_timed(ev, dct_ctx, tv, sbus_dispatch, dct_ctx);
+ if (new_event == NULL) {
+ DEBUG(0,("Could not add dispatch event!\n"));
+
+ /* TODO: Calling exit here is bad */
+ exit(1);
+ }
+ }
+}
+
+/*
+ * dbus_connection_read_write_handler
+ * Callback for D-BUS to handle messages on a file-descriptor
+ */
+static void sbus_conn_read_write_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags, void *data)
+{
+ struct sbus_conn_watch_ctx *conn_w_ctx;
+ conn_w_ctx = talloc_get_type(data, struct sbus_conn_watch_ctx);
+
+ DEBUG(0,("Connection is open for read/write.\n"));
+ dbus_connection_ref(conn_w_ctx->top->conn);
+ if (flags & EVENT_FD_READ) {
+ dbus_watch_handle(conn_w_ctx->watch, DBUS_WATCH_READABLE);
+ }
+ if (flags & EVENT_FD_WRITE) {
+ dbus_watch_handle(conn_w_ctx->watch, DBUS_WATCH_WRITABLE);
+ }
+ dbus_connection_unref(conn_w_ctx->top->conn);
+}
+
+/*
+ * add_connection_watch
+ * Set up hooks into the libevents mainloop for
+ * D-BUS to add file descriptor-based events
+ */
+static dbus_bool_t sbus_add_conn_watch(DBusWatch *watch, void *data)
+{
+ unsigned int flags;
+ unsigned int event_flags;
+ struct sbus_conn_ctx *dt_ctx;
+ struct sbus_conn_watch_ctx *conn_w_ctx;
+
+ if (!dbus_watch_get_enabled(watch)) {
+ return TRUE;
+ }
+
+ dt_ctx = talloc_get_type(data, struct sbus_conn_ctx);
+
+ conn_w_ctx = talloc_zero(dt_ctx, struct sbus_conn_watch_ctx);
+ conn_w_ctx->top = dt_ctx;
+ conn_w_ctx->watch = watch;
+
+ flags = dbus_watch_get_flags(watch);
+ conn_w_ctx->fd = dbus_watch_get_unix_fd(watch);
+
+ event_flags = 0;
+
+ if (flags & DBUS_WATCH_READABLE)
+ event_flags |= EVENT_FD_READ;
+
+ if (flags & DBUS_WATCH_WRITABLE)
+ event_flags |= EVENT_FD_WRITE;
+
+ if (event_flags == 0)
+ return FALSE;
+
+ DEBUG(2,("%lX: %d, %d=%s\n", watch, conn_w_ctx->fd, event_flags, event_flags==EVENT_FD_READ?"READ":"WRITE"));
+
+ /* Add the file descriptor to the event loop */
+ conn_w_ctx->fde = event_add_fd(conn_w_ctx->top->ev, conn_w_ctx,
+ conn_w_ctx->fd, event_flags,
+ sbus_conn_read_write_handler,
+ conn_w_ctx);
+
+ /* Save the event to the watch object so it can be removed later */
+ dbus_watch_set_data(conn_w_ctx->watch,conn_w_ctx->fde,NULL);
+
+ return TRUE;
+}
+
+/*
+ * toggle_connection_watch
+ * Hook for D-BUS to toggle the enabled/disabled state of
+ * an event in the mainloop
+ */
+static void sbus_toggle_conn_watch(DBusWatch *watch, void *data)
+{
+ if (dbus_watch_get_enabled(watch)) {
+ sbus_add_conn_watch(watch, data);
+ } else {
+ sbus_remove_watch(watch, data);
+ }
+}
+
+/*
+ * dbus_connection_timeout_handler
+ * Callback for D-BUS to handle timed events
+ */
+static void sbus_conn_timeout_handler(struct event_context *ev,
+ struct timed_event *te,
+ struct timeval t, void *data)
+{
+ struct sbus_conn_timeout_ctx *conn_t_ctx;
+ conn_t_ctx = talloc_get_type(data, struct sbus_conn_timeout_ctx);
+
+ dbus_timeout_handle(conn_t_ctx->timeout);
+}
+
+
+/*
+ * add_connection_timeout
+ * Hook for D-BUS to add time-based events to the mainloop
+ */
+static dbus_bool_t sbus_add_conn_timeout(DBusTimeout *timeout, void *data)
+{
+ struct sbus_conn_ctx *dt_ctx;
+ struct sbus_conn_timeout_ctx *conn_t_ctx;
+ struct timeval tv;
+
+ if (!dbus_timeout_get_enabled(timeout))
+ return TRUE;
+
+ dt_ctx = talloc_get_type(data, struct sbus_conn_ctx);
+
+ conn_t_ctx = talloc_zero(dt_ctx,struct sbus_conn_timeout_ctx);
+ conn_t_ctx->top = dt_ctx;
+ conn_t_ctx->timeout = timeout;
+
+ tv = _dbus_timeout_get_interval_tv(dbus_timeout_get_interval(timeout));
+
+ struct timeval rightnow;
+ gettimeofday(&rightnow, NULL);
+
+ conn_t_ctx->te = event_add_timed(conn_t_ctx->top->ev, conn_t_ctx, tv,
+ sbus_conn_timeout_handler, conn_t_ctx);
+
+ /* Save the event to the watch object so it can be removed later */
+ dbus_timeout_set_data(conn_t_ctx->timeout,conn_t_ctx->te,NULL);
+
+ return TRUE;
+}
+
+/*
+ * sbus_toggle_conn_timeout
+ * Hook for D-BUS to toggle the enabled/disabled state of a mainloop
+ * event
+ */
+void sbus_toggle_conn_timeout(DBusTimeout *timeout, void *data)
+{
+ if (dbus_timeout_get_enabled(timeout)) {
+ sbus_add_conn_timeout(timeout, data);
+ } else {
+ sbus_remove_timeout(timeout, data);
+ }
+}
+
+/* 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_conn_ctx *dct_ctx;
+ struct timeval tv;
+ struct timed_event *te;
+
+ dct_ctx = talloc_get_type(data, struct sbus_conn_ctx);
+ gettimeofday(&tv, NULL);
+
+ /* D-BUS calls this function when it is time to do a dispatch */
+ te = event_add_timed(dct_ctx->ev, dct_ctx,
+ tv, sbus_dispatch, dct_ctx);
+ if (te == NULL) {
+ DEBUG(0,("Could not add dispatch event!\n"));
+ exit(1);
+ }
+}
+
+/*
+ * 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_add_connection(TALLOC_CTX *ctx,
+ struct event_context *ev,
+ DBusConnection *dbus_conn,
+ struct sbus_conn_ctx **dct_ctx,
+ int connection_type)
+{
+ dbus_bool_t dbret;
+ struct sbus_conn_ctx *dt_ctx;
+
+ DEBUG(0,("Adding connection %lX\n", dbus_conn));
+ dt_ctx = talloc_zero(ctx, struct sbus_conn_ctx);
+ dt_ctx->ev = ev;
+ dt_ctx->conn = dbus_conn;
+ dt_ctx->connection_type = connection_type;
+ dt_ctx->disconnect = 0;
+ /* This will be replaced on the first call to sbus_conn_add_method_ctx() */
+ dt_ctx->method_ctx_list = NULL;
+
+ /*
+ * Set the default destructor
+ * Connections can override this with
+ * sbus_conn_set_destructor
+ */
+ sbus_conn_set_destructor(dt_ctx, NULL);
+
+ /* Set up DBusWatch functions */
+ dbret = dbus_connection_set_watch_functions(dt_ctx->conn,
+ sbus_add_conn_watch,
+ sbus_remove_watch,
+ sbus_toggle_conn_watch,
+ dt_ctx, NULL);
+ if (!dbret) {
+ DEBUG(0,("Error setting up D-BUS connection watch functions\n"));
+ return EIO;
+ }
+
+ /* Set up DBusTimeout functions */
+ dbret = dbus_connection_set_timeout_functions(dt_ctx->conn,
+ sbus_add_conn_timeout,
+ sbus_remove_timeout,
+ sbus_toggle_conn_timeout,
+ dt_ctx, NULL);
+ if (!dbret) {
+ DEBUG(0,("Error setting up D-BUS server timeout functions\n"));
+ /* FIXME: free resources ? */
+ return EIO;
+ }
+
+ /* Set up dispatch handler */
+ dbus_connection_set_wakeup_main_function(dt_ctx->conn,
+ sbus_conn_wakeup_main,
+ dt_ctx, 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(dt_ctx);
+
+ /* Return the new toplevel object */
+ *dct_ctx = dt_ctx;
+
+ return EOK;
+}
+
+/*int sbus_new_connection(struct sbus_method_ctx *ctx, const char *address,
+ DBusConnection **connection,
+ sbus_conn_destructor_fn destructor)*/
+int sbus_new_connection(TALLOC_CTX *ctx, struct event_context *ev, const char *address,
+ struct sbus_conn_ctx **dct_ctx,
+ sbus_conn_destructor_fn destructor)
+{
+ 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(0, ("Failed to open connection: name=%s, message=%s\n",
+ dbus_error.name, dbus_error.message));
+ return EIO;
+ }
+
+ ret = sbus_add_connection(ctx, ev, dbus_conn, dct_ctx, SBUS_CONN_TYPE_SHARED);
+ if (ret != EOK) {
+ /* FIXME: release resources */
+ }
+
+ dbus_connection_set_exit_on_disconnect((*dct_ctx)->conn, FALSE);
+
+ /* Set connection destructor */
+ sbus_conn_set_destructor(*dct_ctx, destructor);
+
+ return ret;
+}
+
+/*
+ * sbus_conn_set_destructor
+ * Configures a callback to clean up this connection when it
+ * is finalized.
+ * @param dct_ctx The sbus_conn_ctx 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_conn_ctx *dct_ctx,
+ sbus_conn_destructor_fn destructor) {
+ if (!dct_ctx) {
+ return;
+ }
+
+ dct_ctx->destructor = destructor;
+ /* TODO: Should we try to handle the talloc_destructor too? */
+}
+
+int sbus_default_connection_destructor(void *ctx) {
+ struct sbus_conn_ctx *dct_ctx;
+ dct_ctx = talloc_get_type(ctx, struct sbus_conn_ctx);
+
+ DEBUG(3, ("Invoking default destructor on connection %lX\n", dct_ctx->conn));
+ if (dct_ctx->connection_type == SBUS_CONN_TYPE_PRIVATE) {
+ /* Private connections must be closed explicitly */
+ dbus_connection_close(dct_ctx->conn);
+ } else if (dct_ctx->connection_type == SBUS_CONN_TYPE_SHARED) {
+ /* Shared connections are destroyed when their last reference is removed */
+ }
+ else {
+ /* Critical Error! */
+ DEBUG(0,("Critical Error, connection_type is neither shared nor private!\n"));
+ return -1;
+ }
+
+ /* Remove object path */
+ /* TODO: Remove object paths */
+
+
+ dbus_connection_unref(dct_ctx->conn);
+ return 0;
+}
+
+/*
+ * sbus_get_connection
+ * Utility function to retreive the DBusConnection object
+ * from a sbus_conn_ctx
+ */
+DBusConnection *sbus_get_connection(struct sbus_conn_ctx *dct_ctx) {
+ return dct_ctx->conn;
+}
+
+void sbus_disconnect (struct sbus_conn_ctx *dct_ctx) {
+ if (dct_ctx == NULL) {
+ return;
+ }
+
+ DEBUG(2,("Disconnecting %lX\n", dct_ctx->conn));
+ dbus_connection_ref(dct_ctx->conn);
+ dct_ctx->disconnect = 1;
+
+ /* Invoke the custom destructor, if it exists */
+ if(dct_ctx->destructor) {
+ dct_ctx->destructor(dct_ctx);
+ }
+
+ /* Unregister object paths */
+ sbus_unreg_object_paths(dct_ctx);
+
+ /* Disable watch functions */
+ dbus_connection_set_watch_functions(dct_ctx->conn,
+ NULL, NULL, NULL,
+ NULL, NULL);
+ /* Disable timeout functions */
+ dbus_connection_set_timeout_functions(dct_ctx->conn,
+ NULL, NULL, NULL,
+ NULL, NULL);
+
+ /* Disable dispatch status function */
+ dbus_connection_set_dispatch_status_function(dct_ctx->conn, NULL, NULL, NULL);
+
+ /* Disable wakeup main function */
+ dbus_connection_set_wakeup_main_function(dct_ctx->conn, NULL, NULL, NULL);
+
+ /* Finalize the connection */
+ sbus_default_connection_destructor(dct_ctx);
+ dbus_connection_unref(dct_ctx->conn);
+ DEBUG(2,("Disconnected %lX\n", dct_ctx->conn));
+}
+
+/* messsage_handler
+ * Receive messages and process them
+ */
+static DBusHandlerResult message_handler(DBusConnection *conn,
+ DBusMessage *message,
+ void *user_data)
+{
+ struct sbus_method_ctx *ctx;
+ const char *method;
+ const char *path;
+ const char *msg_interface;
+ DBusMessage *reply = NULL;
+ int i, ret;
+
+ ctx = talloc_get_type(user_data, struct sbus_method_ctx);
+
+ method = dbus_message_get_member(message);
+ 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 method interface */
+ if (strcmp(msg_interface, ctx->interface) != 0)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ /* Validate the D-BUS path */
+ if (strcmp(path, ctx->path) == 0) {
+ for (i = 0; ctx->methods[i].method != NULL; i++) {
+ if (strcmp(method, ctx->methods[i].method) == 0) {
+ ret = ctx->methods[i].fn(message, ctx, &reply);
+ /* FIXME: check error */
+ break;
+ }
+ }
+ /* FIXME: check if we didn't find any matching method */
+ }
+
+ DEBUG(2, ("Method %s complete. Reply was %srequested.\n", method, reply?"":"not "));
+
+ if (reply) {
+ dbus_connection_send(conn, reply, NULL);
+ dbus_message_unref(reply);
+ }
+
+ return reply ? DBUS_HANDLER_RESULT_HANDLED :
+ DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/* Adds a new D-BUS path message handler to the connection
+ * Note: this must be a unique path.
+ */
+int sbus_conn_add_method_ctx(struct sbus_conn_ctx *dct_ctx, struct sbus_method_ctx *method_ctx) {
+ DBusObjectPathVTable *connection_vtable;
+ dbus_bool_t dbret;
+ if (!method_ctx) {
+ return EINVAL;
+ }
+
+ if (_method_list_contains_path(dct_ctx->method_ctx_list, method_ctx)) {
+ return EINVAL;
+ }
+
+ DLIST_ADD(dct_ctx->method_ctx_list, method_ctx);
+
+ /* Set up the vtable for the object path */
+ connection_vtable = talloc_zero(dct_ctx, DBusObjectPathVTable);
+ if (method_ctx->message_handler) {
+ connection_vtable->message_function = method_ctx->message_handler;
+ } else {
+ connection_vtable->message_function = message_handler;
+ }
+
+ dbret = dbus_connection_register_object_path(dct_ctx->conn, method_ctx->path, connection_vtable, method_ctx);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static int _method_list_contains_path(struct sbus_method_ctx *list, struct sbus_method_ctx *method) {
+ struct sbus_method_ctx *iter;
+
+ if (!list || !method) {
+ return 0; /* FALSE */
+ }
+
+ iter = list;
+ while (iter != NULL) {
+ if (strcmp(iter->path, method->path) == 0)
+ return 1; /* TRUE */
+
+ iter = iter->next;
+ }
+
+ return 0; /* FALSE */
+}
+
+static void sbus_unreg_object_paths(struct sbus_conn_ctx *dct_ctx) {
+ struct sbus_method_ctx *iter = dct_ctx->method_ctx_list;
+ struct sbus_method_ctx *purge;
+
+ while(iter != NULL) {
+ dbus_connection_unregister_object_path(dct_ctx->conn, iter->path);
+ DLIST_REMOVE(dct_ctx->method_ctx_list, iter);
+ purge = iter;
+ iter = iter->next;
+ talloc_free(purge);
+ }
+}
+
+void sbus_conn_set_private_data(struct sbus_conn_ctx *dct_ctx, void *private) {
+ dct_ctx->private = private;
+}
diff --git a/server/sbus/sssd_dbus_private.h b/server/sbus/sssd_dbus_private.h
new file mode 100644
index 000000000..d102c8c9d
--- /dev/null
+++ b/server/sbus/sssd_dbus_private.h
@@ -0,0 +1,14 @@
+#ifndef _SSSD_DBUS_PRIVATE_H_
+#define _SSSD_DBUS_PRIVATE_H_
+
+int sbus_add_connection(TALLOC_CTX *ctx,
+ struct event_context *ev,
+ DBusConnection *dbus_conn,
+ struct sbus_conn_ctx **dct_ctx,
+ int connection_type);
+
+struct timeval _dbus_timeout_get_interval_tv(int interval);
+void sbus_remove_watch(DBusWatch *watch, void *data);
+void sbus_remove_timeout(DBusTimeout *timeout, void *data);
+
+#endif /* _SSSD_DBUS_PRIVATE_H_ */
diff --git a/server/sbus/sssd_dbus_server.c b/server/sbus/sssd_dbus_server.c
new file mode 100644
index 000000000..22dcbf39a
--- /dev/null
+++ b/server/sbus/sssd_dbus_server.c
@@ -0,0 +1,316 @@
+/*
+ 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 "events.h"
+#include "util/util.h"
+#include "dbus/dbus.h"
+#include "sbus/sssd_dbus.h"
+#include "sbus/sssd_dbus_private.h"
+
+/* Types */
+struct sbus_srv_ctx {
+ DBusServer *server;
+ /*
+ * sd_ctx here describes the object path that will be
+ * presented to all clients of this server. Additional
+ * connection-specific paths can be specified by the
+ * init_fn, which is called every time a new connection
+ * is established.
+ * There should only be one global object path (for
+ * simplicity's sake)
+ */
+ struct event_context *ev;
+ struct sbus_method_ctx *sd_ctx;
+ sbus_server_conn_init_fn init_fn;
+};
+
+struct sbus_srv_watch_ctx {
+ DBusWatch *watch;
+ int fd;
+ struct fd_event *fde;
+ struct sbus_srv_ctx *top;
+};
+
+struct dbus_srv_timeout_ctx {
+ DBusTimeout *timeout;
+ struct timed_event *te;
+ struct sbus_srv_ctx *top;
+};
+
+static int sbus_server_destructor(void **server);
+
+/*
+ * dbus_server_read_write_handler
+ * Callback for D-BUS to handle messages on a file-descriptor
+ */
+static void sbus_srv_read_write_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags, void *data)
+{
+ struct sbus_srv_watch_ctx *svw_ctx;
+ svw_ctx = talloc_get_type(data, struct sbus_srv_watch_ctx);
+
+ dbus_server_ref(svw_ctx->top->server);
+ if (flags & EVENT_FD_READ) {
+ dbus_watch_handle(svw_ctx->watch, DBUS_WATCH_READABLE);
+ }
+ if (flags & EVENT_FD_WRITE) {
+ dbus_watch_handle(svw_ctx->watch, DBUS_WATCH_WRITABLE);
+ }
+ dbus_server_unref(svw_ctx->top->server);
+}
+
+/*
+ * add_server_watch
+ * Set up hooks into the libevents mainloop for
+ * D-BUS to add file descriptor-based events
+ */
+static dbus_bool_t sbus_add_srv_watch(DBusWatch *watch, void *data)
+{
+ unsigned int flags;
+ unsigned int event_flags;
+ struct sbus_srv_ctx *dt_ctx;
+ struct sbus_srv_watch_ctx *svw_ctx;
+
+ if (!dbus_watch_get_enabled(watch)) {
+ return FALSE;
+ }
+
+ dt_ctx = talloc_get_type(data, struct sbus_srv_ctx);
+
+ svw_ctx = talloc_zero(dt_ctx, struct sbus_srv_watch_ctx);
+ svw_ctx->top = dt_ctx;
+ svw_ctx->watch = watch;
+
+ flags = dbus_watch_get_flags(watch);
+ svw_ctx->fd = dbus_watch_get_unix_fd(watch);
+
+ event_flags = 0;
+
+ if (flags & DBUS_WATCH_READABLE) {
+ event_flags |= EVENT_FD_READ;
+ }
+
+ if (flags & DBUS_WATCH_WRITABLE) {
+ event_flags |= EVENT_FD_WRITE;
+ }
+ DEBUG(2,("%lX: %d, %d=%s\n", watch, svw_ctx->fd, event_flags, event_flags==EVENT_FD_READ?"READ":"WRITE"));
+
+ svw_ctx->fde = event_add_fd(dt_ctx->ev, svw_ctx, svw_ctx->fd,
+ event_flags, sbus_srv_read_write_handler,
+ svw_ctx);
+
+ /* Save the event to the watch object so it can be removed later */
+ dbus_watch_set_data(svw_ctx->watch, svw_ctx->fde, NULL);
+
+ return TRUE;
+}
+
+/*
+ * server_watch_toggled
+ * Hook for D-BUS to toggle the enabled/disabled state of
+ * an event in the mainloop
+ */
+static void sbus_toggle_srv_watch(DBusWatch *watch, void *data)
+{
+ if (dbus_watch_get_enabled(watch)) {
+ sbus_add_srv_watch(watch, data);
+ } else {
+ sbus_remove_watch(watch, data);
+ }
+}
+
+static void sbus_srv_timeout_handler(struct event_context *ev,
+ struct timed_event *te,
+ struct timeval t, void *data)
+{
+ struct dbus_srv_timeout_ctx *svt_ctx;
+ svt_ctx = talloc_get_type(data, struct dbus_srv_timeout_ctx);
+ dbus_timeout_handle(svt_ctx->timeout);
+}
+
+/*
+ * add_server_timeout
+ * Hook for D-BUS to add time-based events to the mainloop
+ */
+static dbus_bool_t sbus_add_srv_timeout(DBusTimeout *timeout, void *data)
+{
+ struct sbus_srv_ctx *dt_ctx;
+ struct dbus_srv_timeout_ctx *svt_ctx;
+ struct timeval tv;
+
+ if (!dbus_timeout_get_enabled(timeout))
+ return TRUE;
+
+ dt_ctx = talloc_get_type(data, struct sbus_srv_ctx);
+
+ svt_ctx = talloc_zero(dt_ctx,struct dbus_srv_timeout_ctx);
+ svt_ctx->top = dt_ctx;
+ svt_ctx->timeout = timeout;
+
+ tv = _dbus_timeout_get_interval_tv(dbus_timeout_get_interval(timeout));
+
+ svt_ctx->te = event_add_timed(dt_ctx->ev, svt_ctx, tv,
+ sbus_srv_timeout_handler, svt_ctx);
+
+ /* Save the event to the watch object so it can be removed later */
+ dbus_timeout_set_data(svt_ctx->timeout, svt_ctx->te, NULL);
+
+ return TRUE;
+}
+
+/*
+ * server_timeout_toggled
+ * Hook for D-BUS to toggle the enabled/disabled state of a mainloop
+ * event
+ */
+static void sbus_toggle_srv_timeout(DBusTimeout *timeout, void *data)
+{
+ if (dbus_timeout_get_enabled(timeout)) {
+ sbus_add_srv_timeout(timeout, data);
+ } else {
+ sbus_remove_timeout(timeout, data);
+ }
+}
+
+/*
+ * 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 *server, DBusConnection *conn,
+ void *data)
+{
+ struct sbus_srv_ctx *dst_ctx;
+ struct sbus_conn_ctx *dct_ctx;
+ struct sbus_method_ctx *iter;
+
+ /*DBusObjectPathVTable *connection_vtable;*/
+ int ret;
+ DEBUG(0,("Entering.\n"));
+ dst_ctx = talloc_get_type(data,struct sbus_srv_ctx);
+ if(dst_ctx == NULL) {
+ return;
+ }
+
+ DEBUG(0,("Adding connection %lX.\n", conn));
+ ret = sbus_add_connection(dst_ctx, dst_ctx->ev, conn, &dct_ctx, SBUS_CONN_TYPE_PRIVATE);
+ if (ret != 0) {
+ dbus_connection_close(conn);
+ DEBUG(0,("Closing connection (failed setup)"));
+ return;
+ }
+
+ dbus_connection_ref(conn);
+
+ DEBUG(3,("Got a connection\n"));
+
+ /* Set up global methods */
+ iter = dst_ctx->sd_ctx;
+ while (iter != NULL) {
+ sbus_conn_add_method_ctx(dct_ctx, iter);
+ iter = iter->next;
+ }
+
+ /*
+ * 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.
+ */
+ dst_ctx->init_fn(dct_ctx);
+}
+
+/*
+ * 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(struct event_context *ev, struct sbus_method_ctx *ctx, const char *address, sbus_server_conn_init_fn init_fn)
+{
+ struct sbus_srv_ctx *dst_ctx;
+ DBusServer *dbus_server;
+ DBusServer **dbus_server_talloc;
+ DBusError dbus_error;
+ dbus_bool_t dbret;
+
+ /* Set up D-BUS server */
+ dbus_error_init(&dbus_error);
+ dbus_server = dbus_server_listen(address, &dbus_error);
+ if (!dbus_server) {
+ DEBUG(0,("dbus_server_listen failed! (name=%s, message=%s)\n",
+ dbus_error.name, dbus_error.message));
+ return EIO;
+ }
+
+ DEBUG(2, ("D-BUS Server listening on %s\n",
+ dbus_server_get_address(dbus_server)));
+
+ dst_ctx = talloc_zero(ev, struct sbus_srv_ctx);
+ if (!dst_ctx) {
+ return ENOMEM;
+ }
+
+ dbus_server_talloc = talloc_takeover(ctx, dbus_server, sbus_server_destructor);
+ dst_ctx->ev = ev;
+ dst_ctx->server = dbus_server;
+ dst_ctx->sd_ctx = ctx;
+ dst_ctx->init_fn = init_fn;
+
+ /* Set up D-BUS new connection handler */
+ dbus_server_set_new_connection_function(dst_ctx->server,
+ sbus_server_init_new_connection,
+ dst_ctx, NULL);
+
+ /* Set up DBusWatch functions */
+ dbret = dbus_server_set_watch_functions(dst_ctx->server, sbus_add_srv_watch,
+ sbus_remove_watch, sbus_toggle_srv_watch,
+ dst_ctx, NULL);
+ if (!dbret) {
+ DEBUG(0, ("Error setting up D-BUS server watch functions"));
+ talloc_free(dst_ctx);
+ return EIO;
+ }
+
+ /* Set up DBusTimeout functions */
+ dbret = dbus_server_set_timeout_functions(dst_ctx->server,
+ sbus_add_srv_timeout,
+ sbus_remove_timeout,
+ sbus_toggle_srv_timeout,
+ dst_ctx, NULL);
+ if (!dbret) {
+ DEBUG(0,("Error setting up D-BUS server timeout functions"));
+ dbus_server_set_watch_functions(dst_ctx->server, NULL, NULL, NULL, NULL, NULL);
+ talloc_free(dst_ctx);
+ return EIO;
+ }
+
+ return EOK;
+}
+
+static int sbus_server_destructor(void **server) {
+ dbus_server_disconnect(*server);
+ return 0;
+}
diff --git a/server/sbus/tests/test_client.c b/server/sbus/tests/test_client.c
new file mode 100644
index 000000000..bf1c207e5
--- /dev/null
+++ b/server/sbus/tests/test_client.c
@@ -0,0 +1,223 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include "events.h"
+#include "util/util.h"
+#include "dbus/dbus.h"
+#include "sbus/sssd_dbus.h"
+
+/* TODO: get this value from LDB */
+#define DBUS_ADDRESS "unix:path=/var/lib/sss/pipes/private/dbus"
+
+/* Identity */
+#define TEST_CLIENT_NAME "testclient"
+#define TEST_CLIENT_VERSION 1
+
+/* Monitor Interface */
+#define MONITOR_DBUS_INTERFACE "org.freeipa.sssd.monitor"
+#define MONITOR_DBUS_PATH "/org/freeipa/sssd/monitor"
+#define MONITOR_METHOD_VERSION "getVersion"
+
+/* Service Interface */
+#define SERVICE_PATH "/org/freeipa/sssd/service"
+#define SERVICE_INTERFACE "org.freeipa.sssd.service"
+#define SERVICE_METHOD_IDENTITY "getIdentity"
+
+struct test_cli_ctx {
+ struct sbus_method_ctx *sd_ctx;
+ /*DBusConnection *conn;*/
+ struct event_context *ev;
+ struct sbus_conn_ctx *dct_ctx;
+};
+
+static int provide_identity(DBusMessage *message, void *data, DBusMessage **r);
+
+struct sbus_method monitor_service_methods [] = {
+ {SERVICE_METHOD_IDENTITY, provide_identity},
+ {NULL, NULL}
+};
+
+static void request_version_timed(struct test_cli_ctx *ctx);
+
+static void print_version(DBusPendingCall *pending, void *data)
+{
+ DBusMessage *reply;
+ DBusError error;
+ const char *version_string;
+ int type;
+
+ dbus_error_init(&error);
+
+ reply = dbus_pending_call_steal_reply(pending);
+ if (!reply) {
+ /* reply should never be null. This function shouldn't be called
+ * until reply is valid. If reply is NULL here, something is seriously
+ * wrong and we should bail out.
+ */
+ DEBUG(0, ("Serious error. A reply callback was called but no reply was received"));
+ exit(3);
+ }
+
+ type = dbus_message_get_type(reply);
+
+ switch (type) {
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ if (dbus_message_get_args(reply, &error,
+ DBUS_TYPE_STRING,
+ &version_string,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stdout, "Version: %s\n", version_string);
+ fflush(stdout);
+ } else {
+ DEBUG(0, ("Error getting arguments in print_version"));
+ }
+ break;
+ case DBUS_MESSAGE_TYPE_ERROR:
+
+ if (strcmp(DBUS_ERROR_NO_REPLY, dbus_message_get_error_name(reply))==0) {
+ DEBUG(0, ("Received error. Timeout"));
+ }
+ else {
+ DEBUG(0, ("Received error. Not a timeout: %s", dbus_message_get_error_name(reply)));
+ }
+ break;
+ default:
+ DEBUG(0, ("Received unexpected message\n"));
+ exit(4);
+ }
+}
+
+static void test_timed_handler(struct event_context *ev,
+ struct timed_event *te,
+ struct timeval tv, void *data)
+{
+ struct test_cli_ctx *test_ctx;
+ struct sbus_method_ctx *ctx;
+ DBusPendingCall *pending_reply;
+ DBusMessage *vmsg;
+ DBusError error;
+ dbus_bool_t dbret;
+
+ test_ctx = talloc_get_type(data, struct test_cli_ctx);
+ ctx = test_ctx->sd_ctx;
+
+ fprintf(stdout, ".");
+ fflush(stdout);
+
+ dbus_error_init(&error);
+ vmsg = dbus_message_new_method_call(NULL,
+ ctx->path, ctx->interface,
+ MONITOR_METHOD_VERSION);
+
+ dbret = dbus_connection_send_with_reply(sbus_get_connection(test_ctx->dct_ctx), vmsg,
+ &pending_reply, -1);
+ if (!dbret) {
+ /* Critical failure */
+ DEBUG(0,("Failed to send version_request"));
+ exit(2);
+ }
+
+ dbus_pending_call_set_notify(pending_reply, print_version, NULL, NULL);
+
+ dbus_message_unref(vmsg);
+
+ request_version_timed(test_ctx);
+}
+
+static void request_version_timed(struct test_cli_ctx *ctx)
+{
+ struct timed_event *te = NULL;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ tv.tv_sec += 5;
+ tv.tv_usec = 0;
+ te = event_add_timed(ctx->ev, ctx, tv, test_timed_handler, ctx);
+ if (te == NULL) {
+ DEBUG(0, ("failed to add event!\n"));
+ exit(1);
+ }
+}
+
+int main (int argc, const char *argv[])
+{
+ struct event_context *event_ctx;
+ struct sbus_method_ctx *ctx;
+ struct test_cli_ctx *test_ctx;
+ struct sbus_method_ctx *service_methods;
+ int ret;
+
+ event_ctx = event_context_init(talloc_autofree_context());
+ if (!event_ctx) {
+ printf("Out of memory!?\n");
+ exit(1);
+ }
+
+ ctx = talloc_zero(event_ctx, struct sbus_method_ctx);
+ if (!ctx) {
+ printf("Out of memory!?\n");
+ exit(1);
+ }
+
+ test_ctx = talloc(event_ctx, struct test_cli_ctx);
+ if (!test_ctx) {
+ printf("Out of memory!?\n");
+ exit(1);
+ }
+
+ test_ctx->ev = event_ctx;
+ ctx->interface = talloc_strdup(ctx, MONITOR_DBUS_INTERFACE);
+ ctx->path = talloc_strdup(ctx, MONITOR_DBUS_PATH);
+ if (!ctx->interface || !ctx->path) {
+ printf("Out of memory!?\n");
+ exit(1);
+ }
+
+ ret = sbus_new_connection(test_ctx, test_ctx->ev, DBUS_ADDRESS, &(test_ctx->dct_ctx), NULL);
+ if (ret != EOK) {
+ exit(1);
+ }
+
+ test_ctx->sd_ctx = ctx;
+
+ dbus_connection_set_exit_on_disconnect(sbus_get_connection(test_ctx->dct_ctx), TRUE);
+
+ /* Set up a timed event to request the server version every
+ * five seconds and print it to the screen.
+ */
+ request_version_timed(test_ctx);
+
+ /* Set up handler for service methods */
+ service_methods = talloc_zero(test_ctx, struct sbus_method_ctx);
+ service_methods->interface = talloc_strdup(service_methods, SERVICE_INTERFACE);
+ service_methods->path = talloc_strdup(service_methods, SERVICE_PATH);
+ service_methods->methods = monitor_service_methods;
+ sbus_conn_add_method_ctx(test_ctx->dct_ctx, service_methods);
+
+ /* Enter the main loop (and hopefully never return) */
+ event_loop_wait(event_ctx);
+
+ talloc_free(event_ctx);
+ return EXIT_SUCCESS;
+}
+
+static int provide_identity(DBusMessage *message, void *data, DBusMessage **r) {
+ const char *name = TEST_CLIENT_NAME;
+ dbus_uint16_t version = TEST_CLIENT_VERSION;
+
+ DBusMessage *reply;
+ dbus_bool_t ret;
+
+ reply = dbus_message_new_method_return(message);
+ ret = dbus_message_append_args(reply,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_UINT16, &version,
+ DBUS_TYPE_INVALID);
+
+ if (!ret) {
+ return EIO;
+ }
+
+ *r = reply;
+ return EOK;
+}