Add D-Bus helper library for GLib integration

This commit is contained in:
Marcel Holtmann 2009-04-26 20:51:36 +02:00
parent 798111efd4
commit 7851155b80
7 changed files with 1552 additions and 1 deletions

View File

@ -1,4 +1,6 @@
SUBDIRS = gdbus
MAINTAINERCLEANFILES = Makefile.in \
aclocal.m4 configure config.h.in config.sub config.guess \
ltmain.sh depcomp missing install-sh mkinstalldirs

View File

@ -16,6 +16,12 @@ AC_PROG_CC
AC_PROG_CC_PIE
AC_PROG_INSTALL
m4_define([_LT_AC_TAGCONFIG], [])
m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
AC_DISABLE_STATIC
AC_PROG_LIBTOOL
AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],
[enable compiling with debugging information]), [
if (test "${enableval}" = "yes" &&
@ -64,4 +70,7 @@ else
fi
AC_SUBST(DBUS_DATADIR)
AC_OUTPUT(Makefile)
AC_SUBST([GDBUS_CFLAGS], ['$(DBUS_CFLAGS) -I$(top_srcdir)/gdbus'])
AC_SUBST([GDBUS_LIBS], ['$(top_builddir)/gdbus/libgdbus.la $(DBUS_LIBS)'])
AC_OUTPUT(Makefile gdbus/Makefile)

8
gdbus/Makefile.am Normal file
View File

@ -0,0 +1,8 @@
noinst_LTLIBRARIES = libgdbus.la
libgdbus_la_SOURCES = gdbus.h mainloop.c object.c watch.c
AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@
MAINTAINERCLEANFILES = Makefile.in

139
gdbus/gdbus.h Normal file
View File

@ -0,0 +1,139 @@
/*
*
* D-Bus helper library
*
* Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef __GDBUS_H
#define __GDBUS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <dbus/dbus.h>
#include <glib.h>
typedef void (* GDBusWatchFunction) (DBusConnection *connection,
void *user_data);
typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection,
DBusMessage *message, void *user_data);
DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
DBusError *error);
gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
DBusError *error);
gboolean g_dbus_check_service(DBusConnection *connection, const char *name);
gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
GDBusWatchFunction function,
void *user_data, DBusFreeFunction destroy);
typedef void (* GDBusDestroyFunction) (void *user_data);
typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection,
DBusMessage *message, void *user_data);
typedef enum {
G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
G_DBUS_METHOD_FLAG_NOREPLY = (1 << 1),
G_DBUS_METHOD_FLAG_ASYNC = (1 << 2),
} GDBusMethodFlags;
typedef enum {
G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0),
} GDBusSignalFlags;
typedef enum {
G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
} GDBusPropertyFlags;
typedef struct {
const char *name;
const char *signature;
const char *reply;
GDBusMethodFunction function;
GDBusMethodFlags flags;
} GDBusMethodTable;
typedef struct {
const char *name;
const char *signature;
GDBusSignalFlags flags;
} GDBusSignalTable;
typedef struct {
const char *name;
const char *type;
GDBusPropertyFlags flags;
} GDBusPropertyTable;
gboolean g_dbus_register_interface(DBusConnection *connection,
const char *path, const char *name,
GDBusMethodTable *methods,
GDBusSignalTable *signals,
GDBusPropertyTable *properties,
void *user_data,
GDBusDestroyFunction destroy);
gboolean g_dbus_unregister_interface(DBusConnection *connection,
const char *path, const char *name);
DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
const char *format, ...);
DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
const char *format, va_list args);
DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...);
DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
int type, va_list args);
gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message);
gboolean g_dbus_send_reply(DBusConnection *connection,
DBusMessage *message, int type, ...);
gboolean g_dbus_send_reply_valist(DBusConnection *connection,
DBusMessage *message, int type, va_list args);
gboolean g_dbus_emit_signal(DBusConnection *connection,
const char *path, const char *interface,
const char *name, int type, ...);
gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
const char *path, const char *interface,
const char *name, int type, va_list args);
guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
GDBusWatchFunction connect,
GDBusWatchFunction disconnect,
void *user_data, GDBusDestroyFunction destroy);
guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
GDBusWatchFunction function,
void *user_data, GDBusDestroyFunction destroy);
guint g_dbus_add_signal_watch(DBusConnection *connection,
const char *rule, GDBusSignalFunction function,
void *user_data, GDBusDestroyFunction destroy);
gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag);
void g_dbus_remove_all_watches(DBusConnection *connection);
#ifdef __cplusplus
}
#endif
#endif /* __GDBUS_H */

348
gdbus/mainloop.c Normal file
View File

@ -0,0 +1,348 @@
/*
*
* D-Bus helper library
*
* Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdint.h>
#include <glib.h>
#include <dbus/dbus.h>
#ifdef NEED_DBUS_WATCH_GET_UNIX_FD
#define dbus_watch_get_unix_fd dbus_watch_get_fd
#endif
#include "gdbus.h"
#define DISPATCH_TIMEOUT 0
#define info(fmt...)
#define error(fmt...)
#define debug(fmt...)
typedef struct {
uint32_t id;
DBusTimeout *timeout;
} timeout_handler_t;
struct watch_info {
guint watch_id;
GIOChannel *io;
DBusConnection *conn;
};
struct server_info {
guint watch_id;
GIOChannel *io;
DBusServer *server;
};
struct disconnect_data {
GDBusWatchFunction disconnect_cb;
void *user_data;
};
static DBusHandlerResult disconnect_filter(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct disconnect_data *dc_data = data;
if (dbus_message_is_signal(msg,
DBUS_INTERFACE_LOCAL, "Disconnected") == TRUE) {
error("Got disconnected from the system message bus");
dc_data->disconnect_cb(conn, dc_data->user_data);
dbus_connection_unref(conn);
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static gboolean message_dispatch_cb(void *data)
{
DBusConnection *connection = data;
dbus_connection_ref(connection);
/* Dispatch messages */
while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
dbus_connection_unref(connection);
return FALSE;
}
static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
{
DBusWatch *watch = data;
struct watch_info *info = dbus_watch_get_data(watch);
int flags = 0;
if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE;
if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
dbus_watch_handle(watch, flags);
if (dbus_connection_get_dispatch_status(info->conn) == DBUS_DISPATCH_DATA_REMAINS)
g_timeout_add(DISPATCH_TIMEOUT, message_dispatch_cb, info->conn);
return TRUE;
}
static dbus_bool_t add_watch(DBusWatch *watch, void *data)
{
GIOCondition cond = G_IO_HUP | G_IO_ERR;
DBusConnection *conn = data;
struct watch_info *info;
int fd, flags;
if (!dbus_watch_get_enabled(watch))
return TRUE;
info = g_new(struct watch_info, 1);
fd = dbus_watch_get_unix_fd(watch);
info->io = g_io_channel_unix_new(fd);
info->conn = dbus_connection_ref(conn);
dbus_watch_set_data(watch, info, NULL);
flags = dbus_watch_get_flags(watch);
if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN;
if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT;
info->watch_id = g_io_add_watch(info->io, cond, watch_func, watch);
return TRUE;
}
static void remove_watch(DBusWatch *watch, void *data)
{
struct watch_info *info = dbus_watch_get_data(watch);
dbus_watch_set_data(watch, NULL, NULL);
if (info) {
g_source_remove(info->watch_id);
g_io_channel_unref(info->io);
dbus_connection_unref(info->conn);
g_free(info);
}
}
static void watch_toggled(DBusWatch *watch, void *data)
{
/* Because we just exit on OOM, enable/disable is
* no different from add/remove */
if (dbus_watch_get_enabled(watch))
add_watch(watch, data);
else
remove_watch(watch, data);
}
static gboolean timeout_handler_dispatch(gpointer data)
{
timeout_handler_t *handler = data;
/* if not enabled should not be polled by the main loop */
if (dbus_timeout_get_enabled(handler->timeout) != TRUE)
return FALSE;
dbus_timeout_handle(handler->timeout);
return FALSE;
}
static void timeout_handler_free(void *data)
{
timeout_handler_t *handler = data;
if (!handler)
return;
g_source_remove(handler->id);
g_free(handler);
}
static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
{
timeout_handler_t *handler;
if (!dbus_timeout_get_enabled(timeout))
return TRUE;
handler = g_new0(timeout_handler_t, 1);
handler->timeout = timeout;
handler->id = g_timeout_add(dbus_timeout_get_interval(timeout),
timeout_handler_dispatch, handler);
dbus_timeout_set_data(timeout, handler, timeout_handler_free);
return TRUE;
}
static void remove_timeout(DBusTimeout *timeout, void *data)
{
}
static void timeout_toggled(DBusTimeout *timeout, void *data)
{
if (dbus_timeout_get_enabled(timeout))
add_timeout(timeout, data);
else
remove_timeout(timeout, data);
}
static void dispatch_status_cb(DBusConnection *conn,
DBusDispatchStatus new_status, void *data)
{
if (!dbus_connection_get_is_connected(conn))
return;
if (new_status == DBUS_DISPATCH_DATA_REMAINS)
g_timeout_add(DISPATCH_TIMEOUT, message_dispatch_cb, data);
}
static void setup_dbus_with_main_loop(DBusConnection *conn)
{
dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
watch_toggled, conn, NULL);
dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
timeout_toggled, conn, NULL);
dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb,
conn, NULL);
}
DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
DBusError *error)
{
DBusConnection *conn;
conn = dbus_bus_get(type, error);
if (error != NULL) {
if (dbus_error_is_set(error) == TRUE)
return NULL;
}
if (conn == NULL)
return NULL;
if (name != NULL) {
if (dbus_bus_request_name(conn, name,
DBUS_NAME_FLAG_DO_NOT_QUEUE, error) !=
DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ) {
dbus_connection_unref(conn);
return NULL;
}
if (error != NULL) {
if (dbus_error_is_set(error) == TRUE) {
dbus_connection_unref(conn);
return NULL;
}
}
}
setup_dbus_with_main_loop(conn);
return conn;
}
gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
DBusError *error)
{
return TRUE;
}
gboolean g_dbus_check_service(DBusConnection *connection, const char *name)
{
DBusMessage *message, *reply;
const char **names;
int i, count;
gboolean result = FALSE;
message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "ListNames");
if (message == NULL) {
error("Can't allocate new message");
return FALSE;
}
reply = dbus_connection_send_with_reply_and_block(connection,
message, -1, NULL);
dbus_message_unref(message);
if (reply == NULL) {
error("Failed to execute method call");
return FALSE;
}
if (dbus_message_get_args(reply, NULL,
DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
&names, &count, DBUS_TYPE_INVALID) == FALSE) {
error("Failed to read name list");
goto done;
}
for (i = 0; i < count; i++)
if (g_str_equal(names[i], name) == TRUE) {
result = TRUE;
break;
}
done:
dbus_message_unref(reply);
return result;
}
gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
GDBusWatchFunction function,
void *user_data, DBusFreeFunction destroy)
{
struct disconnect_data *dc_data;
dc_data = g_new(struct disconnect_data, 1);
dc_data->disconnect_cb = function;
dc_data->user_data = user_data;
dbus_connection_set_exit_on_disconnect(connection, FALSE);
if (dbus_connection_add_filter(connection, disconnect_filter,
dc_data, g_free) == FALSE) {
error("Can't add D-Bus disconnect filter");
g_free(dc_data);
return FALSE;
}
return TRUE;
}

654
gdbus/object.c Normal file
View File

@ -0,0 +1,654 @@
/*
*
* D-Bus helper library
*
* Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <dbus/dbus.h>
#include "gdbus.h"
#define info(fmt...)
#define error(fmt...)
#define debug(fmt...)
struct generic_data {
unsigned int refcount;
GSList *interfaces;
char *introspect;
};
struct interface_data {
char *name;
GDBusMethodTable *methods;
GDBusSignalTable *signals;
GDBusPropertyTable *properties;
void *user_data;
GDBusDestroyFunction destroy;
};
static void print_arguments(GString *gstr, const char *sig,
const char *direction)
{
int i;
for (i = 0; sig[i]; i++) {
char type[32];
int struct_level, dict_level;
unsigned int len;
gboolean complete;
complete = FALSE;
struct_level = dict_level = 0;
memset(type, 0, sizeof(type));
/* Gather enough data to have a single complete type */
for (len = 0; len < (sizeof(type) - 1) && sig[i]; len++, i++) {
switch (sig[i]){
case '(':
struct_level++;
break;
case ')':
struct_level--;
if (struct_level <= 0 && dict_level <= 0)
complete = TRUE;
break;
case '{':
dict_level++;
break;
case '}':
dict_level--;
if (struct_level <= 0 && dict_level <= 0)
complete = TRUE;
break;
case 'a':
break;
default:
if (struct_level <= 0 && dict_level <= 0)
complete = TRUE;
break;
}
type[len] = sig[i];
if (complete)
break;
}
if (direction)
g_string_append_printf(gstr,
"\t\t\t<arg type=\"%s\" direction=\"%s\"/>\n",
type, direction);
else
g_string_append_printf(gstr,
"\t\t\t<arg type=\"%s\"/>\n",
type);
}
}
static void generate_interface_xml(GString *gstr, struct interface_data *iface)
{
GDBusMethodTable *method;
GDBusSignalTable *signal;
for (method = iface->methods; method && method->name; method++) {
if (!strlen(method->signature) && !strlen(method->reply))
g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
method->name);
else {
g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
method->name);
print_arguments(gstr, method->signature, "in");
print_arguments(gstr, method->reply, "out");
g_string_append_printf(gstr, "\t\t</method>\n");
}
}
for (signal = iface->signals; signal && signal->name; signal++) {
if (!strlen(signal->signature))
g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
signal->name);
else {
g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
signal->name);
print_arguments(gstr, signal->signature, NULL);
g_string_append_printf(gstr, "\t\t</signal>\n");
}
}
}
static void generate_introspection_xml(DBusConnection *conn,
struct generic_data *data, const char *path)
{
GSList *list;
GString *gstr;
char **children;
int i;
g_free(data->introspect);
gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
g_string_append_printf(gstr, "<node name=\"%s\">\n", path);
for (list = data->interfaces; list; list = list->next) {
struct interface_data *iface = list->data;
g_string_append_printf(gstr, "\t<interface name=\"%s\">\n",
iface->name);
generate_interface_xml(gstr, iface);
g_string_append_printf(gstr, "\t</interface>\n");
}
if (!dbus_connection_list_registered(conn, path, &children))
goto done;
for (i = 0; children[i]; i++)
g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n",
children[i]);
dbus_free_string_array(children);
done:
g_string_append_printf(gstr, "</node>\n");
data->introspect = g_string_free(gstr, FALSE);
}
static DBusHandlerResult introspect(DBusConnection *connection,
DBusMessage *message, struct generic_data *data)
{
DBusMessage *reply;
if (!dbus_message_has_signature(message, DBUS_TYPE_INVALID_AS_STRING)) {
error("Unexpected signature to introspect call");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
if (!data->introspect)
generate_introspection_xml(connection, data,
dbus_message_get_path(message));
reply = dbus_message_new_method_return(message);
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
DBUS_TYPE_INVALID);
dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
static void generic_unregister(DBusConnection *connection, void *user_data)
{
struct generic_data *data = user_data;
g_free(data->introspect);
g_free(data);
}
static struct interface_data *find_interface(GSList *interfaces,
const char *name)
{
GSList *list;
if (!name)
return NULL;
for (list = interfaces; list; list = list->next) {
struct interface_data *iface = list->data;
if (!strcmp(name, iface->name))
return iface;
}
return NULL;
}
static DBusHandlerResult generic_message(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
struct generic_data *data = user_data;
struct interface_data *iface;
GDBusMethodTable *method;
const char *interface;
if (dbus_message_is_method_call(message,
DBUS_INTERFACE_INTROSPECTABLE,
"Introspect"))
return introspect(connection, message, data);
interface = dbus_message_get_interface(message);
iface = find_interface(data->interfaces, interface);
if (!iface)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
for (method = iface->methods; method &&
method->name && method->function; method++) {
DBusMessage *reply;
if (dbus_message_is_method_call(message, iface->name,
method->name) == FALSE)
continue;
if (dbus_message_has_signature(message,
method->signature) == FALSE)
continue;
reply = method->function(connection, message, iface->user_data);
if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
if (reply != NULL)
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
if (reply == NULL)
return DBUS_HANDLER_RESULT_HANDLED;
}
if (reply == NULL)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_connection_send(connection, reply, NULL);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static DBusObjectPathVTable generic_table = {
.unregister_function = generic_unregister,
.message_function = generic_message,
};
static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
{
struct generic_data *data = NULL;
char *parent_path, *slash;
parent_path = g_strdup(child_path);
slash = strrchr(parent_path, '/');
if (!slash)
goto done;
*slash = '\0';
if (!strlen(parent_path))
goto done;
if (!dbus_connection_get_object_path_data(conn, parent_path,
(void *) &data))
goto done;
if (!data)
goto done;
g_free(data->introspect);
data->introspect = NULL;
done:
g_free(parent_path);
}
static struct generic_data *object_path_ref(DBusConnection *connection,
const char *path)
{
struct generic_data *data;
if (dbus_connection_get_object_path_data(connection, path,
(void *) &data) == TRUE) {
if (data != NULL) {
data->refcount++;
return data;
}
}
data = g_new0(struct generic_data, 1);
data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
data->refcount = 1;
if (!dbus_connection_register_object_path(connection, path,
&generic_table, data)) {
g_free(data->introspect);
g_free(data);
return NULL;
}
invalidate_parent_data(connection, path);
return data;
}
static void object_path_unref(DBusConnection *connection, const char *path)
{
struct generic_data *data = NULL;
if (dbus_connection_get_object_path_data(connection, path,
(void *) &data) == FALSE)
return;
if (data == NULL)
return;
data->refcount--;
if (data->refcount > 0)
return;
invalidate_parent_data(connection, path);
dbus_connection_unregister_object_path(connection, path);
}
static gboolean check_signal(DBusConnection *conn, const char *path,
const char *interface, const char *name,
const char **args)
{
struct generic_data *data = NULL;
struct interface_data *iface;
GDBusSignalTable *signal;
*args = NULL;
if (!dbus_connection_get_object_path_data(conn, path,
(void *) &data) || !data) {
error("dbus_connection_emit_signal: path %s isn't registered",
path);
return FALSE;
}
iface = find_interface(data->interfaces, interface);
if (!iface) {
error("dbus_connection_emit_signal: %s does not implement %s",
path, interface);
return FALSE;
}
for (signal = iface->signals; signal && signal->name; signal++) {
if (!strcmp(signal->name, name)) {
*args = signal->signature;
break;
}
}
if (!*args) {
error("No signal named %s on interface %s", name, interface);
return FALSE;
}
return TRUE;
}
static dbus_bool_t emit_signal_valist(DBusConnection *conn,
const char *path,
const char *interface,
const char *name,
int first,
va_list var_args)
{
DBusMessage *signal;
dbus_bool_t ret;
const char *signature, *args;
if (!check_signal(conn, path, interface, name, &args))
return FALSE;
signal = dbus_message_new_signal(path, interface, name);
if (!signal) {
error("Unable to allocate new %s.%s signal", interface, name);
return FALSE;
}
ret = dbus_message_append_args_valist(signal, first, var_args);
if (!ret)
goto fail;
signature = dbus_message_get_signature(signal);
if (strcmp(args, signature) != 0) {
error("%s.%s: expected signature'%s' but got '%s'",
interface, name, args, signature);
ret = FALSE;
goto fail;
}
ret = dbus_connection_send(conn, signal, NULL);
fail:
dbus_message_unref(signal);
return ret;
}
gboolean g_dbus_register_interface(DBusConnection *connection,
const char *path, const char *name,
GDBusMethodTable *methods,
GDBusSignalTable *signals,
GDBusPropertyTable *properties,
void *user_data,
GDBusDestroyFunction destroy)
{
struct generic_data *data;
struct interface_data *iface;
data = object_path_ref(connection, path);
if (data == NULL)
return FALSE;
if (find_interface(data->interfaces, name))
return FALSE;
iface = g_new0(struct interface_data, 1);
iface->name = g_strdup(name);
iface->methods = methods;
iface->signals = signals;
iface->properties = properties;
iface->user_data = user_data;
iface->destroy = destroy;
data->interfaces = g_slist_append(data->interfaces, iface);
g_free(data->introspect);
data->introspect = NULL;
return TRUE;
}
gboolean g_dbus_unregister_interface(DBusConnection *connection,
const char *path, const char *name)
{
struct generic_data *data = NULL;
struct interface_data *iface;
if (!path)
return FALSE;
if (dbus_connection_get_object_path_data(connection, path,
(void *) &data) == FALSE)
return FALSE;
if (data == NULL)
return FALSE;
iface = find_interface(data->interfaces, name);
if (!iface)
return FALSE;
data->interfaces = g_slist_remove(data->interfaces, iface);
if (iface->destroy)
iface->destroy(iface->user_data);
g_free(iface->name);
g_free(iface);
g_free(data->introspect);
data->introspect = NULL;
object_path_unref(connection, path);
return TRUE;
}
DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
const char *format, va_list args)
{
char str[1024];
vsnprintf(str, sizeof(str), format, args);
return dbus_message_new_error(message, name, str);
}
DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
const char *format, ...)
{
va_list args;
DBusMessage *reply;
va_start(args, format);
reply = g_dbus_create_error_valist(message, name, format, args);
va_end(args);
return reply;
}
DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
int type, va_list args)
{
DBusMessage *reply;
reply = dbus_message_new_method_return(message);
if (reply == NULL)
return NULL;
if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
dbus_message_unref(reply);
return NULL;
}
return reply;
}
DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
{
va_list args;
DBusMessage *reply;
va_start(args, type);
reply = g_dbus_create_reply_valist(message, type, args);
va_end(args);
return reply;
}
gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
{
dbus_bool_t result;
if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
dbus_message_set_no_reply(message, TRUE);
result = dbus_connection_send(connection, message, NULL);
dbus_message_unref(message);
return result;
}
gboolean g_dbus_send_reply_valist(DBusConnection *connection,
DBusMessage *message, int type, va_list args)
{
DBusMessage *reply;
reply = dbus_message_new_method_return(message);
if (reply == NULL)
return FALSE;
if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
dbus_message_unref(reply);
return FALSE;
}
return g_dbus_send_message(connection, reply);
}
gboolean g_dbus_send_reply(DBusConnection *connection,
DBusMessage *message, int type, ...)
{
va_list args;
gboolean result;
va_start(args, type);
result = g_dbus_send_reply_valist(connection, message, type, args);
va_end(args);
return result;
}
gboolean g_dbus_emit_signal(DBusConnection *connection,
const char *path, const char *interface,
const char *name, int type, ...)
{
va_list args;
gboolean result;
va_start(args, type);
result = emit_signal_valist(connection, path, interface,
name, type, args);
va_end(args);
return result;
}
gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
const char *path, const char *interface,
const char *name, int type, va_list args)
{
return emit_signal_valist(connection, path, interface,
name, type, args);
}

391
gdbus/watch.c Normal file
View File

@ -0,0 +1,391 @@
/*
*
* D-Bus helper library
*
* Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <dbus/dbus.h>
#include "gdbus.h"
#define info(fmt...)
#define error(fmt...)
#define debug(fmt...)
static DBusHandlerResult name_exit_filter(DBusConnection *connection,
DBusMessage *message, void *user_data);
static guint listener_id = 0;
static GSList *name_listeners = NULL;
struct name_callback {
GDBusWatchFunction conn_func;
GDBusWatchFunction disc_func;
void *user_data;
guint id;
};
struct name_data {
DBusConnection *connection;
char *name;
GSList *callbacks;
};
static struct name_data *name_data_find(DBusConnection *connection,
const char *name)
{
GSList *current;
for (current = name_listeners;
current != NULL; current = current->next) {
struct name_data *data = current->data;
if (connection != data->connection)
continue;
if (name == NULL || g_str_equal(name, data->name))
return data;
}
return NULL;
}
static struct name_callback *name_callback_find(GSList *callbacks, guint id)
{
GSList *current;
for (current = callbacks; current != NULL; current = current->next) {
struct name_callback *cb = current->data;
if (cb->id == id)
return cb;
}
return NULL;
}
static void name_data_call_and_free(struct name_data *data)
{
GSList *l;
for (l = data->callbacks; l != NULL; l = l->next) {
struct name_callback *cb = l->data;
if (cb->disc_func)
cb->disc_func(data->connection, cb->user_data);
g_free(cb);
}
g_slist_free(data->callbacks);
g_free(data->name);
g_free(data);
}
static void name_data_free(struct name_data *data)
{
GSList *l;
for (l = data->callbacks; l != NULL; l = l->next)
g_free(l->data);
g_slist_free(data->callbacks);
g_free(data->name);
g_free(data);
}
static int name_data_add(DBusConnection *connection, const char *name,
GDBusWatchFunction connect,
GDBusWatchFunction disconnect,
void *user_data, guint id)
{
int first = 1;
struct name_data *data = NULL;
struct name_callback *cb = NULL;
cb = g_new(struct name_callback, 1);
cb->conn_func = connect;
cb->disc_func = disconnect;
cb->user_data = user_data;
cb->id = id;
data = name_data_find(connection, name);
if (data) {
first = 0;
goto done;
}
data = g_new0(struct name_data, 1);
data->connection = connection;
data->name = g_strdup(name);
name_listeners = g_slist_append(name_listeners, data);
done:
data->callbacks = g_slist_append(data->callbacks, cb);
return first;
}
static void name_data_remove(DBusConnection *connection,
const char *name, guint id)
{
struct name_data *data;
struct name_callback *cb = NULL;
data = name_data_find(connection, name);
if (!data)
return;
cb = name_callback_find(data->callbacks, id);
if (cb) {
data->callbacks = g_slist_remove(data->callbacks, cb);
g_free(cb);
}
if (data->callbacks)
return;
name_listeners = g_slist_remove(name_listeners, data);
name_data_free(data);
/* Remove filter if there are no listeners left for the connection */
data = name_data_find(connection, NULL);
if (!data)
dbus_connection_remove_filter(connection,
name_exit_filter,
NULL);
}
static gboolean add_match(DBusConnection *connection, const char *name)
{
DBusError err;
char match_string[128];
snprintf(match_string, sizeof(match_string),
"interface=%s,member=NameOwnerChanged,arg0=%s",
DBUS_INTERFACE_DBUS, name);
dbus_error_init(&err);
dbus_bus_add_match(connection, match_string, &err);
if (dbus_error_is_set(&err)) {
error("Adding match rule \"%s\" failed: %s", match_string,
err.message);
dbus_error_free(&err);
return FALSE;
}
return TRUE;
}
static gboolean remove_match(DBusConnection *connection, const char *name)
{
DBusError err;
char match_string[128];
snprintf(match_string, sizeof(match_string),
"interface=%s,member=NameOwnerChanged,arg0=%s",
DBUS_INTERFACE_DBUS, name);
dbus_error_init(&err);
dbus_bus_remove_match(connection, match_string, &err);
if (dbus_error_is_set(&err)) {
error("Removing owner match rule for %s failed: %s",
name, err.message);
dbus_error_free(&err);
return FALSE;
}
return TRUE;
}
static DBusHandlerResult name_exit_filter(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
GSList *l;
struct name_data *data;
char *name, *old, *new;
int keep = 0;
if (!dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
"NameOwnerChanged"))
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (!dbus_message_get_args(message, NULL,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_STRING, &old,
DBUS_TYPE_STRING, &new,
DBUS_TYPE_INVALID)) {
error("Invalid arguments for NameOwnerChanged signal");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
data = name_data_find(connection, name);
if (!data) {
error("Got NameOwnerChanged signal for %s which has no listeners", name);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
for (l = data->callbacks; l != NULL; l = l->next) {
struct name_callback *cb = l->data;
if (*new == '\0') {
if (cb->disc_func)
cb->disc_func(connection, cb->user_data);
} else {
if (cb->conn_func)
cb->conn_func(connection, cb->user_data);
}
if (cb->conn_func && cb->disc_func)
keep = 1;
}
if (keep)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
name_listeners = g_slist_remove(name_listeners, data);
name_data_free(data);
/* Remove filter if there no listener left for the connection */
data = name_data_find(connection, NULL);
if (!data)
dbus_connection_remove_filter(connection, name_exit_filter,
NULL);
remove_match(connection, name);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
GDBusWatchFunction connect,
GDBusWatchFunction disconnect,
void *user_data, GDBusDestroyFunction destroy)
{
int first;
if (!name_data_find(connection, NULL)) {
if (!dbus_connection_add_filter(connection,
name_exit_filter, NULL, NULL)) {
error("dbus_connection_add_filter() failed");
return 0;
}
}
listener_id++;
first = name_data_add(connection, name, connect, disconnect,
user_data, listener_id);
/* The filter is already added if this is not the first callback
* registration for the name */
if (!first)
return listener_id;
if (name) {
debug("name_listener_add(%s)", name);
if (!add_match(connection, name)) {
name_data_remove(connection, name, listener_id);
return 0;
}
}
return listener_id;
}
guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
GDBusWatchFunction func,
void *user_data, GDBusDestroyFunction destroy)
{
return g_dbus_add_service_watch(connection, name, NULL, func,
user_data, destroy);
}
guint g_dbus_add_signal_watch(DBusConnection *connection,
const char *rule, GDBusSignalFunction function,
void *user_data, GDBusDestroyFunction destroy)
{
return 0;
}
gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
{
struct name_data *data;
struct name_callback *cb;
GSList *ldata, *lcb;
if (id == 0)
return FALSE;
for (ldata = name_listeners; ldata; ldata = ldata->next) {
data = ldata->data;
for (lcb = data->callbacks; lcb; lcb = lcb->next) {
cb = lcb->data;
if (cb->id == id)
goto remove;
}
}
return FALSE;
remove:
data->callbacks = g_slist_remove(data->callbacks, cb);
g_free(cb);
/* Don't remove the filter if other callbacks exist */
if (data->callbacks)
return TRUE;
if (data->name) {
if (!remove_match(data->connection, data->name))
return FALSE;
}
name_listeners = g_slist_remove(name_listeners, data);
name_data_free(data);
/* Remove filter if there are no listeners left for the connection */
data = name_data_find(connection, NULL);
if (!data)
dbus_connection_remove_filter(connection, name_exit_filter,
NULL);
return TRUE;
}
void g_dbus_remove_all_watches(DBusConnection *connection)
{
struct name_data *data;
while ((data = name_data_find(connection, NULL))) {
name_listeners = g_slist_remove(name_listeners, data);
name_data_call_and_free(data);
}
dbus_connection_remove_filter(connection, name_exit_filter, NULL);
}