diff options
author | David Zeuthen <davidz@redhat.com> | 2011-03-18 11:52:36 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2011-03-18 11:52:36 -0400 |
commit | 0abd81c74b86820f4728f2ad1414ed3a317ca0f5 (patch) | |
tree | f60e9ada83b9b6a1829a254cd8c606761f174686 | |
parent | f3658eb6b424bab10d349db04f491c6a81615122 (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.txt | 9 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/codegen.py | 57 | ||||
-rw-r--r-- | src/gdbuscodegen-enumtypes.c.template | 1 | ||||
-rw-r--r-- | src/gdbuscodegen-marshal.list | 1 | ||||
-rw-r--r-- | src/gdbusinterfacestub.c | 540 | ||||
-rw-r--r-- | src/gdbusinterfacestub.h | 61 | ||||
-rw-r--r-- | src/gdbusobjectmanagerserver.c | 91 | ||||
-rw-r--r-- | src/org.project.xml | 13 | ||||
-rw-r--r-- | src/test.c | 213 |
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> @@ -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); |