/*
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 "events.h"
#include "util/util.h"
#include "dbus/dbus.h"
#include "dbus/sssd_dbus.h"
#include "dbus/sssd_dbus_private.h"
/* Types */
struct dbus_server_toplevel_context {
DBusServer *server;
/* talloc context to manage the server object memory*/
DBusServer **server_talloc;
struct sssd_dbus_ctx *sd_ctx;
};
struct dbus_server_watch_context {
DBusWatch *watch;
int fd;
struct fd_event *fde;
struct dbus_server_toplevel_context *top;
};
struct dbus_server_timeout_context {
DBusTimeout *timeout;
struct timed_event *te;
struct dbus_server_toplevel_context *top;
};
static int dbus_server_destructor(void **server);
/*
* dbus_server_read_write_handler
* Callback for D-BUS to handle messages on a file-descriptor
*/
static void dbus_server_read_write_handler(struct event_context *ev,
struct fd_event *fde,
uint16_t flags, void *data)
{
struct dbus_server_watch_context *svw_ctx;
svw_ctx = talloc_get_type(data, struct dbus_server_watch_context);
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 add_server_watch(DBusWatch *watch, void *data)
{
unsigned int flags;
unsigned int event_flags;
struct dbus_server_toplevel_context *dt_ctx;
struct dbus_server_watch_context *svw_ctx;
if (!dbus_watch_get_enabled(watch)) {
return FALSE;
}
dt_ctx = talloc_get_type(data, struct dbus_server_toplevel_context);
svw_ctx = talloc_zero(dt_ctx, struct dbus_server_watch_context);
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->sd_ctx->ev, svw_ctx, svw_ctx->fd,
event_flags, dbus_server_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 toggle_server_watch(DBusWatch *watch, void *data)
{
if (dbus_watch_get_enabled(watch)) {
add_server_watch(watch, data);
} else {
remove_watch(watch, data);
}
}
static void dbus_server_timeout_handler(struct event_context *ev,
struct timed_event *te,
struct timeval t, void *data)
{
struct dbus_server_timeout_context *svt_ctx;
svt_ctx = talloc_get_type(data, struct dbus_server_timeout_context);
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 add_server_timeout(DBusTimeout *timeout, void *data)
{
struct dbus_server_toplevel_context *dt_ctx;
struct dbus_server_timeout_context *svt_ctx;
struct timeval tv;
if (!dbus_timeout_get_enabled(timeout))
return TRUE;
dt_ctx = talloc_get_type(data, struct dbus_server_toplevel_context);
svt_ctx = talloc_zero(dt_ctx,struct dbus_server_timeout_context);
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->sd_ctx->ev, svt_ctx, tv,
dbus_server_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 toggle_server_timeout(DBusTimeout *timeout, void *data)
{
if (dbus_timeout_get_enabled(timeout)) {
add_server_timeout(timeout, data);
} else {
remove_timeout(timeout, data);
}
}
/* messsage_handler
* Receive messages and process them
*/
static DBusHandlerResult message_handler(DBusConnection *conn,
DBusMessage *message,
void *user_data)
{
struct sssd_dbus_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 sssd_dbus_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->name) != 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 */
}
if (reply) {
dbus_connection_send(conn, reply, NULL);
dbus_message_unref(reply);
}
return reply ? DBUS_HANDLER_RESULT_HANDLED :
DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
/*
* 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 new_connection_callback(DBusServer *server, DBusConnection *conn,
void *data)
{
struct dbus_server_toplevel_context *dst_ctx;
DBusObjectPathVTable *monitor_vtable;
int *connection_type;
int ret;
dst_ctx = talloc_get_type(data,struct dbus_server_toplevel_context);
if (!dbus_connection_allocate_data_slot(&connection_type_slot)) {
dbus_connection_close(conn);
return;
}
connection_type = talloc(dst_ctx, int);
*connection_type = DBUS_CONNECTION_TYPE_PRIVATE;
dbus_connection_set_data(conn, connection_type_slot, connection_type, talloc_free);
ret = sssd_add_dbus_connection(dst_ctx->sd_ctx, conn);
if (ret != 0) {
dbus_connection_close(conn);
DEBUG(0,("Closing connection (failed setup)"));
return;
}
dbus_connection_ref(conn);
DEBUG(3,("Got a connection\n"));
monitor_vtable = talloc_zero(dst_ctx, DBusObjectPathVTable);
DEBUG (3,("Initializing D-BUS methods.\n"));
monitor_vtable->message_function = message_handler;
dbus_connection_register_object_path(conn, dst_ctx->sd_ctx->path,
monitor_vtable, dst_ctx->sd_ctx);
DEBUG(3,("D-BUS method initialization complete.\n"));
}
/*
* dbus_new_server
* Set up a D-BUS server, integrate with the event loop
* for handling file descriptor and timed events
*/
int sssd_new_dbus_server(struct sssd_dbus_ctx *ctx, const char *address)
{
struct dbus_server_toplevel_context *dt_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;
}
dbus_server_talloc = talloc_takeover(ctx, dbus_server, dbus_server_destructor);
DEBUG(2, ("D-BUS Server listening on %s\n",
dbus_server_get_address(dbus_server)));
dt_ctx = talloc_zero(ctx, struct dbus_server_toplevel_context);
if (!dt_ctx) {
talloc_free(dbus_server_talloc);
return ENOMEM;
}
dt_ctx->server_talloc = dbus_server_talloc;
dt_ctx->server = *dbus_server_talloc;
dt_ctx->sd_ctx = ctx;
/* Set up D-BUS new connection handler */
dbus_server_set_new_connection_function(dt_ctx->server,
new_connection_callback,
dt_ctx, NULL);
/* Set up DBusWatch functions */
dbret = dbus_server_set_watch_functions(dt_ctx->server, add_server_watch,
remove_server_watch, toggle_server_watch,
dt_ctx, NULL);
if (!dbret) {
DEBUG(0, ("Error setting up D-BUS server watch functions"));
talloc_free(dt_ctx->server_talloc);
dt_ctx->server = NULL;
return EIO;
}
/* Set up DBusTimeout functions */
dbret = dbus_server_set_timeout_functions(dt_ctx->server,
add_server_timeout,
remove_timeout,
toggle_server_timeout,
dt_ctx, NULL);
if (!dbret) {
DEBUG(0,("Error setting up D-BUS server timeout functions"));
dbus_server_set_watch_functions(dt_ctx->server, NULL, NULL, NULL, NULL, NULL);
talloc_free(dt_ctx->server_talloc);
dt_ctx->server = NULL;
return EIO;
}
return EOK;
}
static int dbus_server_destructor(void **server) {
dbus_server_disconnect(*server);
return 0;
}