diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2008-08-13 01:13:59 +0200 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2008-08-13 01:13:59 +0200 |
commit | e3a4699f34f751d47d8fd8e5a488e4b95343732f (patch) | |
tree | e1b56caf8556c11ef8c914a15c87b3bad7621b85 /gdbus | |
parent | b7b518712d302a7c372e62582c3b60af4020f13c (diff) | |
download | connman-e3a4699f34f751d47d8fd8e5a488e4b95343732f.tar.gz connman-e3a4699f34f751d47d8fd8e5a488e4b95343732f.tar.bz2 connman-e3a4699f34f751d47d8fd8e5a488e4b95343732f.zip |
Add copy of libgdbus helper for easier adoption
Diffstat (limited to 'gdbus')
-rw-r--r-- | gdbus/Makefile.am | 8 | ||||
-rw-r--r-- | gdbus/gdbus.h | 133 | ||||
-rw-r--r-- | gdbus/mainloop.c | 305 | ||||
-rw-r--r-- | gdbus/object.c | 641 | ||||
-rw-r--r-- | gdbus/watch.c | 353 |
5 files changed, 1440 insertions, 0 deletions
diff --git a/gdbus/Makefile.am b/gdbus/Makefile.am new file mode 100644 index 00000000..756b2fa2 --- /dev/null +++ b/gdbus/Makefile.am @@ -0,0 +1,8 @@ + +noinst_LIBRARIES = libgdbus.a + +libgdbus_a_SOURCES = gdbus.h mainloop.c object.c watch.c + +AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ + +MAINTAINERCLEANFILES = Makefile.in diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h new file mode 100644 index 00000000..b44bc867 --- /dev/null +++ b/gdbus/gdbus.h @@ -0,0 +1,133 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2008 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) (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_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_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 */ diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c new file mode 100644 index 00000000..b50d83c0 --- /dev/null +++ b/gdbus/mainloop.c @@ -0,0 +1,305 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2008 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 { + void (*disconnect_cb)(void *); + 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"); + dbus_connection_unref(conn); + dc_data->disconnect_cb(dc_data->user_data); + } + + 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_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; +} diff --git a/gdbus/object.c b/gdbus/object.c new file mode 100644 index 00000000..a417ab98 --- /dev/null +++ b/gdbus/object.c @@ -0,0 +1,641 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2008 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 len, struct_level, dict_level; + 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; + + 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 (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) +{ + return dbus_message_new_error(message, name, format); +} + +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; + + 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); +} diff --git a/gdbus/watch.c b/gdbus/watch.c new file mode 100644 index 00000000..fb0bef3f --- /dev/null +++ b/gdbus/watch.c @@ -0,0 +1,353 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2008 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 guint listener_id = 0; +static GSList *name_listeners = NULL; + +struct name_callback { + GDBusWatchFunction 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 (name == NULL && data->name == NULL) { + if (connection == data->connection) + return data; + } else { + if (strcmp(name, data->name) == 0) + return data; + } + } + + return NULL; +} + +static struct name_callback *name_callback_find(GSList *callbacks, + GDBusWatchFunction func, void *user_data) +{ + GSList *current; + + for (current = callbacks; current != NULL; current = current->next) { + struct name_callback *cb = current->data; + if (cb->func == func && cb->user_data == user_data) + 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->func) + cb->func(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 func, 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->func = func; + 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, GDBusWatchFunction func, void *user_data) +{ + struct name_data *data; + struct name_callback *cb = NULL; + + data = name_data_find(connection, name); + if (!data) + return; + + cb = name_callback_find(data->callbacks, func, user_data); + if (cb) { + data->callbacks = g_slist_remove(data->callbacks, cb); + g_free(cb); + } + + if (!data->callbacks) { + name_listeners = g_slist_remove(name_listeners, data); + name_data_free(data); + } +} + +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; + + 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; + } + + /* We are not interested of service creations */ + if (*new != '\0') + 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; + cb->func(cb->user_data); + } + + name_listeners = g_slist_remove(name_listeners, data); + name_data_free(data); + + remove_match(connection, name); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +guint g_dbus_add_disconnect_watch(DBusConnection *connection, + const char *name, + GDBusWatchFunction func, + void *user_data, GDBusDestroyFunction destroy) +{ + int first; + + if (!listener_id) { + 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, func, 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, func, user_data); + return 0; + } + } + + return listener_id; +} + +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); + + return TRUE; +} + +void g_dbus_remove_all_watches(DBusConnection *connection) +{ + struct name_data *data; + + data = name_data_find(connection, NULL); + if (!data) { + error("name_listener_indicate_disconnect: no listener found"); + return; + } + + debug("name_listener_indicate_disconnect"); + + name_data_call_and_free(data); +} |