/* 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" /* 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; void *init_pvt_data; }; 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 *ctx); /* * 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(5,("%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 *srv_ctx; struct sbus_conn_ctx *conn_ctx; struct sbus_method_ctx *iter; int ret; DEBUG(5,("Entering.\n")); srv_ctx = talloc_get_type(data, struct sbus_srv_ctx); if (srv_ctx == NULL) { return; } DEBUG(5,("Adding connection %lX.\n", conn)); ret = sbus_add_connection(srv_ctx, srv_ctx->ev, conn, &conn_ctx, SBUS_CONN_TYPE_PRIVATE); if (ret != 0) { dbus_connection_close(conn); DEBUG(5,("Closing connection (failed setup)")); return; } dbus_connection_ref(conn); DEBUG(5,("Got a connection\n")); /* Set up global methods */ iter = srv_ctx->sd_ctx; while (iter != NULL) { sbus_conn_add_method_ctx(conn_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. */ ret = srv_ctx->init_fn(conn_ctx, srv_ctx->init_pvt_data); if (ret != EOK) { DEBUG(1,("Initialization failed!\n")); } } /* * 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 event_context *ev, struct sbus_method_ctx *ctx, struct sbus_srv_ctx **server_ctx, const char *address, sbus_server_conn_init_fn init_fn, void *init_pvt_data) { struct sbus_srv_ctx *srv_ctx; DBusServer *dbus_server; DBusError dbus_error; dbus_bool_t dbret; char *tmp; *server_ctx = 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)); return EIO; } tmp = dbus_server_get_address(dbus_server); DEBUG(3, ("D-BUS Server listening on %s\n", tmp)); free(tmp); srv_ctx = talloc_zero(mem_ctx, struct sbus_srv_ctx); if (!srv_ctx) { return ENOMEM; } srv_ctx->ev = ev; srv_ctx->server = dbus_server; srv_ctx->sd_ctx = ctx; srv_ctx->init_fn = init_fn; srv_ctx->init_pvt_data = init_pvt_data; talloc_set_destructor((TALLOC_CTX *)srv_ctx, sbus_server_destructor); /* Set up D-BUS new connection handler */ dbus_server_set_new_connection_function(srv_ctx->server, sbus_server_init_new_connection, srv_ctx, NULL); /* Set up DBusWatch functions */ dbret = dbus_server_set_watch_functions(srv_ctx->server, sbus_add_srv_watch, sbus_remove_watch, sbus_toggle_srv_watch, srv_ctx, NULL); if (!dbret) { DEBUG(4, ("Error setting up D-BUS server watch functions")); talloc_free(srv_ctx); return EIO; } /* Set up DBusTimeout functions */ dbret = dbus_server_set_timeout_functions(srv_ctx->server, sbus_add_srv_timeout, sbus_remove_timeout, sbus_toggle_srv_timeout, srv_ctx, NULL); if (!dbret) { DEBUG(4,("Error setting up D-BUS server timeout functions")); dbus_server_set_watch_functions(srv_ctx->server, NULL, NULL, NULL, NULL, NULL); talloc_free(srv_ctx); return EIO; } *server_ctx = srv_ctx; return EOK; } static int sbus_server_destructor(void *ctx) { struct sbus_srv_ctx *srv_ctx = talloc_get_type(ctx, struct sbus_srv_ctx); dbus_server_disconnect(srv_ctx->server); return 0; }