/*
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 .
*/
#include
#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 *dbus_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 tevent_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 tevent_fd *fde;
struct sbus_srv_ctx *srv_ctx;
};
struct dbus_srv_timeout_ctx {
DBusTimeout *timeout;
struct tevent_timer *te;
struct sbus_srv_ctx *srv_ctx;
};
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 tevent_context *ev,
struct tevent_fd *fde,
uint16_t flags, void *data)
{
struct sbus_srv_watch_ctx *watch_ctx;
watch_ctx = talloc_get_type(data, struct sbus_srv_watch_ctx);
dbus_server_ref(watch_ctx->srv_ctx->dbus_server);
if (flags & TEVENT_FD_READ) {
dbus_watch_handle(watch_ctx->watch, DBUS_WATCH_READABLE);
}
if (flags & TEVENT_FD_WRITE) {
dbus_watch_handle(watch_ctx->watch, DBUS_WATCH_WRITABLE);
}
dbus_server_unref(watch_ctx->srv_ctx->dbus_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 *srv_ctx;
struct sbus_srv_watch_ctx *watch_ctx;
if (!dbus_watch_get_enabled(watch)) {
return FALSE;
}
srv_ctx = talloc_get_type(data, struct sbus_srv_ctx);
watch_ctx = talloc_zero(srv_ctx, struct sbus_srv_watch_ctx);
watch_ctx->srv_ctx = srv_ctx;
watch_ctx->watch = watch;
flags = dbus_watch_get_flags(watch);
#ifdef HAVE_DBUS_WATCH_GET_UNIX_FD
watch_ctx->fd = dbus_watch_get_unix_fd(watch);
#else
watch_ctx->fd = dbus_watch_get_fd(watch);
#endif
event_flags = 0;
if (flags & DBUS_WATCH_READABLE) {
event_flags |= TEVENT_FD_READ;
}
if (flags & DBUS_WATCH_WRITABLE) {
event_flags |= TEVENT_FD_WRITE;
}
DEBUG(5,("%lX: %d, %d=%s\n", watch, watch_ctx->fd, event_flags, event_flags==TEVENT_FD_READ?"READ":"WRITE"));
watch_ctx->fde = tevent_add_fd(srv_ctx->ev, watch_ctx, watch_ctx->fd,
event_flags, sbus_srv_read_write_handler,
watch_ctx);
/* Save the event to the watch object so it can be removed later */
dbus_watch_set_data(watch_ctx->watch, watch_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 tevent_context *ev,
struct tevent_timer *te,
struct timeval t, void *data)
{
struct dbus_srv_timeout_ctx *timeout_ctx;
timeout_ctx = talloc_get_type(data, struct dbus_srv_timeout_ctx);
dbus_timeout_handle(timeout_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 *srv_ctx;
struct dbus_srv_timeout_ctx *timeout_ctx;
struct timeval tv;
if (!dbus_timeout_get_enabled(timeout))
return TRUE;
srv_ctx = talloc_get_type(data, struct sbus_srv_ctx);
timeout_ctx = talloc_zero(srv_ctx,struct dbus_srv_timeout_ctx);
timeout_ctx->srv_ctx = srv_ctx;
timeout_ctx->timeout = timeout;
tv = _dbus_timeout_get_interval_tv(dbus_timeout_get_interval(timeout));
timeout_ctx->te = tevent_add_timer(srv_ctx->ev, timeout_ctx, tv,
sbus_srv_timeout_handler, timeout_ctx);
/* Save the event to the watch object so it can be removed later */
dbus_timeout_set_data(timeout_ctx->timeout, timeout_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 *dbus_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 tevent_context *ev, struct sbus_method_ctx *ctx,
struct sbus_srv_ctx **_srv_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;
*_srv_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));
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);
srv_ctx = talloc_zero(mem_ctx, struct sbus_srv_ctx);
if (!srv_ctx) {
return ENOMEM;
}
srv_ctx->ev = ev;
srv_ctx->dbus_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->dbus_server,
sbus_server_init_new_connection,
srv_ctx, NULL);
/* Set up DBusWatch functions */
dbret = dbus_server_set_watch_functions(srv_ctx->dbus_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->dbus_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->dbus_server,
NULL, NULL, NULL, NULL, NULL);
talloc_free(srv_ctx);
return EIO;
}
*_srv_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->dbus_server);
return 0;
}