summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2011-03-18 11:52:36 -0400
committerDavid Zeuthen <davidz@redhat.com>2011-03-18 11:52:36 -0400
commit0abd81c74b86820f4728f2ad1414ed3a317ca0f5 (patch)
treef60e9ada83b9b6a1829a254cd8c606761f174686
parentf3658eb6b424bab10d349db04f491c6a81615122 (diff)
Add a way to authorize and run method implementations in a thread
Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r--doc/gen-sections.txt9
-rw-r--r--src/Makefile.am2
-rw-r--r--src/codegen.py57
-rw-r--r--src/gdbuscodegen-enumtypes.c.template1
-rw-r--r--src/gdbuscodegen-marshal.list1
-rw-r--r--src/gdbusinterfacestub.c540
-rw-r--r--src/gdbusinterfacestub.h61
-rw-r--r--src/gdbusobjectmanagerserver.c91
-rw-r--r--src/org.project.xml13
-rw-r--r--src/test.c213
10 files changed, 848 insertions, 140 deletions
diff --git a/doc/gen-sections.txt b/doc/gen-sections.txt
index 847d34b..d2ff21e 100644
--- a/doc/gen-sections.txt
+++ b/doc/gen-sections.txt
@@ -27,8 +27,15 @@ GDBusInterfaceStub
GDBusInterfaceStubClass
g_dbus_interface_stub_flush
g_dbus_interface_stub_get_info
+g_dbus_interface_stub_get_vtable
g_dbus_interface_stub_get_properties
g_dbus_interface_stub_export
+g_dbus_interface_stub_unexport
+g_dbus_interface_stub_get_connection
+g_dbus_interface_stub_get_object_path
+GDBusInterfaceStubFlags
+g_dbus_interface_stub_get_flags
+g_dbus_interface_stub_set_flags
<SUBSECTION Standard>
G_DBUS_INTERFACE_STUB
G_IS_DBUS_INTERFACE_STUB
@@ -39,6 +46,8 @@ G_IS_DBUS_INTERFACE_STUB_CLASS
G_DBUS_INTERFACE_STUB_GET_CLASS
<SUBSECTION Private>
GDBusInterfaceStubPrivate
+G_TYPE_DBUS_INTERFACE_STUB_FLAGS
+g_dbus_interface_stub_flags_get_type
</SECTION>
<SECTION>
diff --git a/src/Makefile.am b/src/Makefile.am
index 60d66de..f9467c6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,7 +28,7 @@ BUILT_SOURCES = \
gdbuscodegen-enumtypes.h gdbuscodegen-enumtypes.c \
$(NULL)
-enumhfiles=gdbusobjectmanagerclient.h gdbusinterface.h
+enumhfiles=gdbusobjectmanagerclient.h gdbusinterfacestub.h gdbusinterface.h
gdbuscodegen-enumtypes.h: $(enumhfiles) gdbuscodegen-enumtypes.h.template
( top_builddir=`cd $(top_builddir) && pwd`; \
diff --git a/src/codegen.py b/src/codegen.py
index ecd9bbe..dc7f793 100644
--- a/src/codegen.py
+++ b/src/codegen.py
@@ -1327,8 +1327,6 @@ class CodeGenerator:
' GValueArray *properties;\n'
' GList *changed_properties;\n'
' GSource *changed_properties_idle_source;\n'
- ' GDBusConnection *connection;\n'
- ' gchar *object_path;\n'
' GMainContext *context;\n'
'};\n'
'\n'%i.camel_name)
@@ -1490,6 +1488,14 @@ class CodeGenerator:
self.c.write('}\n'
'\n')
+ self.c.write('static GDBusInterfaceVTable *\n'
+ '%s_stub_dbus_interface_get_vtable (GDBusInterfaceStub *stub)\n'
+ '{\n'
+ ' return (GDBusInterfaceVTable *) &_%s_stub_vtable;\n'
+ %(i.name_lower, i.name_lower))
+ self.c.write('}\n'
+ '\n')
+
self.c.write('static GVariant *\n'
'%s_stub_dbus_interface_get_properties (GDBusInterfaceStub *_stub)\n'
'{\n'
@@ -1507,7 +1513,7 @@ class CodeGenerator:
' if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)\n'
' {\n'
' GVariant *value;\n'
- ' value = _%s_stub_handle_get_property (stub->priv->connection, NULL, stub->priv->object_path, "%s", info->name, NULL, stub);\n'
+ ' value = _%s_stub_handle_get_property (g_dbus_interface_stub_get_connection (G_DBUS_INTERFACE_STUB (stub)), NULL, g_dbus_interface_stub_get_object_path (G_DBUS_INTERFACE_STUB (stub)), "%s", info->name, NULL, stub);\n'
' if (value != NULL)\n'
' {\n'
' if (g_variant_is_floating (value))\n'
@@ -1544,33 +1550,6 @@ class CodeGenerator:
self.c.write('}\n'
'\n')
- self.c.write('static void\n'
- '_%s_on_object_unregistered (%sStub *stub)\n'
- '{\n'
- ' stub->priv->connection = NULL;\n'
- ' g_free (stub->priv->object_path);\n'
- ' stub->priv->object_path = NULL;\n'
- '}\n'
- '\n'
- %(i.name_lower, i.camel_name))
-
- self.c.write('static guint\n'
- '%s_stub_dbus_interface_export (GDBusInterfaceStub *_stub,'
- ' GDBusConnection *connection,\n'
- ' const gchar *object_path,\n'
- ' GError **error)\n'
- '{\n'
- ' %sStub *stub = %s%s_STUB (_stub);\n'
- %(i.name_lower, i.camel_name, i.ns_upper, i.name_upper))
- self.c.write(' stub->priv->connection = connection;\n')
- self.c.write(' stub->priv->object_path = g_strdup (object_path);\n')
- self.c.write(' stub->priv->context = g_main_context_get_thread_default ();\n')
- self.c.write(' if (stub->priv->context != NULL)\n')
- self.c.write(' g_main_context_ref (stub->priv->context);\n')
- self.c.write(' return g_dbus_connection_register_object (connection, object_path, %s_interface_info (), &_%s_stub_vtable, stub, (GDestroyNotify) _%s_on_object_unregistered, error);\n'%(i.name_lower, i.name_lower, i.name_lower))
- self.c.write('}\n'
- '\n')
-
for s in i.signals:
self.c.write('static void\n'
'_%s_on_signal_%s (\n'
@@ -1580,11 +1559,12 @@ class CodeGenerator:
self.c.write(')\n'
'{\n'
' %sStub *stub = %s%s_STUB (object);\n'
+ ' GDBusConnection *connection = g_dbus_interface_stub_get_connection (G_DBUS_INTERFACE_STUB (stub));\n'
%(i.camel_name, i.ns_upper, i.name_upper))
- self.c.write(' if (stub->priv->connection == NULL)\n'
+ self.c.write(' if (connection == NULL)\n'
' return;\n'
- ' g_dbus_connection_emit_signal (stub->priv->connection,\n'
- ' NULL, stub->priv->object_path, "%s", "%s",\n'
+ ' g_dbus_connection_emit_signal (connection,\n'
+ ' NULL, g_dbus_interface_stub_get_object_path (G_DBUS_INTERFACE_STUB (stub)), "%s", "%s",\n'
' g_variant_new ("('
%(i.name, s.name))
for a in s.args:
@@ -1660,8 +1640,8 @@ class CodeGenerator:
' g_variant_builder_add (&builder, "{sv}", cp->info->parent_struct.name, variant);\n'
' g_variant_unref (variant);\n'
' }\n'
- ' g_dbus_connection_emit_signal (stub->priv->connection,\n'
- ' NULL, stub->priv->object_path,\n'
+ ' g_dbus_connection_emit_signal (g_dbus_interface_stub_get_connection (G_DBUS_INTERFACE_STUB (stub)),\n'
+ ' NULL, g_dbus_interface_stub_get_object_path (G_DBUS_INTERFACE_STUB (stub)),\n'
' "org.freedesktop.DBus.Properties",\n'
' "PropertiesChanged",\n'
' g_variant_new ("(sa{sv}as)",\n'
@@ -1726,7 +1706,7 @@ class CodeGenerator:
' {\n'
' g_value_copy (value, &stub->priv->properties->values[prop_id - 1]);\n'
' g_object_notify_by_pspec (object, pspec);\n'
- ' if (stub->priv->connection != NULL)\n'
+ ' if (g_dbus_interface_stub_get_connection (G_DBUS_INTERFACE_STUB (stub)) != NULL)\n'
' _%s_schedule_emit_changed (stub, _%s_property_info_pointers[prop_id - 1], pspec, value);\n'
' }\n'
%(i.camel_name, i.ns_upper, i.name_upper, len(i.properties), i.name_lower, i.name_lower))
@@ -1738,6 +1718,9 @@ class CodeGenerator:
'{\n'
' stub->priv = G_TYPE_INSTANCE_GET_PRIVATE (stub, %sTYPE_%s_STUB, %sStubPrivate);\n'
%(i.name_lower, i.camel_name, i.ns_upper, i.name_upper, i.camel_name))
+ self.c.write(' stub->priv->context = g_main_context_get_thread_default ();\n')
+ self.c.write(' if (stub->priv->context != NULL)\n')
+ self.c.write(' g_main_context_ref (stub->priv->context);\n')
if len(i.properties) > 0:
self.c.write(' stub->priv->properties = g_value_array_new (%d);\n'%(len(i.properties)))
n = 0
@@ -1768,7 +1751,7 @@ class CodeGenerator:
self.c.write(' stub_class->get_info = %s_stub_dbus_interface_get_info;\n'%(i.name_lower))
self.c.write(' stub_class->get_properties = %s_stub_dbus_interface_get_properties;\n'%(i.name_lower))
self.c.write(' stub_class->flush = %s_stub_dbus_interface_flush;\n'%(i.name_lower))
- self.c.write(' stub_class->export = %s_stub_dbus_interface_export;\n'%(i.name_lower))
+ self.c.write(' stub_class->get_vtable = %s_stub_dbus_interface_get_vtable;\n'%(i.name_lower))
self.c.write('}\n'
'\n')
diff --git a/src/gdbuscodegen-enumtypes.c.template b/src/gdbuscodegen-enumtypes.c.template
index 6d0b933..db619c1 100644
--- a/src/gdbuscodegen-enumtypes.c.template
+++ b/src/gdbuscodegen-enumtypes.c.template
@@ -2,6 +2,7 @@
#include "gdbuscodegen-enumtypes.h"
#include "gdbusobjectmanagerclient.h"
#include "gdbusinterface.h"
+#include "gdbusinterfacestub.h"
/*** END file-header ***/
diff --git a/src/gdbuscodegen-marshal.list b/src/gdbuscodegen-marshal.list
index 97683c7..984797b 100644
--- a/src/gdbuscodegen-marshal.list
+++ b/src/gdbuscodegen-marshal.list
@@ -2,3 +2,4 @@ BOOLEAN:VARIANT,BOXED
VOID:OBJECT,OBJECT
VOID:OBJECT,OBJECT,STRING,STRING,VARIANT
VOID:OBJECT,OBJECT,VARIANT,BOXED
+BOOL:OBJECT
diff --git a/src/gdbusinterfacestub.c b/src/gdbusinterfacestub.c
index 32f2d20..cb5afea 100644
--- a/src/gdbusinterfacestub.c
+++ b/src/gdbusinterfacestub.c
@@ -23,6 +23,8 @@
#include "config.h"
#include "gdbusinterfacestub.h"
+#include "gdbuscodegen-marshal.h"
+#include "gdbuscodegen-enumtypes.h"
/**
* SECTION:gdbusinterfacestub
@@ -35,8 +37,28 @@
struct _GDBusInterfaceStubPrivate
{
GDBusObject *object;
+ GDBusInterfaceStubFlags flags;
+ guint registration_id;
+
+ GDBusConnection *connection;
+ gchar *object_path;
+ GDBusInterfaceVTable *hooked_vtable;
+};
+
+enum
+{
+ G_AUTHORIZE_METHOD_SIGNAL,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_G_FLAGS
};
+static guint signals[LAST_SIGNAL] = {0};
+
static void dbus_interface_interface_init (GDBusInterfaceIface *iface);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GDBusInterfaceStub, g_dbus_interface_stub, G_TYPE_OBJECT,
@@ -46,17 +68,157 @@ static void
g_dbus_interface_stub_finalize (GObject *object)
{
GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (object);
+ /* unexport if already exported */
+ if (stub->priv->registration_id > 0)
+ g_dbus_interface_stub_unexport (stub);
+
+ g_assert (stub->priv->connection == NULL);
+ g_assert (stub->priv->object_path == NULL);
+ g_assert (stub->priv->hooked_vtable == NULL);
+
if (stub->priv->object != NULL)
g_object_remove_weak_pointer (G_OBJECT (stub->priv->object), (gpointer *) &stub->priv->object);
G_OBJECT_CLASS (g_dbus_interface_stub_parent_class)->finalize (object);
}
static void
+g_dbus_interface_stub_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (object);
+
+ switch (prop_id)
+ {
+ case PROP_G_FLAGS:
+ g_value_set_flags (value, g_dbus_interface_stub_get_flags (stub));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+g_dbus_interface_stub_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (object);
+
+ switch (prop_id)
+ {
+ case PROP_G_FLAGS:
+ g_dbus_interface_stub_set_flags (stub, g_value_get_flags (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+_g_signal_accumulator_false_handled (GSignalInvocationHint *ihint,
+ GValue *return_accu,
+ const GValue *handler_return,
+ gpointer dummy)
+{
+ gboolean continue_emission;
+ gboolean signal_return;
+
+ signal_return = g_value_get_boolean (handler_return);
+ g_value_set_boolean (return_accu, signal_return);
+ continue_emission = signal_return;
+
+ return continue_emission;
+}
+
+static gboolean
+g_dbus_interface_stub_g_authorize_method_default (GDBusInterfaceStub *stub,
+ GDBusMethodInvocation *invocation)
+{
+ return TRUE;
+}
+
+static void
g_dbus_interface_stub_class_init (GDBusInterfaceStubClass *klass)
{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GObjectClass *gobject_class;
+ gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = g_dbus_interface_stub_finalize;
+ gobject_class->set_property = g_dbus_interface_stub_set_property;
+ gobject_class->get_property = g_dbus_interface_stub_get_property;
+
+ klass->g_authorize_method = g_dbus_interface_stub_g_authorize_method_default;
+
+ /**
+ * GDBusInterfaceStub:g-flags:
+ *
+ * Flags from the #GDBusInterfaceStubFlags enumeration.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_G_FLAGS,
+ g_param_spec_flags ("g-flags",
+ "g-flags",
+ "Flags for the interface stub",
+ G_TYPE_DBUS_INTERFACE_STUB_FLAGS,
+ G_DBUS_INTERFACE_STUB_FLAGS_NONE,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDBusInterfaceStub::g-authorize-method:
+ * @interface: The #GDBusInterfaceStub emitting the signal.
+ * @invocation: A #GDBusMethodInvocation.
+ *
+ * Emitted when a method is invoked by a remote caller and used to
+ * determine if the method call is authorized.
+ *
+ * Note that this signal is emitted in a thread dedicated to
+ * handling the method call so handlers are allowed to perform
+ * blocking IO. This means that it is appropriate to call
+ * e.g. <ulink
+ * url="http://hal.freedesktop.org/docs/polkit/PolkitAuthority.html#polkit-authority-check-authorization-sync">polkit_authority_check_authorization_sync()</ulink>
+ * with the <ulink
+ * url="http://hal.freedesktop.org/docs/polkit/PolkitAuthority.html#POLKIT-CHECK-AUTHORIZATION-FLAGS-ALLOW-USER-INTERACTION:CAPS">POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION</ulink> flag set.
+ *
+ * If %FALSE is returned then no further handlers are run and the
+ * signal handler must take ownership of @invocation and finish
+ * handling the call (e.g. return an error via
+ * g_dbus_method_invocation_return_error()).
+ *
+ * Otherwise, if %TRUE is returned, signal emission continues. If no
+ * handlers return %FALSE, then the method is dispatched.
+ *
+ * The default class handler just returns %TRUE.
+ *
+ * Note that the common case is optimized: if no signals handlers
+ * are connected and the default class handler isn't overridden and
+ * #GDBusInterfaceStub:g-flags does not have the
+ * %G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD
+ * flags set, no dedicated thread is ever used and the call will be
+ * handled in the same thread as the object that @interface belongs
+ * to was exported in.
+ *
+ * Returns: %TRUE if the call is authorized, %FALSE otherwise.
+ */
+ signals[G_AUTHORIZE_METHOD_SIGNAL] =
+ g_signal_new ("g-authorize-method",
+ G_TYPE_DBUS_INTERFACE_STUB,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GDBusInterfaceStubClass, g_authorize_method),
+ _g_signal_accumulator_false_handled,
+ NULL,
+ _gdbuscodegen_marshal_BOOL__OBJECT,
+ G_TYPE_BOOLEAN,
+ 1,
+ G_TYPE_DBUS_METHOD_INVOCATION);
g_type_class_add_private (klass, sizeof (GDBusInterfaceStubPrivate));
}
@@ -70,6 +232,41 @@ g_dbus_interface_stub_init (GDBusInterfaceStub *stub)
/* ---------------------------------------------------------------------------------------------------- */
/**
+ * g_dbus_interface_stub_get_flags:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Gets the #GDBusInterfaceStubFlags that describes what the behavior
+ * of @stub
+ *
+ * Returns: One or more flags from the #GDBusInterfaceStubFlags enumeration.
+ */
+GDBusInterfaceStubFlags
+g_dbus_interface_stub_get_flags (GDBusInterfaceStub *stub)
+{
+ g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), G_DBUS_INTERFACE_STUB_FLAGS_NONE);
+ return stub->priv->flags;
+}
+
+/**
+ * g_dbus_interface_stub_set_flags:
+ * @stub: A #GDBusInterfaceStub.
+ * @flags: Flags from the #GDBusInterfaceStubFlags enumeration.
+ *
+ * Sets flags describing what the behavior of @stub should be.
+ */
+void
+g_dbus_interface_stub_set_flags (GDBusInterfaceStub *stub,
+ GDBusInterfaceStubFlags flags)
+{
+ g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub));
+ if (stub->priv->flags != flags)
+ {
+ stub->priv->flags = flags;
+ g_object_notify (G_OBJECT (stub), "g-flags");
+ }
+}
+
+/**
* g_dbus_interface_stub_get_info:
* @stub: A #GDBusInterfaceStub.
*
@@ -89,6 +286,26 @@ g_dbus_interface_stub_get_info (GDBusInterfaceStub *stub)
}
/**
+ * g_dbus_interface_stub_get_vtable:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Gets the interface vtable for the D-Bus interface implemented by
+ * @interface. The returned function pointers should expect @stub
+ * itself to be passed as @user_data.
+ *
+ * Returns: A #GDBusInterfaceVTable (never %NULL).
+ */
+GDBusInterfaceVTable *
+g_dbus_interface_stub_get_vtable (GDBusInterfaceStub *stub)
+{
+ GDBusInterfaceVTable *ret;
+ g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL);
+ ret = G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->get_vtable (stub);
+ g_warn_if_fail (ret != NULL);
+ return ret;
+}
+
+/**
* g_dbus_interface_stub_get_properties:
* @stub: A #GDBusInterfaceStub.
*
@@ -126,31 +343,6 @@ g_dbus_interface_stub_flush (GDBusInterfaceStub *stub)
G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->flush (stub);
}
-/**
- * g_dbus_interface_stub_export:
- * @stub: The D-Bus interface to export.
- * @connection: A #GDBusConnection to export @stub on.
- * @object_path: The path to export the interface at.
- * @error: Return location for error or %NULL.
- *
- * Exports @stubs at @object_path on @connection.
- *
- * Returns: 0 if @error is set, otherwise a registration id (never 0)
- * that can be used with g_dbus_connection_unregister_object().
- */
-guint
-g_dbus_interface_stub_export (GDBusInterfaceStub *stub,
- GDBusConnection *connection,
- const gchar *object_path,
- GError **error)
-{
- g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), 0);
- g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0);
- g_return_val_if_fail (g_variant_is_object_path (object_path), 0);
- g_return_val_if_fail (error == NULL || *error == NULL, 0);
- return G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->export (stub, connection, object_path, error);
-}
-
/* ---------------------------------------------------------------------------------------------------- */
static GDBusInterfaceInfo *
@@ -186,3 +378,299 @@ dbus_interface_interface_init (GDBusInterfaceIface *iface)
iface->get_object = g_dbus_interface_stub_get_object;
iface->set_object = g_dbus_interface_stub_set_object;
}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ volatile gint ref_count;
+ GDBusInterfaceStub *stub;
+ GDBusInterfaceMethodCallFunc method_call_func;
+ GDBusMethodInvocation *invocation;
+ GMainContext *context;
+} DispatchData;
+
+static void
+dispatch_data_unref (DispatchData *data)
+{
+ if (g_atomic_int_dec_and_test (&data->ref_count))
+ {
+ if (data->context != NULL)
+ g_main_context_unref (data->context);
+ g_free (data);
+ }
+}
+
+static DispatchData *
+dispatch_data_ref (DispatchData *data)
+{
+ g_atomic_int_inc (&data->ref_count);
+ return data;
+}
+
+static gboolean
+dispatch_invoke_in_context_func (gpointer user_data)
+{
+ DispatchData *data = user_data;
+ data->method_call_func (g_dbus_method_invocation_get_connection (data->invocation),
+ g_dbus_method_invocation_get_sender (data->invocation),
+ g_dbus_method_invocation_get_object_path (data->invocation),
+ g_dbus_method_invocation_get_interface_name (data->invocation),
+ g_dbus_method_invocation_get_method_name (data->invocation),
+ g_dbus_method_invocation_get_parameters (data->invocation),
+ data->invocation,
+ g_dbus_method_invocation_get_user_data (data->invocation));
+ return FALSE;
+}
+
+static gboolean
+dispatch_in_thread_func (GIOSchedulerJob *job,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ DispatchData *data = user_data;
+ gboolean authorized;
+
+ authorized = FALSE;
+ g_signal_emit (data->stub,
+ signals[G_AUTHORIZE_METHOD_SIGNAL],
+ 0,
+ data->invocation,
+ &authorized);
+
+ if (authorized)
+ {
+ gboolean run_in_thread;
+ run_in_thread = (data->stub->priv->flags & G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
+ if (run_in_thread)
+ {
+ /* might as well just re-use the existing thread */
+ data->method_call_func (g_dbus_method_invocation_get_connection (data->invocation),
+ g_dbus_method_invocation_get_sender (data->invocation),
+ g_dbus_method_invocation_get_object_path (data->invocation),
+ g_dbus_method_invocation_get_interface_name (data->invocation),
+ g_dbus_method_invocation_get_method_name (data->invocation),
+ g_dbus_method_invocation_get_parameters (data->invocation),
+ data->invocation,
+ g_dbus_method_invocation_get_user_data (data->invocation));
+ }
+ else
+ {
+ /* bah, back to original context */
+ g_main_context_invoke_full (data->context,
+ G_PRIORITY_DEFAULT,
+ dispatch_invoke_in_context_func,
+ dispatch_data_ref (data),
+ (GDestroyNotify) dispatch_data_unref);
+ }
+ }
+ else
+ {
+ /* do nothing */
+ }
+
+ return FALSE;
+}
+
+static void
+g_dbus_interface_method_dispatch_helper (GDBusInterfaceStub *stub,
+ GDBusInterfaceMethodCallFunc method_call_func,
+ GDBusMethodInvocation *invocation)
+{
+ gboolean has_handlers;
+ gboolean has_default_class_handler;
+ gboolean emit_authorized_signal;
+ gboolean run_in_thread;
+
+ g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub));
+ g_return_if_fail (method_call_func != NULL);
+ g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
+
+ /* optimization for the common case where
+ *
+ * a) no handler is connected and class handler is not overridden; and
+ * b) method calls are not dispatched in a thread
+ */
+ has_handlers = g_signal_has_handler_pending (stub,
+ signals[G_AUTHORIZE_METHOD_SIGNAL],
+ 0,
+ TRUE);
+ has_default_class_handler = (G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->g_authorize_method ==
+ g_dbus_interface_stub_g_authorize_method_default);
+ emit_authorized_signal = (has_handlers || !has_default_class_handler);
+ run_in_thread = (stub->priv->flags & G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
+ if (!emit_authorized_signal && !run_in_thread)
+ {
+ method_call_func (g_dbus_method_invocation_get_connection (invocation),
+ g_dbus_method_invocation_get_sender (invocation),
+ g_dbus_method_invocation_get_object_path (invocation),
+ g_dbus_method_invocation_get_interface_name (invocation),
+ g_dbus_method_invocation_get_method_name (invocation),
+ g_dbus_method_invocation_get_parameters (invocation),
+ invocation,
+ g_dbus_method_invocation_get_user_data (invocation));
+ }
+ else
+ {
+ DispatchData *data;
+ data = g_new0 (DispatchData, 1);
+ data->stub = stub;
+ data->method_call_func = method_call_func;
+ data->invocation = invocation;
+ data->context = g_main_context_get_thread_default ();
+ data->ref_count = 1;
+ if (data->context != NULL)
+ g_main_context_ref (data->context);
+ g_io_scheduler_push_job (dispatch_in_thread_func,
+ data,
+ (GDestroyNotify) dispatch_data_unref,
+ G_PRIORITY_DEFAULT,
+ NULL); /* GCancellable* */
+ }
+}
+
+static void
+stub_intercept_handle_method_call(GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (user_data);
+ g_dbus_interface_method_dispatch_helper (stub,
+ g_dbus_interface_stub_get_vtable (stub)->method_call,
+ invocation);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_interface_stub_get_connection:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Gets the connection that @stub is exported on, if any.
+ *
+ * Returns: (transfer none): A #GDBusConnection or %NULL if @stub is
+ * not exported anywhere. Do not free, the object belongs to @stub.
+ */
+GDBusConnection *
+g_dbus_interface_stub_get_connection (GDBusInterfaceStub *stub)
+{
+ g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL);
+ return stub->priv->connection;
+}
+
+/**
+ * g_dbus_interface_stub_get_object_path:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Gets the object that that @stub is exported on, if any.
+ *
+ * Returns: A string owned by @stub or %NULL if stub is not exported
+ * anywhere. Do not free, the string belongs to @stub.
+ */
+const gchar *
+g_dbus_interface_stub_get_object_path (GDBusInterfaceStub *stub)
+{
+ g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL);
+ return stub->priv->object_path;
+}
+
+/**
+ * g_dbus_interface_stub_export:
+ * @stub: The D-Bus interface to export.
+ * @connection: A #GDBusConnection to export @stub on.
+ * @object_path: The path to export the interface at.
+ * @error: Return location for error or %NULL.
+ *
+ * Exports @stubs at @object_path on @connection.
+ *
+ * Use g_dbus_interface_stub_unexport() to unexport the object.
+ *
+ * Returns: %TRUE if the interface was exported, other %FALSE with
+ * @error set.
+ */
+gboolean
+g_dbus_interface_stub_export (GDBusInterfaceStub *stub,
+ GDBusConnection *connection,
+ const gchar *object_path,
+ GError **error)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), 0);
+ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0);
+ g_return_val_if_fail (g_variant_is_object_path (object_path), 0);
+ g_return_val_if_fail (error == NULL || *error == NULL, 0);
+
+ ret = FALSE;
+ if (stub->priv->registration_id > 0)
+ {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED, /* TODO: new error code */
+ "The object is already exported");
+ goto out;
+ }
+
+ g_assert (stub->priv->connection == NULL);
+ g_assert (stub->priv->object_path == NULL);
+ g_assert (stub->priv->hooked_vtable == NULL);
+
+ /* Hook the vtable since we need to intercept method calls for
+ * ::g-authorize-method and for dispatching in thread vs
+ * context
+ */
+ stub->priv->hooked_vtable = g_memdup (g_dbus_interface_stub_get_vtable (stub), sizeof (GDBusInterfaceVTable));
+ stub->priv->hooked_vtable->method_call = stub_intercept_handle_method_call;
+
+ stub->priv->connection = g_object_ref (connection);
+ stub->priv->object_path = g_strdup (object_path);
+ stub->priv->registration_id = g_dbus_connection_register_object (connection,
+ object_path,
+ g_dbus_interface_stub_get_info (stub),
+ stub->priv->hooked_vtable,
+ stub,
+ NULL, /* user_data_free_func */
+ error);
+ if (stub->priv->registration_id == 0)
+ goto out;
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
+/**
+ * g_dbus_interface_stub_unexport:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Stops exporting an interface previously exported with
+ * g_dbus_interface_stub_export().
+ */
+void
+g_dbus_interface_stub_unexport (GDBusInterfaceStub *stub)
+{
+ g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub));
+ g_return_if_fail (stub->priv->registration_id > 0);
+
+ g_assert (stub->priv->connection != NULL);
+ g_assert (stub->priv->object_path != NULL);
+ g_assert (stub->priv->hooked_vtable != NULL);
+
+ g_warn_if_fail (g_dbus_connection_unregister_object (stub->priv->connection,
+ stub->priv->registration_id));
+
+ g_object_unref (stub->priv->connection);
+ g_free (stub->priv->object_path);
+ stub->priv->connection = NULL;
+ stub->priv->object_path = NULL;
+ stub->priv->hooked_vtable = NULL;
+ stub->priv->registration_id = 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/gdbusinterfacestub.h b/src/gdbusinterfacestub.h
index aac0e05..deae82d 100644
--- a/src/gdbusinterfacestub.h
+++ b/src/gdbusinterfacestub.h
@@ -53,12 +53,29 @@ struct _GDBusInterfaceStub
};
/**
+ * GDBusInterfaceStubFlags:
+ * @G_DBUS_INTERFACE_STUB_FLAGS_NONE: No flags set.
+ * @G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD: Each method invocation is handled in
+ * a thread dedicated to the invocation. This means that the method implementation can use blocking IO
+ * without blocking any other part of the process. It also means that the method implementation must
+ * use locking to access data structures used by other threads.
+ *
+ * Flags describing the behavior of a #GDBusInterfaceStub class.
+ */
+typedef enum
+{
+ G_DBUS_INTERFACE_STUB_FLAGS_NONE = 0,
+ G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD = (1<<0)
+} GDBusInterfaceStubFlags;
+
+/**
* GDBusInterfaceStubClass:
* @parent_class: The parent class.
- * @get_info: Returns a #GDBusInterfaceInfo. See g_dbus_interface_stub_get_info().
+ * @get_info: Returns a #GDBusInterfaceInfo. See g_dbus_interface_stub_get_info() for details.
+ * @get_vtable: Returns a #GDBusInterfaceVTable. See g_dbus_interface_stub_get_vtable() for details.
* @get_properties: Returns a new, floating, #GVariant with all properties. See g_dbus_interface_stub_get_properties().
* @flush: Emits outstanding changes, if any. See g_dbus_interface_stub_flush().
- * @export: Exports the instance on a #GDBusConnection. See g_dbus_interface_stub_export().
+ * @g_authorize_method: Signal class handler for the #GDBusInterfaceStub::g-authorize-method signal.
*
* Class structure for #GDBusInterfaceStub.
*/
@@ -68,25 +85,39 @@ struct _GDBusInterfaceStubClass
/* Virtual Functions */
GDBusInterfaceInfo *(*get_info) (GDBusInterfaceStub *stub);
+ GDBusInterfaceVTable *(*get_vtable) (GDBusInterfaceStub *stub);
GVariant *(*get_properties) (GDBusInterfaceStub *stub);
void (*flush) (GDBusInterfaceStub *stub);
- guint (*export) (GDBusInterfaceStub *stub,
- GDBusConnection *connection,
- const gchar *object_path,
- GError **error);
/*< private >*/
- gpointer padding[8];
+ gpointer vfunc_padding[8];
+ /*< public >*/
+
+ /* Signals */
+ gboolean (*g_authorize_method) (GDBusInterfaceStub *stub,
+ GDBusMethodInvocation *invocation);
+
+ /*< private >*/
+ gpointer signal_padding[8];
};
-GType g_dbus_interface_stub_get_type (void) G_GNUC_CONST;
-GDBusInterfaceInfo *g_dbus_interface_stub_get_info (GDBusInterfaceStub *stub);
-GVariant *g_dbus_interface_stub_get_properties (GDBusInterfaceStub *stub);
-void g_dbus_interface_stub_flush (GDBusInterfaceStub *stub);
-guint g_dbus_interface_stub_export (GDBusInterfaceStub *stub,
- GDBusConnection *connection,
- const gchar *object_path,
- GError **error);
+GType g_dbus_interface_stub_get_type (void) G_GNUC_CONST;
+GDBusInterfaceStubFlags g_dbus_interface_stub_get_flags (GDBusInterfaceStub *stub);
+void g_dbus_interface_stub_set_flags (GDBusInterfaceStub *stub,
+ GDBusInterfaceStubFlags flags);
+GDBusInterfaceInfo *g_dbus_interface_stub_get_info (GDBusInterfaceStub *stub);
+GDBusInterfaceVTable *g_dbus_interface_stub_get_vtable (GDBusInterfaceStub *stub);
+GVariant *g_dbus_interface_stub_get_properties (GDBusInterfaceStub *stub);
+void g_dbus_interface_stub_flush (GDBusInterfaceStub *stub);
+
+gboolean g_dbus_interface_stub_export (GDBusInterfaceStub *stub,
+ GDBusConnection *connection,
+ const gchar *object_path,
+ GError **error);
+void g_dbus_interface_stub_unexport (GDBusInterfaceStub *stub);
+GDBusConnection *g_dbus_interface_stub_get_connection (GDBusInterfaceStub *stub);
+const gchar *g_dbus_interface_stub_get_object_path (GDBusInterfaceStub *stub);
+
G_END_DECLS
#endif /* __G_DBUS_INTERFACE_STUB_H */
diff --git a/src/gdbusobjectmanagerserver.c b/src/gdbusobjectmanagerserver.c
index 642edaf..b5cc0e4 100644
--- a/src/gdbusobjectmanagerserver.c
+++ b/src/gdbusobjectmanagerserver.c
@@ -48,20 +48,11 @@
* be used with #GDBusObjectManagerServer.
*/
-
-typedef struct
-{
- GDBusInterfaceStub *interface_stub;
- guint reg_id;
-} IfaceData;
-
-static void iface_data_free (IfaceData *data);
-
typedef struct
{
GDBusObjectStub *object;
GDBusObjectManagerServer *manager;
- GHashTable *map_iface_name_to_iface_data;
+ GHashTable *map_iface_name_to_iface;
gboolean exported;
} RegistrationData;
@@ -271,20 +262,17 @@ registration_data_export_interface (RegistrationData *data,
GDBusInterfaceStub *interface_stub)
{
GDBusInterfaceInfo *info;
- guint registration_id;
GError *error;
- IfaceData *iface_data;
const gchar *object_path;
object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object));
info = g_dbus_interface_stub_get_info (interface_stub);
error = NULL;
- registration_id = g_dbus_interface_stub_export (interface_stub,
- data->manager->priv->connection,
- object_path,
- &error);
- if (registration_id == 0)
+ if (!g_dbus_interface_stub_export (interface_stub,
+ data->manager->priv->connection,
+ object_path,
+ &error))
{
/* TODO: probably wrong to complain on stderr */
g_warning ("%s: Error registering object at %s with interface %s: %s",
@@ -296,14 +284,10 @@ registration_data_export_interface (RegistrationData *data,
goto out;
}
- iface_data = g_new0 (IfaceData, 1);
- iface_data->reg_id = registration_id;
- iface_data->interface_stub = g_object_ref (interface_stub);
-
- g_assert (g_hash_table_lookup (data->map_iface_name_to_iface_data, info->name) == NULL);
- g_hash_table_insert (data->map_iface_name_to_iface_data,
+ g_assert (g_hash_table_lookup (data->map_iface_name_to_iface, info->name) == NULL);
+ g_hash_table_insert (data->map_iface_name_to_iface,
info->name,
- iface_data);
+ g_object_ref (interface_stub));
/* if we are already exported, then... */
if (data->exported)
@@ -317,7 +301,6 @@ registration_data_export_interface (RegistrationData *data,
out:
;
- //g_free (object_path);
}
static void
@@ -325,16 +308,15 @@ registration_data_unexport_interface (RegistrationData *data,
GDBusInterfaceStub *interface_stub)
{
GDBusInterfaceInfo *info;
- IfaceData *iface_data;
+ GDBusInterfaceStub *iface;
info = g_dbus_interface_stub_get_info (interface_stub);
- iface_data = g_hash_table_lookup (data->map_iface_name_to_iface_data, info->name);
- g_assert (iface_data != NULL);
- g_assert (iface_data->reg_id > 0);
+ iface = g_hash_table_lookup (data->map_iface_name_to_iface, info->name);
+ g_assert (iface != NULL);
- g_warn_if_fail (g_dbus_connection_unregister_object (data->manager->priv->connection, iface_data->reg_id));
+ g_dbus_interface_stub_unexport (iface);
- g_warn_if_fail (g_hash_table_remove (data->map_iface_name_to_iface_data, info->name));
+ g_warn_if_fail (g_hash_table_remove (data->map_iface_name_to_iface, info->name));
/* if we are already exported, then... */
if (data->exported)
@@ -369,32 +351,23 @@ on_interface_removed (GDBusObject *object,
/* ---------------------------------------------------------------------------------------------------- */
-static void
-iface_data_free (IfaceData *data)
-{
- g_object_unref (data->interface_stub);
- g_free (data);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
static void
registration_data_free (RegistrationData *data)
{
GHashTableIter iter;
- IfaceData *iface_data;
+ GDBusInterfaceStub *iface;
data->exported = FALSE;
- g_hash_table_iter_init (&iter, data->map_iface_name_to_iface_data);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &iface_data))
- g_warn_if_fail (g_dbus_connection_unregister_object (data->manager->priv->connection, iface_data->reg_id));
+ g_hash_table_iter_init (&iter, data->map_iface_name_to_iface);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &iface))
+ g_dbus_interface_stub_unexport (iface);
g_signal_handlers_disconnect_by_func (data->object, G_CALLBACK (on_interface_added), data);
g_signal_handlers_disconnect_by_func (data->object, G_CALLBACK (on_interface_removed), data);
g_object_unref (data->object);
- g_hash_table_destroy (data->map_iface_name_to_iface_data);
+ g_hash_table_destroy (data->map_iface_name_to_iface);
g_free (data);
}
@@ -441,10 +414,10 @@ g_dbus_object_manager_server_export (GDBusObjectManagerServer *manager,
data = g_new0 (RegistrationData, 1);
data->object = g_object_ref (object);
data->manager = manager;
- data->map_iface_name_to_iface_data = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- NULL,
- (GDestroyNotify) iface_data_free);
+ data->map_iface_name_to_iface = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify) g_object_unref);
g_signal_connect (object,
"interface-added",
@@ -560,7 +533,7 @@ g_dbus_object_manager_server_unexport (GDBusObjectManagerServer *manager,
const gchar *iface_name;
interface_names = g_ptr_array_new ();
- g_hash_table_iter_init (&iter, data->map_iface_name_to_iface_data);
+ g_hash_table_iter_init (&iter, data->map_iface_name_to_iface);
while (g_hash_table_iter_next (&iter, (gpointer) &iface_name, NULL))
g_ptr_array_add (interface_names, (gpointer) iface_name);
g_ptr_array_add (interface_names, NULL);
@@ -714,17 +687,17 @@ manager_method_call (GDBusConnection *connection,
{
GVariantBuilder interfaces_builder;
GHashTableIter interface_iter;
- IfaceData *iface_data;
+ GDBusInterfaceStub *iface;
const gchar *iter_object_path;
g_variant_builder_init (&interfaces_builder, G_VARIANT_TYPE ("a{sa{sv}}"));
- g_hash_table_iter_init (&interface_iter, data->map_iface_name_to_iface_data);
- while (g_hash_table_iter_next (&interface_iter, NULL, (gpointer) &iface_data))
+ g_hash_table_iter_init (&interface_iter, data->map_iface_name_to_iface);
+ while (g_hash_table_iter_next (&interface_iter, NULL, (gpointer) &iface))
{
g_variant_builder_add_value (&interfaces_builder,
g_variant_new ("{s@a{sv}}",
- g_dbus_interface_stub_get_info (iface_data->interface_stub)->name,
- g_dbus_interface_stub_get_properties (iface_data->interface_stub)));
+ g_dbus_interface_stub_get_info (iface)->name,
+ g_dbus_interface_stub_get_properties (iface)));
}
iter_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object));
g_variant_builder_add (&array_builder,
@@ -797,13 +770,13 @@ g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *ma
g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{sa{sv}}"));
for (n = 0; interfaces[n] != NULL; n++)
{
- IfaceData *iface_data;
- iface_data = g_hash_table_lookup (data->map_iface_name_to_iface_data, interfaces[n]);
- g_assert (iface_data != NULL);
+ GDBusInterfaceStub *iface;
+ iface = g_hash_table_lookup (data->map_iface_name_to_iface, interfaces[n]);
+ g_assert (iface != NULL);
g_variant_builder_add_value (&array_builder,
g_variant_new ("{s@a{sv}}",
interfaces[n],
- g_dbus_interface_stub_get_properties (iface_data->interface_stub)));
+ g_dbus_interface_stub_get_properties (iface)));
}
error = NULL;
diff --git a/src/org.project.xml b/src/org.project.xml
index 754b142..56bccd3 100644
--- a/src/org.project.xml
+++ b/src/org.project.xml
@@ -207,4 +207,17 @@
</interface> <!-- End org.project.Bat -->
+ <!-- Test interface for g-authorized-method -->
+ <interface name="org.project.Authorize">
+ <method name="CheckNotAuthorized"/>
+ <method name="CheckAuthorized"/>
+ </interface> <!-- End org.project.Authorize -->
+
+ <!-- Test interfaces for handling methods in a thread -->
+ <interface name="org.project.MethodThreads">
+ <method name="GetSelf">
+ <arg name="self_pointer" direction="out" type="s"/>
+ </method>
+ </interface> <!-- End org.project.MethodThreads -->
+
</node>
diff --git a/src/test.c b/src/test.c
index f0416fb..0b14ed0 100644
--- a/src/test.c
+++ b/src/test.c
@@ -23,6 +23,7 @@
#include "config.h"
#include <string.h>
+#include <stdio.h>
#include "test-generated.h"
@@ -85,8 +86,6 @@ check_bar_introspection_data (void)
/* ---------------------------------------------------------------------------------------------------- */
-//#define FOO_BAR_HANDLE_HELLO_WORLD_CALLBACK(f, user_type) (__builtin_choose_expr (__builtin_types_compatible_p (typeof (&f), gboolean (*)(FooBar *, GDBusMethodInvocation*, const gchar *, user_type)), G_CALLBACK (f), f))
-
static gboolean
on_handle_hello_world (FooBar *object,
GDBusMethodInvocation *invocation,
@@ -271,8 +270,81 @@ on_handle_force_method (FooBat *object,
/* ---------------------------------------------------------------------------------------------------- */
+static gboolean
+my_g_authorize_method_handler (GDBusInterfaceStub *interface,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ const gchar *method_name;
+ gboolean authorized;
+
+ authorized = FALSE;
+
+ method_name = g_dbus_method_invocation_get_method_name (invocation);
+ if (g_strcmp0 (method_name, "CheckNotAuthorized") == 0)
+ {
+ authorized = FALSE;
+ }
+ else if (g_strcmp0 (method_name, "CheckAuthorized") == 0)
+ {
+ authorized = TRUE;
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ if (!authorized)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_IO_ERROR,
+ G_IO_ERROR_PERMISSION_DENIED,
+ "not authorized...");
+ }
+ return authorized;
+}
+
+static gboolean
+on_handle_check_not_authorized (FooAuthorize *object,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ foo_authorize_complete_check_not_authorized (object, invocation);
+ return TRUE;
+}
+
+static gboolean
+on_handle_check_authorized (FooAuthorize *object,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ foo_authorize_complete_check_authorized (object, invocation);
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+on_handle_get_self (FooMethodThreads *object,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ gchar *s;
+ s = g_strdup_printf ("%p", g_thread_self ());
+ foo_method_threads_complete_get_self (object, invocation, s);
+ g_free (s);
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GThread *method_handler_thread = NULL;
+
static FooBar *exported_bar_object = NULL;
static FooBat *exported_bat_object = NULL;
+static FooAuthorize *exported_authorize_object = NULL;
+static FooMethodThreads *exported_thread_object_1 = NULL;
+static FooMethodThreads *exported_thread_object_2 = NULL;
static void
on_bus_acquired (GDBusConnection *connection,
@@ -347,6 +419,53 @@ on_bus_acquired (GDBusConnection *connection,
"force-ay", g_variant_new_bytestring ("prop bytestring\xff"),
"force-struct", g_variant_new ("(i)", 4300),
NULL);
+
+ exported_authorize_object = foo_authorize_stub_new ();
+ g_dbus_interface_stub_export (G_DBUS_INTERFACE_STUB (exported_authorize_object),
+ connection,
+ "/authorize",
+ &error);
+ g_assert_no_error (error);
+ g_signal_connect (exported_authorize_object,
+ "g-authorize-method",
+ G_CALLBACK (my_g_authorize_method_handler),
+ NULL);
+ g_signal_connect (exported_authorize_object,
+ "handle-check-not-authorized",
+ FOO_AUTHORIZE_HANDLE_CHECK_NOT_AUTHORIZED_CALLBACK (on_handle_check_not_authorized, gpointer),
+ NULL);
+ g_signal_connect (exported_authorize_object,
+ "handle-check-authorized",
+ FOO_AUTHORIZE_HANDLE_CHECK_AUTHORIZED_CALLBACK (on_handle_check_authorized, gpointer),
+ NULL);
+
+
+ /* only object 1 has the G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD flag set */
+ exported_thread_object_1 = foo_method_threads_stub_new ();
+ g_dbus_interface_stub_set_flags (G_DBUS_INTERFACE_STUB (exported_thread_object_1),
+ G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
+ g_dbus_interface_stub_export (G_DBUS_INTERFACE_STUB (exported_thread_object_1),
+ connection,
+ "/method_threads_1",
+ &error);
+ g_assert_no_error (error);
+ g_signal_connect (exported_thread_object_1,
+ "handle-get-self",
+ FOO_METHOD_THREADS_HANDLE_GET_SELF_CALLBACK (on_handle_get_self, gpointer),
+ NULL);
+
+ exported_thread_object_2 = foo_method_threads_stub_new ();
+ g_dbus_interface_stub_export (G_DBUS_INTERFACE_STUB (exported_thread_object_2),
+ connection,
+ "/method_threads_2",
+ &error);
+ g_assert_no_error (error);
+ g_signal_connect (exported_thread_object_2,
+ "handle-get-self",
+ FOO_METHOD_THREADS_HANDLE_GET_SELF_CALLBACK (on_handle_get_self, gpointer),
+ NULL);
+
+ method_handler_thread = g_thread_self ();
}
static gpointer check_proxies_in_thread (gpointer user_data);
@@ -866,6 +985,63 @@ check_bat_proxy (FooBat *proxy,
/* ---------------------------------------------------------------------------------------------------- */
+static void
+check_authorize_proxy (FooAuthorize *proxy,
+ GMainLoop *thread_loop)
+{
+ GError *error;
+ gboolean ret;
+
+ /* Check that g-authorize-method works as intended */
+
+ error = NULL;
+ ret = foo_authorize_call_check_not_authorized_sync (proxy, NULL, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED);
+ g_error_free (error);
+ g_assert (!ret);
+
+ error = NULL;
+ ret = foo_authorize_call_check_authorized_sync (proxy, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GThread *
+get_self_via_proxy (FooMethodThreads *proxy_1)
+{
+ GError *error;
+ gchar *self_str;
+ gboolean ret;
+ gpointer self;
+
+ error = NULL;
+ ret = foo_method_threads_call_get_self_sync (proxy_1, &self_str, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert_cmpint (sscanf (self_str, "%p", &self), ==, 1);
+
+ g_free (self_str);
+
+ return self;
+}
+
+static void
+check_thread_proxies (FooMethodThreads *proxy_1,
+ FooMethodThreads *proxy_2,
+ GMainLoop *thread_loop)
+{
+ /* proxy_1 is indeed using threads so should never get the handler thread */
+ g_assert (get_self_via_proxy (proxy_1) != method_handler_thread);
+
+ /* proxy_2 is not using threads so should get the handler thread */
+ g_assert (get_self_via_proxy (proxy_2) == method_handler_thread);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
static gpointer
check_proxies_in_thread (gpointer user_data)
{
@@ -875,6 +1051,9 @@ check_proxies_in_thread (gpointer user_data)
GError *error;
FooBar *bar_proxy;
FooBat *bat_proxy;
+ FooAuthorize *authorize_proxy;
+ FooMethodThreads *thread_proxy_1;
+ FooMethodThreads *thread_proxy_2;
thread_context = g_main_context_new ();
thread_loop = g_main_loop_new (thread_context, FALSE);
@@ -903,6 +1082,36 @@ check_proxies_in_thread (gpointer user_data)
g_assert_no_error (error);
g_object_unref (bat_proxy);
+ error = NULL;
+ authorize_proxy = foo_authorize_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.gtk.GDBus.BindingsTool.Test",
+ "/authorize",
+ NULL, /* GCancellable* */
+ &error);
+ check_authorize_proxy (authorize_proxy, thread_loop);
+ g_assert_no_error (error);
+ g_object_unref (authorize_proxy);
+
+ error = NULL;
+ thread_proxy_1 = foo_method_threads_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.gtk.GDBus.BindingsTool.Test",
+ "/method_threads_1",
+ NULL, /* GCancellable* */
+ &error);
+ g_assert_no_error (error);
+ thread_proxy_2 = foo_method_threads_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.gtk.GDBus.BindingsTool.Test",
+ "/method_threads_2",
+ NULL, /* GCancellable* */
+ &error);
+ g_assert_no_error (error);
+ check_thread_proxies (thread_proxy_1, thread_proxy_2, thread_loop);
+ g_object_unref (thread_proxy_1);
+ g_object_unref (thread_proxy_2);
+
g_main_loop_unref (thread_loop);
g_main_context_unref (thread_context);