-#include "config.h"
+/*
+ * Copyright 2008-2011 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
#include <stdio.h>
-#define DBUS_API_SUBJECT_TO_CHANGE
-#include <dbind/dbind.h>
-#include <dbind/dbind-any.h>
-#include <glib.h>
#include <stdarg.h>
+#include <sys/time.h>
+#include <string.h>
+#include <glib.h>
+
+#include "config.h"
+#include "dbind/dbind.h"
+#include "atspi/atspi-gmain.h"
+
+static int dbind_timeout = -1;
/*
* FIXME: compare types - to ensure they match &
* do dynamic padding of structures etc.
*/
-struct _DBindContext {
- DBusConnection *cnx;
-};
+/*---------------------------------------------------------------------------*/
-DBindContext *
-dbind_create_context (DBusBusType type, DBusError *opt_error)
+typedef struct _SpiReentrantCallClosure
{
- DBindContext *ctx = NULL;
- DBusConnection *cnx;
- DBusError *err, real_err;
-
- if (opt_error)
- err = opt_error;
- else {
- dbus_error_init (&real_err);
- err = &real_err;
- }
-
- cnx = dbus_bus_get (DBUS_BUS_SESSION, err);
- if (!cnx)
- goto out;
-
- ctx = g_new0 (DBindContext, 1);
- ctx->cnx = cnx;
-
-out:
- if (err == &real_err)
- dbus_error_free (err);
-
- return ctx;
-}
+ DBusMessage *reply;
+} SpiReentrantCallClosure;
-void
-dbind_context_free (DBindContext *ctx)
-{
- if (!ctx)
- return;
- dbus_connection_unref (ctx->cnx);
- g_free (ctx);
-}
-
-dbus_bool_t
-dbind_context_method_call (DBindContext *ctx,
- const char *bus_name,
- const char *path,
- const char *interface,
- const char *method,
- DBusError *opt_error,
- const char *arg_types,
- ...)
+static void
+set_reply (DBusPendingCall * pending, void *user_data)
{
- dbus_bool_t success;
- va_list args;
+ SpiReentrantCallClosure* closure = (SpiReentrantCallClosure *) user_data;
- va_start (args, arg_types);
-
- success = dbind_connection_method_call_va
- (ctx->cnx, bus_name, path, interface, method, opt_error, arg_types, args);
-
- va_end (args);
-
- return success;
+ closure->reply = dbus_pending_call_steal_reply (pending);
+ dbus_pending_call_unref (pending);
}
-dbus_bool_t
-dbind_connection_method_call (DBusConnection *cnx,
- const char *bus_name,
- const char *path,
- const char *interface,
- const char *method,
- DBusError *opt_error,
- const char *arg_types,
- ...)
+static gint
+time_elapsed (struct timeval *origin)
{
- dbus_bool_t success;
- va_list args;
-
- va_start (args, arg_types);
-
- success = dbind_connection_method_call_va
- (cnx, bus_name, path, interface, method, opt_error, arg_types, args);
+ struct timeval tv;
- va_end (args);
-
- return success;
+ gettimeofday (&tv, NULL);
+ return (tv.tv_sec - origin->tv_sec) * 1000 + (tv.tv_usec - origin->tv_usec) / 1000;
}
-static void set_reply (DBusPendingCall *pending, void *user_data)
+DBusMessage *
+dbind_send_and_allow_reentry (DBusConnection * bus, DBusMessage * message, DBusError *error)
{
- void **replyptr = (void **)user_data;
+ DBusPendingCall *pending;
+ SpiReentrantCallClosure *closure;
+ const char *unique_name = dbus_bus_get_unique_name (bus);
+ const char *destination = dbus_message_get_destination (message);
+ struct timeval tv;
+ DBusMessage *ret;
+ static gboolean in_dispatch = FALSE;
+
+ if (unique_name && destination &&
+ strcmp (destination, unique_name) != 0)
+ {
+ ret = dbus_connection_send_with_reply_and_block (bus, message,
+ dbind_timeout, error);
+ if (g_main_depth () == 0 && !in_dispatch)
+ {
+ in_dispatch = TRUE;
+ while (dbus_connection_dispatch (bus) == DBUS_DISPATCH_DATA_REMAINS);
+ in_dispatch = FALSE;
+ }
+ return ret;
+ }
- *replyptr = dbus_pending_call_steal_reply (pending);
-}
+ closure = g_new0 (SpiReentrantCallClosure, 1);
+ closure->reply = NULL;
+ if (!dbus_connection_send_with_reply (bus, message, &pending, dbind_timeout)
+ || !pending)
+ {
+ g_free (closure);
+ return NULL;
+ }
+ dbus_pending_call_set_notify (pending, set_reply, (void *) closure, g_free);
-static DBusMessage *
-send_and_allow_reentry (DBusConnection *bus, DBusMessage *message, int timeout, DBusError *error)
-{
- DBusPendingCall *pending;
- DBusMessage *reply = NULL;
-
- if (!dbus_connection_send_with_reply (bus, message, &pending, timeout))
- {
- return NULL;
- }
- dbus_pending_call_set_notify (pending, set_reply, (void *)&reply, NULL);
- while (!reply)
- {
- if (!dbus_connection_read_write_dispatch (bus, timeout)) return NULL;
- }
- return reply;
+ closure->reply = NULL;
+ gettimeofday (&tv, NULL);
+ dbus_pending_call_ref (pending);
+ while (!closure->reply)
+ {
+ if (!dbus_connection_read_write_dispatch (bus, dbind_timeout))
+ {
+ //dbus_pending_call_set_notify (pending, NULL, NULL, NULL);
+ dbus_pending_call_cancel (pending);
+ dbus_pending_call_unref (pending);
+ return NULL;
+ }
+ if (time_elapsed (&tv) > dbind_timeout)
+ {
+ //dbus_pending_call_set_notify (pending, NULL, NULL, NULL);
+ dbus_pending_call_cancel (pending);
+ dbus_pending_call_unref (pending);
+ dbus_set_error_const (error, "org.freedesktop.DBus.Error.NoReply",
+ "timeout from dbind");
+ return NULL;
+ }
+ }
+
+ ret = closure->reply;
+ dbus_pending_call_unref (pending);
+ return ret;
}
dbus_bool_t
-dbind_connection_method_call_va (DBusConnection *cnx,
- const char *bus_name,
- const char *path,
- const char *interface,
- const char *method,
- DBusError *opt_error,
- const char *arg_types,
- va_list args)
+dbind_method_call_reentrant_va (DBusConnection *cnx,
+ const char *bus_name,
+ const char *path,
+ const char *interface,
+ const char *method,
+ DBusError *opt_error,
+ const char *arg_types,
+ va_list args)
{
dbus_bool_t success = FALSE;
DBusMessage *msg = NULL, *reply = NULL;
+ DBusMessageIter iter;
DBusError *err, real_err;
- char *p;
- char *dest;
+ const char *p;
+ va_list args_demarshal;
+
+ dbus_error_init (&real_err);
+ va_copy (args_demarshal, args);
if (opt_error)
err = opt_error;
else {
- dbus_error_init (&real_err);
err = &real_err;
}
msg = dbus_message_new_method_call (bus_name, path, interface, method);
if (!msg)
goto out;
- dbus_message_set_auto_start (msg, TRUE);
- /* marshal */
- p = (char *)arg_types;
- {
- DBusMessageIter iter;
-
- dbus_message_iter_init_append (msg, &iter);
- /* special case base-types since we need to walk the stack worse-luck */
- for (;*p != '\0' && *p != '=';) {
- int intarg;
- void *ptrarg;
- double doublearg;
- dbus_int64_t int64arg;
- void *arg = NULL;
-
- switch (*p) {
- case DBUS_TYPE_BYTE:
- case DBUS_TYPE_BOOLEAN:
- case DBUS_TYPE_INT16:
- case DBUS_TYPE_UINT16:
- case DBUS_TYPE_INT32:
- case DBUS_TYPE_UINT32:
- intarg = va_arg (args, int);
- arg = &intarg;
- break;
- case DBUS_TYPE_INT64:
- case DBUS_TYPE_UINT64:
- int64arg = va_arg (args, dbus_int64_t);
- arg = &int64arg;
- break;
- case DBUS_TYPE_DOUBLE:
- doublearg = va_arg (args, double);
- arg = &doublearg;
- break;
- /* ptr types */
- case DBUS_TYPE_STRING:
- case DBUS_TYPE_OBJECT_PATH:
- case DBUS_TYPE_SIGNATURE:
- case DBUS_TYPE_ARRAY:
- case DBUS_TYPE_DICT_ENTRY:
- ptrarg = va_arg (args, void *);
- arg = &ptrarg;
- break;
- case DBUS_STRUCT_BEGIN_CHAR:
- ptrarg = va_arg (args, void *);
- arg = ptrarg;
- break;
-
- case DBUS_TYPE_VARIANT:
- fprintf (stderr, "No variant support yet - very toolkit specific\n");
- ptrarg = va_arg (args, void *);
- arg = &ptrarg;
- break;
- default:
- fprintf (stderr, "Unknown / invalid arg type %c\n", *p);
- break;
- }
- if (arg != NULL)
- dbind_any_marshal (&iter, &p, &arg);
- }
- }
+ p = arg_types;
+ dbus_message_iter_init_append (msg, &iter);
+ dbind_any_marshal_va (&iter, &p, args);
- dest = dbus_message_get_destination(msg);
- if (!dest)
- goto out;
- if (!strcmp (dbus_bus_get_unique_name(cnx), dest))
- {
- /* Can't use dbus_message_send_with_reply_and_block because it will
- * not pass messages on to the provider side, causing deadlock */
- reply = send_and_allow_reentry (cnx, msg, -1, err);
- }
- else
- {
- reply = dbus_connection_send_with_reply_and_block (cnx, msg, -1, err);
- }
+ reply = dbind_send_and_allow_reentry (cnx, msg, err);
if (!reply)
goto out;
if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
{
- char *name = dbus_message_get_error_name (reply);
- dbus_set_error (err, name, g_strdup (""));
goto out;
}
/* demarshal */
if (p[0] == '=' && p[1] == '>')
{
DBusMessageIter iter;
- p += 2;
dbus_message_iter_init (reply, &iter);
- for (;*p != '\0';) {
- void *arg = va_arg (args, void *);
- dbind_any_demarshal (&iter, &p, &arg);
- }
+ if (strcmp (p + 2, dbus_message_get_signature (reply)) != 0)
+ {
+ g_warning ("dbind: Call to \"%s\" returned signature %s; expected %s",
+ method, dbus_message_get_signature (reply), p + 2);
+ if (opt_error)
+ dbus_set_error (opt_error, DBUS_ERROR_INVALID_ARGS,
+ "Call to \"%s\" returned signature %s; expected %s",
+ method, dbus_message_get_signature (reply),
+ p + 2);
+ goto out;
+ }
+ p = arg_types;
+ dbind_any_demarshal_va (&iter, &p, args_demarshal);
}
success = TRUE;
if (reply)
dbus_message_unref (reply);
- if (err == &real_err)
- dbus_error_free (err);
+ if (dbus_error_is_set (&real_err))
+ dbus_error_free (&real_err);
+
+ va_end (args_demarshal);
+ return success;
+}
+
+/**
+ * dbind_method_call_reentrant:
+ *
+ * @cnx: A D-Bus Connection used to make the method call.
+ * @bus_name: The D-Bus bus name of the program where the method call should
+ * be made.
+ * @path: The D-Bus object path that should handle the method.
+ * @interface: The D-Bus interface used to scope the method name.
+ * @method: Method to be invoked.
+ * @opt_error: D-Bus error.
+ * @arg_types: Variable length arguments interleaving D-Bus argument types
+ * and pointers to argument data.
+ *
+ * Makes a D-Bus method call using the supplied location data, method name and
+ * argument data.This function is re-entrant. It continuously reads from the D-Bus
+ * bus and dispatches messages until a reply has been recieved.
+ **/
+dbus_bool_t
+dbind_method_call_reentrant (DBusConnection *cnx,
+ const char *bus_name,
+ const char *path,
+ const char *interface,
+ const char *method,
+ DBusError *opt_error,
+ const char *arg_types,
+ ...)
+{
+ dbus_bool_t success = FALSE;
+ va_list args;
+
+ va_start (args, arg_types);
+ success = dbind_method_call_reentrant_va (cnx,
+ bus_name,
+ path,
+ interface,
+ method,
+ opt_error,
+ arg_types,
+ args);
+ va_end (args);
+
+ return success;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* TODO: opt_error is unused; should be removed */
+dbus_bool_t
+dbind_emit_signal_va (DBusConnection *cnx,
+ const char *path,
+ const char *interface,
+ const char *signal,
+ DBusError *opt_error,
+ const char *arg_types,
+ va_list args)
+{
+ dbus_bool_t success = FALSE;
+ DBusMessage *msg = NULL;
+ DBusMessageIter iter;
+ const char *p;
+
+ msg = dbus_message_new_signal (path, interface, signal);
+ if (!msg)
+ goto out;
+
+ p = arg_types;
+ dbus_message_iter_init_append (msg, &iter);
+ dbind_any_marshal_va (&iter, &p, args);
+
+ if (!dbus_connection_send (cnx, msg, NULL))
+ goto out;
+
+ success = TRUE;
+out:
+
+ if (msg)
+ dbus_message_unref (msg);
return success;
}
+/**
+ * dbind_emit_signal:
+ *
+ * @cnx: A D-Bus Connection used to make the method call.
+ * @path: The D-Bus object path that this signal is emitted from.
+ * @interface: The D-Bus interface used to scope the method name.
+ * @signal: Name of signal to emit.
+ * @opt_error: D-Bus error.
+ * @arg_types: Variable length arguments interleaving D-Bus argument types
+ * and pointers to argument data.
+ *
+ * Emits a D-Bus signal using the supplied signal name and argument data.
+ **/
+dbus_bool_t
+dbind_emit_signal (DBusConnection *cnx,
+ const char *path,
+ const char *interface,
+ const char *signal,
+ DBusError *opt_error,
+ const char *arg_types,
+ ...)
+{
+ dbus_bool_t success = FALSE;
+ va_list args;
+
+ va_start (args, arg_types);
+ success = dbind_emit_signal_va (cnx, path, interface, signal, opt_error, arg_types, args);
+ va_end (args);
+
+ return success;
+}
+void
+dbind_set_timeout (int timeout)
+{
+ dbind_timeout = timeout;
+}
+
+
+/*END------------------------------------------------------------------------*/