diff options
author | Stephen Gallagher <sgallagh@redhat.com> | 2010-02-18 07:49:04 -0500 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2010-02-18 13:48:45 -0500 |
commit | 1c48b5a62f73234ed26bb20f0ab345ab61cda0ab (patch) | |
tree | 0b6cddd567a862e1a7b5df23764869782a62ca78 /src/sbus | |
parent | 8c56df3176f528fe0260974b3bf934173c4651ea (diff) | |
download | sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.tar.gz sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.tar.xz sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.zip |
Rename server/ directory to src/
Also update BUILD.txt
Diffstat (limited to 'src/sbus')
-rw-r--r-- | src/sbus/sbus_client.c | 57 | ||||
-rw-r--r-- | src/sbus/sbus_client.h | 36 | ||||
-rw-r--r-- | src/sbus/sssd_dbus.h | 153 | ||||
-rw-r--r-- | src/sbus/sssd_dbus_common.c | 444 | ||||
-rw-r--r-- | src/sbus/sssd_dbus_connection.c | 692 | ||||
-rw-r--r-- | src/sbus/sssd_dbus_private.h | 98 | ||||
-rw-r--r-- | src/sbus/sssd_dbus_server.c | 171 |
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; +} |