Add copy of libgdbus helper for easier adoption
authorMarcel Holtmann <marcel@holtmann.org>
Tue, 12 Aug 2008 23:13:59 +0000 (01:13 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 12 Aug 2008 23:13:59 +0000 (01:13 +0200)
Makefile.am
configure.ac
gdbus/Makefile.am [new file with mode: 0644]
gdbus/gdbus.h [new file with mode: 0644]
gdbus/mainloop.c [new file with mode: 0644]
gdbus/object.c [new file with mode: 0644]
gdbus/watch.c [new file with mode: 0644]
plugins/Makefile.am
src/Makefile.am
src/main.c

index fe127cd..66e0a0b 100644 (file)
@@ -1,5 +1,5 @@
 
-SUBDIRS = include src doc test plugins scripts
+SUBDIRS = gdbus include src doc test plugins scripts
 
 pkgconfigdir = $(libdir)/pkgconfig
 
index 096711f..e5d9f09 100644 (file)
@@ -59,16 +59,14 @@ PKG_CHECK_MODULES(GMODULE, gmodule-2.0, dummy=yes,
 AC_SUBST(GMODULE_CFLAGS)
 AC_SUBST(GMODULE_LIBS)
 
-PKG_CHECK_MODULES(GDBUS, gdbus, dummy=yes,
-                               AC_MSG_ERROR(libgdbus is required))
-AC_SUBST(GDBUS_CFLAGS)
-AC_SUBST(GDBUS_LIBS)
-
 PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.0, dummy=yes,
                                AC_MSG_ERROR(libdbus is required))
 AC_SUBST(DBUS_CFLAGS)
 AC_SUBST(DBUS_LIBS)
 
+AC_SUBST([GDBUS_CFLAGS], ['$(DBUS_CFLAGS) -I$(top_srcdir)/gdbus'])
+AC_SUBST([GDBUS_LIBS], ['$(top_builddir)/gdbus/libgdbus.a $(DBUS_LIBS)'])
+
 PKG_CHECK_MODULES(HAL, hal >= 0.5.8, hal_found=yes, hal_found=no)
 AC_SUBST(HAL_CFLAGS)
 AC_SUBST(HAL_LIBS)
@@ -83,6 +81,6 @@ PKG_CHECK_MODULES(SQLITE, sqlite3, sqlite_found=yes, sqlite_found=no)
 AC_SUBST(SQLITE_CFLAGS)
 AC_SUBST(SQLITE_LIBS)
 
-AC_OUTPUT(Makefile include/Makefile src/Makefile doc/Makefile
-                       test/Makefile plugins/Makefile scripts/Makefile
-                       scripts/connman src/connman.service connman.pc)
+AC_OUTPUT(Makefile gdbus/Makefile include/Makefile src/Makefile doc/Makefile
+                               test/Makefile plugins/Makefile scripts/Makefile
+                               scripts/connman src/connman.service connman.pc)
diff --git a/gdbus/Makefile.am b/gdbus/Makefile.am
new file mode 100644 (file)
index 0000000..756b2fa
--- /dev/null
@@ -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 (file)
index 0000000..b44bc86
--- /dev/null
@@ -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 (file)
index 0000000..b50d83c
--- /dev/null
@@ -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 (file)
index 0000000..a417ab9
--- /dev/null
@@ -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 (file)
index 0000000..fb0bef3
--- /dev/null
@@ -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);
+}
index 96b59b3..3aee132 100644 (file)
@@ -13,7 +13,7 @@ wifi_la_SOURCES = wifi.c supplicant.h supplicant.c
 bluetooth_la_SOURCES = bluetooth.c
 
 dhclient_la_SOURCES = dhclient.c
-dhclient_la_CFLAGS = @GDBUS_CFLAGS@ -DDHCLIENT=\"@DHCLIENT@\" \
+dhclient_la_CFLAGS = @GLIB_CFLAGS@ @GDBUS_CFLAGS@ -DDHCLIENT=\"@DHCLIENT@\" \
                -DSTATEDIR=\""$(statedir)"\" -DSCRIPTDIR=\""$(scriptdir)"\"
 
 ipv4_la_SOURCES = ipv4.c
@@ -49,7 +49,7 @@ else
 scriptdir = $(libdir)/connman/scripts
 endif
 
-AM_CFLAGS = @GDBUS_CFLAGS@
+AM_CFLAGS = @GLIB_CFLAGS@ @GDBUS_CFLAGS@
 
 INCLUDES = -I$(top_builddir)/include
 
index f959379..9518f28 100644 (file)
@@ -14,7 +14,7 @@ sbin_PROGRAMS = connmand
 connmand_SOURCES = main.c connman.h log.c plugin.c profile.c element.c \
                                        storage.c manager.c agent.c rtnl.c
 
-connmand_LDADD = @SQLITE_LIBS@ @GDBUS_LIBS@ @GMODULE_LIBS@ @GTHREAD_LIBS@
+connmand_LDADD = @GDBUS_LIBS@ @GLIB_LIBS@ @GMODULE_LIBS@ @GTHREAD_LIBS@
 
 connmand_LDFLAGS = -Wl,--version-script=connman.ver
 
@@ -32,7 +32,7 @@ else
 plugindir = $(libdir)/connman/plugins
 endif
 
-AM_CFLAGS = @GTHREAD_CFLAGS@ @GMODULE_CFLAGS@ @GDBUS_CFLAGS@ @SQLITE_CFLAGS@ \
+AM_CFLAGS = @GTHREAD_CFLAGS@ @GMODULE_CFLAGS@ @GLIB_CFLAGS@ @GDBUS_CFLAGS@ \
                        -DSTATEDIR=\""$(statedir)"\" \
                        -DSTORAGEDIR=\""$(storagedir)\"" \
                        -DPLUGINDIR=\""$(plugindir)"\"
index 77dff01..87efa00 100644 (file)
@@ -175,7 +175,7 @@ int main(int argc, char *argv[])
 
        __connman_log_cleanup();
 
-       g_dbus_cleanup_connection(conn);
+       dbus_connection_unref(conn);
 
        g_main_loop_unref(main_loop);