X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgdbusconnection.c;h=a4abc14dcb4546c3c54f1154133351259a1931aa;hb=4d9ae95ae05669f329381898c38e6a98ae7f0cd5;hp=d9c1615552d1fbfd8d7521461ee0f3637ef17201;hpb=4ad4c306c3b80620185cf975b402e17a6174aea9;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index d9c1615..a4abc14 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -23,11 +23,6 @@ /* * TODO for GDBus: * - * - Validate all data (e.g. UTF-8) and check all the required D-Bus headers - * are present and forbidden ones aren't - * - When writing: g_dbus_message_to_blob() - * - When reading: g_dbus_message_new_from_blob() - * * - would be nice to expose GDBusAuthMechanism and an extension point * * - Need to rewrite GDBusAuth and rework GDBusAuthMechanism. In particular @@ -71,7 +66,7 @@ * - see g_dbus_address_connect() in gdbusaddress.c * * - would be cute to use kernel-specific APIs to resolve fds for - * debug output when using G_DBUS_DEBUG=messages, e.g. in addition to + * debug output when using G_DBUS_DEBUG=message, e.g. in addition to * * fd 21: dev=8:1,mode=0100644,ino=1171231,uid=0,gid=0,rdev=0:0,size=234,atime=1273070640,mtime=1267126160,ctime=1267126160 * @@ -96,16 +91,24 @@ * - Export objects before claiming names * - Talk about auto-starting services (cf. GBusNameWatcherFlags) * - * - Mention in all API that the GVariant is sunk. Also mention - * when the returned GVariant is floating. + * - use abstract sockets in test code + * - right now it doesn't work, dbus-daemon(1) fails with + * + * /gdbus/connection/filter: Failed to start message bus: Failed to bind + * socket "/tmp/g-dbus-tests-pid-28531": Address already in use + * ** WARNING **: Error reading address from dbus daemon, 0 bytes read * - * - Consistent timeout handling (25s vs 30s?) + * or similar. */ #include "config.h" #include #include +#include +#ifdef HAVE_UNISTD_H +#include +#endif #include "gdbusauth.h" #include "gdbusutils.h" @@ -126,14 +129,11 @@ #include "gsimpleasyncresult.h" #ifdef G_OS_UNIX -#include -#include -#include -#include +#include "gunixconnection.h" +#include "gunixfdmessage.h" #endif #include "glibintl.h" -#include "gioalias.h" /** * SECTION:gdbusconnection @@ -147,7 +147,7 @@ * * This class is rarely used directly in D-Bus clients. If you are writing * an D-Bus client, it is often easier to use the g_bus_own_name(), - * g_bus_watch_name() or g_bus_watch_proxy() APIs. + * g_bus_watch_name() or g_dbus_proxy_new_for_bus() APIs. * * D-Bus server exampleFIXME: MISSING XINCLUDE CONTENT * @@ -160,6 +160,28 @@ /* ---------------------------------------------------------------------------------------------------- */ +typedef struct _GDBusConnectionClass GDBusConnectionClass; + +/** + * GDBusConnectionClass: + * @closed: Signal class handler for the #GDBusConnection::closed signal. + * + * Class structure for #GDBusConnection. + * + * Since: 2.26 + */ +struct _GDBusConnectionClass +{ + /*< private >*/ + GObjectClass parent_class; + + /*< public >*/ + /* Signals */ + void (*closed) (GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error); +}; + G_LOCK_DEFINE_STATIC (message_bus_lock); static GDBusConnection *the_session_bus = NULL; @@ -167,6 +189,77 @@ static GDBusConnection *the_system_bus = NULL; /* ---------------------------------------------------------------------------------------------------- */ +typedef struct +{ + GDestroyNotify callback; + gpointer user_data; + GMainContext *context; +} CallDestroyNotifyData; + +static gboolean +call_destroy_notify_data_in_idle (gpointer user_data) +{ + CallDestroyNotifyData *data = user_data; + data->callback (data->user_data); + return FALSE; +} + +static void +call_destroy_notify_data_free (CallDestroyNotifyData *data) +{ + if (data->context != NULL) + g_main_context_unref (data->context); + g_free (data); +} + +/* + * call_destroy_notify: + * @context: A #GMainContext or %NULL. + * @callback: A #GDestroyNotify or %NULL. + * @user_data: Data to pass to @callback. + * + * Schedules @callback to run in @context. + */ +static void +call_destroy_notify (GMainContext *context, + GDestroyNotify callback, + gpointer user_data) +{ + if (callback == NULL) + goto out; + + if (context == g_main_context_get_thread_default ()) + { + callback (user_data); + } + else + { + GSource *idle_source; + CallDestroyNotifyData *data; + + data = g_new0 (CallDestroyNotifyData, 1); + data->callback = callback; + data->user_data = user_data; + data->context = context; + if (data->context != NULL) + g_main_context_ref (data->context); + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + call_destroy_notify_data_in_idle, + data, + (GDestroyNotify) call_destroy_notify_data_free); + g_source_attach (idle_source, data->context); + g_source_unref (idle_source); + } + + out: + ; +} + +/* ---------------------------------------------------------------------------------------------------- */ + static gboolean _g_strv_has_string (const gchar* const *haystack, const gchar *needle) @@ -188,7 +281,7 @@ _g_strv_has_string (const gchar* const *haystack, #else // TODO: for some reason this doesn't work on Windows #define CONNECTION_ENSURE_LOCK(obj) do { \ - if (G_UNLIKELY (g_mutex_trylock((obj)->priv->lock))) \ + if (G_UNLIKELY (g_mutex_trylock((obj)->lock))) \ { \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ "CONNECTION_ENSURE_LOCK: GDBusConnection object lock is not locked"); \ @@ -197,15 +290,26 @@ _g_strv_has_string (const gchar* const *haystack, #endif #define CONNECTION_LOCK(obj) do { \ - g_mutex_lock ((obj)->priv->lock); \ + g_mutex_lock ((obj)->lock); \ } while (FALSE) #define CONNECTION_UNLOCK(obj) do { \ - g_mutex_unlock ((obj)->priv->lock); \ + g_mutex_unlock ((obj)->lock); \ } while (FALSE) -struct _GDBusConnectionPrivate +/** + * GDBusConnection: + * + * The #GDBusConnection structure contains only private data and + * should only be accessed using the provided API. + * + * Since: 2.26 + */ +struct _GDBusConnection { + /*< private >*/ + GObject parent_instance; + /* ------------------------------------------------------------------------ */ /* -- General object state ------------------------------------------------ */ /* ------------------------------------------------------------------------ */ @@ -268,9 +372,9 @@ struct _GDBusConnectionPrivate GHashTable *map_method_serial_to_send_message_data; /* guint32 -> SendMessageData* */ /* Maps used for managing signal subscription */ - GHashTable *map_rule_to_signal_data; /* gchar* -> SignalData */ - GHashTable *map_id_to_signal_data; /* guint -> SignalData */ - GHashTable *map_sender_to_signal_data_array; /* gchar* -> GPtrArray* of SignalData */ + GHashTable *map_rule_to_signal_data; /* match rule (gchar*) -> SignalData */ + GHashTable *map_id_to_signal_data; /* id (guint) -> SignalData */ + GHashTable *map_sender_unique_name_to_signal_data_array; /* unique sender (gchar*) -> GPtrArray* of SignalData */ /* Maps used for managing exported objects and subtrees */ GHashTable *map_object_path_to_eo; /* gchar* -> ExportedObject* */ @@ -288,7 +392,10 @@ struct _GDBusConnectionPrivate GDBusCapabilityFlags capabilities; GDBusAuthObserver *authentication_observer; - GCredentials *crendentials; + GCredentials *credentials; + + /* set to TRUE when finalizing */ + gboolean finalizing; }; typedef struct ExportedObject ExportedObject; @@ -348,13 +455,14 @@ G_DEFINE_TYPE_WITH_CODE (GDBusConnection, g_dbus_connection, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) ); +static GHashTable *alive_connections = NULL; + static void g_dbus_connection_dispose (GObject *object) { GDBusConnection *connection = G_DBUS_CONNECTION (object); G_LOCK (message_bus_lock); - //g_debug ("disposing %p", connection); if (connection == the_session_bus) { the_session_bus = NULL; @@ -363,11 +471,20 @@ g_dbus_connection_dispose (GObject *object) { the_system_bus = NULL; } - if (connection->priv->worker != NULL) + CONNECTION_LOCK (connection); + if (connection->worker != NULL) + { + _g_dbus_worker_stop (connection->worker); + connection->worker = NULL; + if (alive_connections != NULL) + g_warn_if_fail (g_hash_table_remove (alive_connections, connection)); + } + else { - _g_dbus_worker_stop (connection->priv->worker); - connection->priv->worker = NULL; + if (alive_connections != NULL) + g_warn_if_fail (g_hash_table_lookup (alive_connections, connection) == NULL); } + CONNECTION_UNLOCK (connection); G_UNLOCK (message_bus_lock); if (G_OBJECT_CLASS (g_dbus_connection_parent_class)->dispose != NULL) @@ -379,55 +496,60 @@ g_dbus_connection_finalize (GObject *object) { GDBusConnection *connection = G_DBUS_CONNECTION (object); - if (connection->priv->authentication_observer != NULL) - g_object_unref (connection->priv->authentication_observer); + connection->finalizing = TRUE; + + purge_all_signal_subscriptions (connection); + + purge_all_filters (connection); + g_ptr_array_unref (connection->filters); + + if (connection->authentication_observer != NULL) + g_object_unref (connection->authentication_observer); - if (connection->priv->auth != NULL) - g_object_unref (connection->priv->auth); + if (connection->auth != NULL) + g_object_unref (connection->auth); - //g_debug ("finalizing %p", connection); - if (connection->priv->stream != NULL) + if (connection->credentials) + g_object_unref (connection->credentials); + + if (connection->stream != NULL) { /* We don't really care if closing the stream succeeds or not */ - g_io_stream_close_async (connection->priv->stream, + g_io_stream_close_async (connection->stream, G_PRIORITY_DEFAULT, NULL, /* GCancellable */ NULL, /* GAsyncReadyCallback */ NULL); /* userdata */ - g_object_unref (connection->priv->stream); - connection->priv->stream = NULL; + g_object_unref (connection->stream); + connection->stream = NULL; } - g_free (connection->priv->address); - - g_free (connection->priv->guid); - g_free (connection->priv->bus_unique_name); + g_free (connection->address); - if (connection->priv->initialization_error != NULL) - g_error_free (connection->priv->initialization_error); + g_free (connection->guid); + g_free (connection->bus_unique_name); - g_hash_table_unref (connection->priv->map_method_serial_to_send_message_data); + if (connection->initialization_error != NULL) + g_error_free (connection->initialization_error); - purge_all_signal_subscriptions (connection); - g_hash_table_unref (connection->priv->map_rule_to_signal_data); - g_hash_table_unref (connection->priv->map_id_to_signal_data); - g_hash_table_unref (connection->priv->map_sender_to_signal_data_array); + g_hash_table_unref (connection->map_method_serial_to_send_message_data); - g_hash_table_unref (connection->priv->map_id_to_ei); - g_hash_table_unref (connection->priv->map_object_path_to_eo); - g_hash_table_unref (connection->priv->map_id_to_es); - g_hash_table_unref (connection->priv->map_object_path_to_es); + g_hash_table_unref (connection->map_rule_to_signal_data); + g_hash_table_unref (connection->map_id_to_signal_data); + g_hash_table_unref (connection->map_sender_unique_name_to_signal_data_array); - purge_all_filters (connection); - g_ptr_array_unref (connection->priv->filters); + g_hash_table_unref (connection->map_id_to_ei); + g_hash_table_unref (connection->map_object_path_to_eo); + g_hash_table_unref (connection->map_id_to_es); + g_hash_table_unref (connection->map_object_path_to_es); - if (connection->priv->main_context_at_construction != NULL) - g_main_context_unref (connection->priv->main_context_at_construction); + if (connection->main_context_at_construction != NULL) + g_main_context_unref (connection->main_context_at_construction); - g_free (connection->priv->machine_id); + g_free (connection->machine_id); - g_mutex_free (connection->priv->init_lock); - g_mutex_free (connection->priv->lock); + g_mutex_free (connection->init_lock); + g_mutex_free (connection->lock); G_OBJECT_CLASS (g_dbus_connection_parent_class)->finalize (object); } @@ -483,19 +605,19 @@ g_dbus_connection_set_property (GObject *object, switch (prop_id) { case PROP_STREAM: - connection->priv->stream = g_value_dup_object (value); + connection->stream = g_value_dup_object (value); break; case PROP_GUID: - connection->priv->guid = g_value_dup_string (value); + connection->guid = g_value_dup_string (value); break; case PROP_ADDRESS: - connection->priv->address = g_value_dup_string (value); + connection->address = g_value_dup_string (value); break; case PROP_FLAGS: - connection->priv->flags = g_value_get_flags (value); + connection->flags = g_value_get_flags (value); break; case PROP_EXIT_ON_CLOSE: @@ -503,7 +625,7 @@ g_dbus_connection_set_property (GObject *object, break; case PROP_AUTHENTICATION_OBSERVER: - connection->priv->authentication_observer = g_value_dup_object (value); + connection->authentication_observer = g_value_dup_object (value); break; default: @@ -517,9 +639,19 @@ g_dbus_connection_real_closed (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error) { - if (remote_peer_vanished && connection->priv->exit_on_close) + if (remote_peer_vanished && connection->exit_on_close) { - g_print ("%s: Remote peer vanished. Exiting.\n", G_STRFUNC); + if (error != NULL) + { + g_print ("%s: Remote peer vanished with error: %s (%s, %d). Exiting.\n", + G_STRFUNC, + error->message, + g_quark_to_string (error->domain), error->code); + } + else + { + g_print ("%s: Remote peer vanished. Exiting.\n", G_STRFUNC); + } raise (SIGTERM); } } @@ -529,8 +661,6 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) { GObjectClass *gobject_class; - g_type_class_add_private (klass, sizeof (GDBusConnectionPrivate)); - gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = g_dbus_connection_finalize; @@ -777,43 +907,41 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) static void g_dbus_connection_init (GDBusConnection *connection) { - connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, G_TYPE_DBUS_CONNECTION, GDBusConnectionPrivate); + connection->lock = g_mutex_new (); + connection->init_lock = g_mutex_new (); - connection->priv->lock = g_mutex_new (); - connection->priv->init_lock = g_mutex_new (); + connection->map_method_serial_to_send_message_data = g_hash_table_new (g_direct_hash, g_direct_equal); - connection->priv->map_method_serial_to_send_message_data = g_hash_table_new (g_direct_hash, g_direct_equal); + connection->map_rule_to_signal_data = g_hash_table_new (g_str_hash, + g_str_equal); + connection->map_id_to_signal_data = g_hash_table_new (g_direct_hash, + g_direct_equal); + connection->map_sender_unique_name_to_signal_data_array = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_ptr_array_unref); - connection->priv->map_rule_to_signal_data = g_hash_table_new (g_str_hash, - g_str_equal); - connection->priv->map_id_to_signal_data = g_hash_table_new (g_direct_hash, - g_direct_equal); - connection->priv->map_sender_to_signal_data_array = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - NULL); + connection->map_object_path_to_eo = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify) exported_object_free); - connection->priv->map_object_path_to_eo = g_hash_table_new_full (g_str_hash, - g_str_equal, - NULL, - (GDestroyNotify) exported_object_free); + connection->map_id_to_ei = g_hash_table_new (g_direct_hash, + g_direct_equal); - connection->priv->map_id_to_ei = g_hash_table_new (g_direct_hash, - g_direct_equal); + connection->map_object_path_to_es = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify) exported_subtree_free); - connection->priv->map_object_path_to_es = g_hash_table_new_full (g_str_hash, - g_str_equal, - NULL, - (GDestroyNotify) exported_subtree_free); + connection->map_id_to_es = g_hash_table_new (g_direct_hash, + g_direct_equal); - connection->priv->map_id_to_es = g_hash_table_new (g_direct_hash, - g_direct_equal); + connection->main_context_at_construction = g_main_context_get_thread_default (); + if (connection->main_context_at_construction != NULL) + g_main_context_ref (connection->main_context_at_construction); - connection->priv->main_context_at_construction = g_main_context_get_thread_default (); - if (connection->priv->main_context_at_construction != NULL) - g_main_context_ref (connection->priv->main_context_at_construction); - - connection->priv->filters = g_ptr_array_new (); + connection->filters = g_ptr_array_new (); } /** @@ -830,9 +958,26 @@ GIOStream * g_dbus_connection_get_stream (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); - return connection->priv->stream; + return connection->stream; } +/** + * g_dbus_connection_start_message_processing: + * @connection: A #GDBusConnection. + * + * If @connection was created with + * %G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING, this method + * starts processing messages. Does nothing on if @connection wasn't + * created with this flag or if the method has already been called. + * + * Since: 2.26 + */ +void +g_dbus_connection_start_message_processing (GDBusConnection *connection) +{ + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + _g_dbus_worker_unfreeze (connection->worker); +} /** * g_dbus_connection_is_closed: @@ -848,7 +993,7 @@ gboolean g_dbus_connection_is_closed (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); - return connection->priv->closed; + return connection->closed; } /** @@ -865,9 +1010,155 @@ GDBusCapabilityFlags g_dbus_connection_get_capabilities (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), G_DBUS_CAPABILITY_FLAGS_NONE); - return connection->priv->capabilities; + return connection->capabilities; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +flush_in_thread_func (GSimpleAsyncResult *res, + GObject *object, + GCancellable *cancellable) +{ + GError *error; + + error = NULL; + if (!g_dbus_connection_flush_sync (G_DBUS_CONNECTION (object), + cancellable, + &error)) + { + g_simple_async_result_set_from_error (res, error); + g_error_free (error); + } +} + +/** + * g_dbus_connection_flush: + * @connection: A #GDBusConnection. + * @cancellable: A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't + * care about the result. + * @user_data: The data to pass to @callback. + * + * Asynchronously flushes @connection, that is, writes all queued + * outgoing message to the transport and then flushes the transport + * (using g_output_stream_flush_async()). This is useful in programs + * that wants to emit a D-Bus signal and then exit + * immediately. Without flushing the connection, there is no guarantee + * that the message has been sent to the networking buffers in the OS + * kernel. + * + * This is an asynchronous method. When the operation is finished, + * @callback will be invoked in the thread-default main + * loop of the thread you are calling this method from. You can + * then call g_dbus_connection_flush_finish() to get the result of the + * operation. See g_dbus_connection_flush_sync() for the synchronous + * version. + * + * Since: 2.26 + */ +void +g_dbus_connection_flush (GDBusConnection *connection, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + + simple = g_simple_async_result_new (G_OBJECT (connection), + callback, + user_data, + g_dbus_connection_flush); + g_simple_async_result_run_in_thread (simple, + flush_in_thread_func, + G_PRIORITY_DEFAULT, + cancellable); + g_object_unref (simple); } +/** + * g_dbus_connection_flush_finish: + * @connection: A #GDBusConnection. + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_flush(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_connection_flush(). + * + * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. + * + * Since: 2.26 + */ +gboolean +g_dbus_connection_flush_finish (GDBusConnection *connection, + GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + gboolean ret; + + ret = FALSE; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_connection_flush); + + if (g_simple_async_result_propagate_error (simple, error)) + goto out; + + ret = TRUE; + + out: + return ret; +} + +/** + * g_dbus_connection_flush_sync: + * @connection: A #GDBusConnection. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously flushes @connection. The calling thread is blocked + * until this is done. See g_dbus_connection_flush() for the + * asynchronous version of this method and more details about what it + * does. + * + * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. + * + * Since: 2.26 + */ +gboolean +g_dbus_connection_flush_sync (GDBusConnection *connection, + GCancellable *cancellable, + GError **error) +{ + gboolean ret; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = FALSE; + + if (connection->closed) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_CLOSED, + _("The connection is closed")); + goto out; + } + + ret = _g_dbus_worker_flush_sync (connection->worker, + cancellable, + error); + + out: + return ret; +} /* ---------------------------------------------------------------------------------------------------- */ @@ -914,9 +1205,9 @@ set_closed_unlocked (GDBusConnection *connection, CONNECTION_ENSURE_LOCK (connection); - g_assert (!connection->priv->closed); + g_assert (!connection->closed); - connection->priv->closed = TRUE; + connection->closed = TRUE; data = g_new0 (EmitClosedData, 1); data->connection = g_object_ref (connection); @@ -929,45 +1220,168 @@ set_closed_unlocked (GDBusConnection *connection, emit_closed_in_idle, data, (GDestroyNotify) emit_closed_data_free); - g_source_attach (idle_source, connection->priv->main_context_at_construction); + g_source_attach (idle_source, connection->main_context_at_construction); g_source_unref (idle_source); } /* ---------------------------------------------------------------------------------------------------- */ +static void +close_in_thread_func (GSimpleAsyncResult *res, + GObject *object, + GCancellable *cancellable) +{ + GError *error; + + error = NULL; + if (!g_dbus_connection_close_sync (G_DBUS_CONNECTION (object), + cancellable, + &error)) + { + g_simple_async_result_set_from_error (res, error); + g_error_free (error); + } +} + /** * g_dbus_connection_close: * @connection: A #GDBusConnection. + * @cancellable: A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't + * care about the result. + * @user_data: The data to pass to @callback. * * Closes @connection. Note that this never causes the process to * exit (this might only happen if the other end of a shared message - * bus connection disconnects). + * bus connection disconnects, see #GDBusConnection:exit-on-close). + * + * Once the connection is closed, operations such as sending a message + * will return with the error %G_IO_ERROR_CLOSED. Closing a connection + * will not automatically flush the connection so queued messages may + * be lost. Use g_dbus_connection_flush() if you need such guarantees. * - * If @connection is already closed, this method does nothing. + * If @connection is already closed, this method fails with + * %G_IO_ERROR_CLOSED. + * + * When @connection has been closed, the #GDBusConnection::closed + * signal is emitted in the thread-default main + * loop of the thread that @connection was constructed in. + * + * This is an asynchronous method. When the operation is finished, + * @callback will be invoked in the thread-default main + * loop of the thread you are calling this method from. You can + * then call g_dbus_connection_close_finish() to get the result of the + * operation. See g_dbus_connection_close_sync() for the synchronous + * version. * * Since: 2.26 */ void -g_dbus_connection_close (GDBusConnection *connection) +g_dbus_connection_close (GDBusConnection *connection, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + GSimpleAsyncResult *simple; + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); - CONNECTION_LOCK (connection); - if (!connection->priv->closed) - { - GError *error = NULL; + simple = g_simple_async_result_new (G_OBJECT (connection), + callback, + user_data, + g_dbus_connection_close); + g_simple_async_result_run_in_thread (simple, + close_in_thread_func, + G_PRIORITY_DEFAULT, + cancellable); + g_object_unref (simple); +} - /* TODO: do this async */ - //g_debug ("closing connection %p's stream %p", connection, connection->priv->stream); - if (!g_io_stream_close (connection->priv->stream, NULL, &error)) - { - g_warning ("Error closing stream: %s", error->message); - g_error_free (error); - } +/** + * g_dbus_connection_close_finish: + * @connection: A #GDBusConnection. + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_close(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_connection_close(). + * + * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. + * + * Since: 2.26 + */ +gboolean +g_dbus_connection_close_finish (GDBusConnection *connection, + GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + gboolean ret; + + ret = FALSE; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - set_closed_unlocked (connection, FALSE, NULL); + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_connection_close); + + if (g_simple_async_result_propagate_error (simple, error)) + goto out; + + ret = TRUE; + + out: + return ret; +} + +/** + * g_dbus_connection_close_sync: + * @connection: A #GDBusConnection. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously closees @connection. The calling thread is blocked + * until this is done. See g_dbus_connection_close() for the + * asynchronous version of this method and more details about what it + * does. + * + * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. + * + * Since: 2.26 + */ +gboolean +g_dbus_connection_close_sync (GDBusConnection *connection, + GCancellable *cancellable, + GError **error) +{ + gboolean ret; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = FALSE; + + CONNECTION_LOCK (connection); + if (!connection->closed) + { + ret = g_io_stream_close (connection->stream, + cancellable, + error); + if (ret) + set_closed_unlocked (connection, FALSE, NULL); + } + else + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_CLOSED, + _("The connection is closed")); } CONNECTION_UNLOCK (connection); + + return ret; } /* ---------------------------------------------------------------------------------------------------- */ @@ -975,6 +1389,7 @@ g_dbus_connection_close (GDBusConnection *connection) static gboolean g_dbus_connection_send_message_unlocked (GDBusConnection *connection, GDBusMessage *message, + GDBusSendMessageFlags flags, volatile guint32 *out_serial, GError **error) { @@ -996,7 +1411,7 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, if (out_serial != NULL) *out_serial = 0; - if (connection->priv->closed) + if (connection->closed) { g_set_error_literal (error, G_IO_ERROR, @@ -1007,12 +1422,15 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, blob = g_dbus_message_to_blob (message, &blob_size, - connection->priv->capabilities, + connection->capabilities, error); if (blob == NULL) goto out; - serial_to_use = ++connection->priv->last_serial; /* TODO: handle overflow */ + if (flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) + serial_to_use = g_dbus_message_get_serial (message); + else + serial_to_use = ++connection->last_serial; /* TODO: handle overflow */ switch (blob[0]) { @@ -1035,14 +1453,15 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, g_printerr ("----\n"); #endif - /* TODO: use connection->priv->auth to encode the blob */ + /* TODO: use connection->auth to encode the blob */ if (out_serial != NULL) *out_serial = serial_to_use; g_dbus_message_set_serial (message, serial_to_use); - _g_dbus_worker_send_message (connection->priv->worker, + g_dbus_message_lock (message); + _g_dbus_worker_send_message (connection->worker, message, (gchar*) blob, blob_size); @@ -1060,24 +1479,30 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, * g_dbus_connection_send_message: * @connection: A #GDBusConnection. * @message: A #GDBusMessage + * @flags: Flags affecting how the message is sent. * @out_serial: Return location for serial number assigned to @message when sending it or %NULL. * @error: Return location for error or %NULL. * * Asynchronously sends @message to the peer represented by @connection. * - * If @out_serial is not %NULL, then the serial number assigned to - * @message by @connection will be written to this location prior to + * Unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number + * will be assigned by @connection and set on @message via + * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the + * serial number used will be written to this location prior to * submitting the message to the underlying transport. * * If @connection is closed then the operation will fail with - * %G_IO_ERROR_CLOSED. If @cancellable is canceled, the operation will - * fail with %G_IO_ERROR_CANCELLED. If @message is not well-formed, + * %G_IO_ERROR_CLOSED. If @message is not well-formed, * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT. * * See and for an example of how to use this * low-level API to send and receive UNIX file descriptors. * + * Note that @message must be unlocked, unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag. + * * Returns: %TRUE if the message was well-formed and queued for * transmission, %FALSE if @error is set. * @@ -1086,6 +1511,7 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, gboolean g_dbus_connection_send_message (GDBusConnection *connection, GDBusMessage *message, + GDBusSendMessageFlags flags, volatile guint32 *out_serial, GError **error) { @@ -1093,10 +1519,11 @@ g_dbus_connection_send_message (GDBusConnection *connection, g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE); + g_return_val_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); CONNECTION_LOCK (connection); - ret = g_dbus_connection_send_message_unlocked (connection, message, out_serial, error); + ret = g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, error); CONNECTION_UNLOCK (connection); return ret; } @@ -1172,7 +1599,7 @@ send_message_with_reply_deliver (SendMessageData *data) data->cancellable_handler_id = 0; } - g_warn_if_fail (g_hash_table_remove (data->connection->priv->map_method_serial_to_send_message_data, + g_warn_if_fail (g_hash_table_remove (data->connection->map_method_serial_to_send_message_data, GUINT_TO_POINTER (data->serial))); send_message_data_unref (data); @@ -1271,6 +1698,7 @@ send_message_with_reply_timeout_cb (gpointer user_data) static void g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connection, GDBusMessage *message, + GDBusSendMessageFlags flags, gint timeout_msec, volatile guint32 *out_serial, GCancellable *cancellable, @@ -1288,7 +1716,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect out_serial = &serial; if (timeout_msec == -1) - timeout_msec = 30 * 1000; /* TODO: check 30 secs is the default timeout */ + timeout_msec = 25 * 1000; simple = g_simple_async_result_new (G_OBJECT (connection), callback, @@ -1306,7 +1734,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect goto out; } - if (connection->priv->closed) + if (connection->closed) { g_simple_async_result_set_error (simple, G_IO_ERROR, @@ -1318,7 +1746,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect } error = NULL; - if (!g_dbus_connection_send_message_unlocked (connection, message, out_serial, &error)) + if (!g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, &error)) { g_simple_async_result_set_from_error (simple, error); g_simple_async_result_complete_in_idle (simple); @@ -1357,7 +1785,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect g_source_attach (data->timeout_source, data->main_context); g_source_unref (data->timeout_source); - g_hash_table_insert (connection->priv->map_method_serial_to_send_message_data, + g_hash_table_insert (connection->map_method_serial_to_send_message_data, GUINT_TO_POINTER (*out_serial), data); @@ -1369,6 +1797,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect * g_dbus_connection_send_message_with_reply: * @connection: A #GDBusConnection. * @message: A #GDBusMessage. + * @flags: Flags affecting how the message is sent. * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. * @out_serial: Return location for serial number assigned to @message when sending it or %NULL. * @cancellable: A #GCancellable or %NULL. @@ -1378,8 +1807,11 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect * * Asynchronously sends @message to the peer represented by @connection. * - * If @out_serial is not %NULL, then the serial number assigned to - * @message by @connection will be written to this location prior to + * Unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number + * will be assigned by @connection and set on @message via + * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the + * serial number used will be written to this location prior to * submitting the message to the underlying transport. * * If @connection is closed then the operation will fail with @@ -1393,6 +1825,9 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect * g_dbus_connection_send_message_with_reply_finish() to get the result of the operation. * See g_dbus_connection_send_message_with_reply_sync() for the synchronous version. * + * Note that @message must be unlocked, unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag. + * * See and for an example of how to use this * low-level API to send and receive UNIX file descriptors. @@ -1402,6 +1837,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect void g_dbus_connection_send_message_with_reply (GDBusConnection *connection, GDBusMessage *message, + GDBusSendMessageFlags flags, gint timeout_msec, volatile guint32 *out_serial, GCancellable *cancellable, @@ -1410,11 +1846,13 @@ g_dbus_connection_send_message_with_reply (GDBusConnection *connection, { g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message)); g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1); CONNECTION_LOCK (connection); g_dbus_connection_send_message_with_reply_unlocked (connection, message, + flags, timeout_msec, out_serial, cancellable, @@ -1440,7 +1878,7 @@ g_dbus_connection_send_message_with_reply (GDBusConnection *connection, * linkend="gdbus-unix-fd-client"/> for an example of how to use this * low-level API to send and receive UNIX file descriptors. * - * Returns: A #GDBusMessage or %NULL if @error is set. + * Returns: A locked #GDBusMessage or %NULL if @error is set. * * Since: 2.26 */ @@ -1501,6 +1939,7 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, * g_dbus_connection_send_message_with_reply_sync: * @connection: A #GDBusConnection. * @message: A #GDBusMessage. + * @flags: Flags affecting how the message is sent. * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. * @out_serial: Return location for serial number assigned to @message when sending it or %NULL. * @cancellable: A #GCancellable or %NULL. @@ -1511,8 +1950,11 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, * timeout is reached. See g_dbus_connection_send_message_with_reply() * for the asynchronous version of this method. * - * If @out_serial is not %NULL, then the serial number assigned to - * @message by @connection will be written to this location prior to + * Unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number + * will be assigned by @connection and set on @message via + * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the + * serial number used will be written to this location prior to * submitting the message to the underlying transport. * * If @connection is closed then the operation will fail with @@ -1529,13 +1971,17 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, * linkend="gdbus-unix-fd-client"/> for an example of how to use this * low-level API to send and receive UNIX file descriptors. * - * Returns: A #GDBusMessage that is the reply to @message or %NULL if @error is set. + * Note that @message must be unlocked, unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag. + * + * Returns: A locked #GDBusMessage that is the reply to @message or %NULL if @error is set. * * Since: 2.26 */ GDBusMessage * g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, GDBusMessage *message, + GDBusSendMessageFlags flags, gint timeout_msec, volatile guint32 *out_serial, GCancellable *cancellable, @@ -1546,6 +1992,7 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + g_return_val_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message), FALSE); g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); @@ -1557,6 +2004,7 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, g_dbus_connection_send_message_with_reply (connection, message, + flags, timeout_msec, out_serial, cancellable, @@ -1599,41 +2047,62 @@ on_worker_message_received (GDBusWorker *worker, GDBusMessage *message, gpointer user_data) { - GDBusConnection *connection = G_DBUS_CONNECTION (user_data); + GDBusConnection *connection; FilterCallback *filters; gboolean consumed_by_filter; + gboolean altered_by_filter; guint num_filters; guint n; + gboolean alive; + + G_LOCK (message_bus_lock); + alive = (g_hash_table_lookup (alive_connections, user_data) != NULL); + if (!alive) + { + G_UNLOCK (message_bus_lock); + return; + } + connection = G_DBUS_CONNECTION (user_data); + g_object_ref (connection); + G_UNLOCK (message_bus_lock); //g_debug ("in on_worker_message_received"); - g_object_ref (connection); + g_object_ref (message); + g_dbus_message_lock (message); + + //g_debug ("boo ref_count = %d %p %p", G_OBJECT (connection)->ref_count, connection, connection->worker); /* First collect the set of callback functions */ CONNECTION_LOCK (connection); - num_filters = connection->priv->filters->len; + num_filters = connection->filters->len; filters = g_new0 (FilterCallback, num_filters); for (n = 0; n < num_filters; n++) { - FilterData *data = connection->priv->filters->pdata[n]; + FilterData *data = connection->filters->pdata[n]; filters[n].func = data->filter_function; filters[n].user_data = data->user_data; } CONNECTION_UNLOCK (connection); - /* the call the filters in order (without holding the lock) */ + /* then call the filters in order (without holding the lock) */ consumed_by_filter = FALSE; + altered_by_filter = FALSE; for (n = 0; n < num_filters; n++) { - consumed_by_filter = filters[n].func (connection, - message, - filters[n].user_data); - if (consumed_by_filter) + message = filters[n].func (connection, + message, + TRUE, + filters[n].user_data); + if (message == NULL) break; + g_dbus_message_lock (message); } - /* Standard dispatch unless the filter ate the message */ - if (!consumed_by_filter) + /* Standard dispatch unless the filter ate the message - no need to + * do anything if the message was altered + */ + if (message != NULL) { GDBusMessageType message_type; @@ -1645,7 +2114,7 @@ on_worker_message_received (GDBusWorker *worker, reply_serial = g_dbus_message_get_reply_serial (message); CONNECTION_LOCK (connection); - send_message_data = g_hash_table_lookup (connection->priv->map_method_serial_to_send_message_data, + send_message_data = g_hash_table_lookup (connection->map_method_serial_to_send_message_data, GUINT_TO_POINTER (reply_serial)); if (send_message_data != NULL) { @@ -1672,8 +2141,65 @@ on_worker_message_received (GDBusWorker *worker, } } + if (message != NULL) + g_object_unref (message); + g_object_unref (connection); + g_free (filters); +} + +/* Called in worker's thread */ +static GDBusMessage * +on_worker_message_about_to_be_sent (GDBusWorker *worker, + GDBusMessage *message, + gpointer user_data) +{ + GDBusConnection *connection; + FilterCallback *filters; + guint num_filters; + guint n; + gboolean alive; + + G_LOCK (message_bus_lock); + alive = (g_hash_table_lookup (alive_connections, user_data) != NULL); + if (!alive) + { + G_UNLOCK (message_bus_lock); + return message; + } + connection = G_DBUS_CONNECTION (user_data); + g_object_ref (connection); + G_UNLOCK (message_bus_lock); + + //g_debug ("in on_worker_message_about_to_be_sent"); + + /* First collect the set of callback functions */ + CONNECTION_LOCK (connection); + num_filters = connection->filters->len; + filters = g_new0 (FilterCallback, num_filters); + for (n = 0; n < num_filters; n++) + { + FilterData *data = connection->filters->pdata[n]; + filters[n].func = data->filter_function; + filters[n].user_data = data->user_data; + } + CONNECTION_UNLOCK (connection); + + /* then call the filters in order (without holding the lock) */ + for (n = 0; n < num_filters; n++) + { + g_dbus_message_lock (message); + message = filters[n].func (connection, + message, + FALSE, + filters[n].user_data); + if (message == NULL) + break; + } + g_object_unref (connection); g_free (filters); + + return message; } /* Called in worker's thread - we must not block */ @@ -1683,14 +2209,28 @@ on_worker_closed (GDBusWorker *worker, GError *error, gpointer user_data) { - GDBusConnection *connection = G_DBUS_CONNECTION (user_data); + GDBusConnection *connection; + gboolean alive; + + G_LOCK (message_bus_lock); + alive = (g_hash_table_lookup (alive_connections, user_data) != NULL); + if (!alive) + { + G_UNLOCK (message_bus_lock); + return; + } + connection = G_DBUS_CONNECTION (user_data); + g_object_ref (connection); + G_UNLOCK (message_bus_lock); //g_debug ("in on_worker_closed: %s", error->message); CONNECTION_LOCK (connection); - if (!connection->priv->closed) + if (!connection->closed) set_closed_unlocked (connection, remote_peer_vanished, error); CONNECTION_UNLOCK (connection); + + g_object_unref (connection); } /* ---------------------------------------------------------------------------------------------------- */ @@ -1702,7 +2242,7 @@ get_offered_capabilities_max (GDBusConnection *connection) GDBusCapabilityFlags ret; ret = G_DBUS_CAPABILITY_FLAGS_NONE; #ifdef G_OS_UNIX - if (G_IS_UNIX_CONNECTION (connection->priv->stream)) + if (G_IS_UNIX_CONNECTION (connection->stream)) ret |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING; #endif return ret; @@ -1724,19 +2264,19 @@ initable_init (GInitable *initable, * callbacks above needs the lock during initialization (for message * bus connections we do a synchronous Hello() call on the bus). */ - g_mutex_lock (connection->priv->init_lock); + g_mutex_lock (connection->init_lock); ret = FALSE; - if (connection->priv->is_initialized) + if (connection->is_initialized) { - if (connection->priv->stream != NULL) + if (connection->stream != NULL) ret = TRUE; else - g_assert (connection->priv->initialization_error != NULL); + g_assert (connection->initialization_error != NULL); goto out; } - g_assert (connection->priv->initialization_error == NULL); + g_assert (connection->initialization_error == NULL); /* The user can pass multiple (but mutally exclusive) construct * properties: @@ -1745,14 +2285,14 @@ initable_init (GInitable *initable, * - address (of type gchar*) * * At the end of the day we end up with a non-NULL GIOStream - * object in connection->priv->stream. + * object in connection->stream. */ - if (connection->priv->address != NULL) + if (connection->address != NULL) { - g_assert (connection->priv->stream == NULL); + g_assert (connection->stream == NULL); - if ((connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) || - (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS)) + if ((connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) || + (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS)) { g_set_error_literal (error, G_IO_ERROR, @@ -1761,14 +2301,14 @@ initable_init (GInitable *initable, goto out; } - connection->priv->stream = g_dbus_address_get_stream_sync (connection->priv->address, - NULL, /* TODO: out_guid */ - cancellable, - &connection->priv->initialization_error); - if (connection->priv->stream == NULL) + connection->stream = g_dbus_address_get_stream_sync (connection->address, + NULL, /* TODO: out_guid */ + cancellable, + &connection->initialization_error); + if (connection->stream == NULL) goto out; } - else if (connection->priv->stream != NULL) + else if (connection->stream != NULL) { /* nothing to do */ } @@ -1778,97 +2318,118 @@ initable_init (GInitable *initable, } /* Authenticate the connection */ - if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) - { - g_assert (!(connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT)); - g_assert (connection->priv->guid != NULL); - connection->priv->auth = _g_dbus_auth_new (connection->priv->stream); - if (!_g_dbus_auth_run_server (connection->priv->auth, - connection->priv->authentication_observer, - connection->priv->guid, - (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS), + if (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) + { + g_assert (!(connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT)); + g_assert (connection->guid != NULL); + connection->auth = _g_dbus_auth_new (connection->stream); + if (!_g_dbus_auth_run_server (connection->auth, + connection->authentication_observer, + connection->guid, + (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS), get_offered_capabilities_max (connection), - &connection->priv->capabilities, - &connection->priv->crendentials, + &connection->capabilities, + &connection->credentials, cancellable, - &connection->priv->initialization_error)) + &connection->initialization_error)) goto out; } - else if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT) - { - g_assert (!(connection->priv->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER)); - g_assert (connection->priv->guid == NULL); - connection->priv->auth = _g_dbus_auth_new (connection->priv->stream); - connection->priv->guid = _g_dbus_auth_run_client (connection->priv->auth, - get_offered_capabilities_max (connection), - &connection->priv->capabilities, - cancellable, - &connection->priv->initialization_error); - if (connection->priv->guid == NULL) + else if (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT) + { + g_assert (!(connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER)); + g_assert (connection->guid == NULL); + connection->auth = _g_dbus_auth_new (connection->stream); + connection->guid = _g_dbus_auth_run_client (connection->auth, + get_offered_capabilities_max (connection), + &connection->capabilities, + cancellable, + &connection->initialization_error); + if (connection->guid == NULL) goto out; } - if (connection->priv->authentication_observer != NULL) + if (connection->authentication_observer != NULL) { - g_object_unref (connection->priv->authentication_observer); - connection->priv->authentication_observer = NULL; + g_object_unref (connection->authentication_observer); + connection->authentication_observer = NULL; } - //g_output_stream_flush (G_SOCKET_CONNECTION (connection->priv->stream) + //g_output_stream_flush (G_SOCKET_CONNECTION (connection->stream) - //g_debug ("haz unix fd passing powers: %d", connection->priv->capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); + //g_debug ("haz unix fd passing powers: %d", connection->capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); +#ifdef G_OS_UNIX /* Hack used until * * https://bugzilla.gnome.org/show_bug.cgi?id=616458 * * has been resolved */ - if (G_IS_SOCKET_CONNECTION (connection->priv->stream)) + if (G_IS_SOCKET_CONNECTION (connection->stream)) { - g_socket_set_blocking (g_socket_connection_get_socket (G_SOCKET_CONNECTION (connection->priv->stream)), FALSE); + g_socket_set_blocking (g_socket_connection_get_socket (G_SOCKET_CONNECTION (connection->stream)), FALSE); } +#endif - connection->priv->worker = _g_dbus_worker_new (connection->priv->stream, - connection->priv->capabilities, - on_worker_message_received, - on_worker_closed, - connection); + G_LOCK (message_bus_lock); + if (alive_connections == NULL) + alive_connections = g_hash_table_new (g_direct_hash, g_direct_equal); + g_hash_table_insert (alive_connections, connection, connection); + G_UNLOCK (message_bus_lock); - /* if a bus connection, invoke org.freedesktop.DBus.Hello - this is how we're getting a name */ - if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) + connection->worker = _g_dbus_worker_new (connection->stream, + connection->capabilities, + (connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING), + on_worker_message_received, + on_worker_message_about_to_be_sent, + on_worker_closed, + connection); + + /* if a bus connection, call org.freedesktop.DBus.Hello - this is how we're getting a name */ + if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) { GVariant *hello_result; + /* we could lift this restriction by adding code in gdbusprivate.c */ + if (connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING) + { + g_set_error_literal (&connection->initialization_error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Cannot use DELAY_MESSAGE_PROCESSING with MESSAGE_BUS_CONNECTION"); + goto out; + } + hello_result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", /* name */ "/org/freedesktop/DBus", /* path */ "org.freedesktop.DBus", /* interface */ "Hello", NULL, /* parameters */ + G_VARIANT_TYPE ("(s)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, /* TODO: cancellable */ - &connection->priv->initialization_error); + &connection->initialization_error); if (hello_result == NULL) goto out; - g_variant_get (hello_result, "(s)", &connection->priv->bus_unique_name); + g_variant_get (hello_result, "(s)", &connection->bus_unique_name); g_variant_unref (hello_result); - //g_debug ("unique name is `%s'", connection->priv->bus_unique_name); + //g_debug ("unique name is `%s'", connection->bus_unique_name); } - connection->priv->is_initialized = TRUE; + connection->is_initialized = TRUE; ret = TRUE; out: if (!ret) { - g_assert (connection->priv->initialization_error != NULL); - g_propagate_error (error, g_error_copy (connection->priv->initialization_error)); + g_assert (connection->initialization_error != NULL); + g_propagate_error (error, g_error_copy (connection->initialization_error)); } - g_mutex_unlock (connection->priv->init_lock); + g_mutex_unlock (connection->init_lock); return ret; } @@ -1882,53 +2443,9 @@ initable_iface_init (GInitableIface *initable_iface) /* ---------------------------------------------------------------------------------------------------- */ static void -async_init_thread (GSimpleAsyncResult *res, - GObject *object, - GCancellable *cancellable) -{ - GError *error = NULL; - - if (!g_initable_init (G_INITABLE (object), cancellable, &error)) - { - g_simple_async_result_set_from_error (res, error); - g_error_free (error); - } -} - -static void -async_initable_init_async (GAsyncInitable *initable, - gint io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *res; - - g_return_if_fail (G_IS_INITABLE (initable)); - - res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, - async_initable_init_async); - g_simple_async_result_run_in_thread (res, async_init_thread, - io_priority, cancellable); - g_object_unref (res); -} - -static gboolean -async_initable_init_finish (GAsyncInitable *initable, - GAsyncResult *res, - GError **error) -{ - return TRUE; /* Errors handled by base impl */ -} - -static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface) { - /* We basically just want to use GIO's default implementation - though that one is - * unfortunately broken, see #615111. So we copy-paste a fixed-up version. - */ - async_initable_iface->init_async = async_initable_init_async; - async_initable_iface->init_finish = async_initable_init_finish; + /* Use default */ } /* ---------------------------------------------------------------------------------------------------- */ @@ -2208,7 +2725,7 @@ g_dbus_connection_set_exit_on_close (GDBusConnection *connection, gboolean exit_on_close) { g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); - connection->priv->exit_on_close = exit_on_close; + connection->exit_on_close = exit_on_close; } /** @@ -2228,7 +2745,7 @@ gboolean g_dbus_connection_get_exit_on_close (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); - return connection->priv->exit_on_close; + return connection->exit_on_close; } /** @@ -2247,7 +2764,7 @@ const gchar * g_dbus_connection_get_guid (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); - return connection->priv->guid; + return connection->guid; } /** @@ -2268,7 +2785,7 @@ const gchar * g_dbus_connection_get_unique_name (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); - return connection->priv->bus_unique_name; + return connection->bus_unique_name; } /** @@ -2294,7 +2811,7 @@ GCredentials * g_dbus_connection_get_peer_credentials (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); - return connection->priv->crendentials; + return connection->credentials; } /* ---------------------------------------------------------------------------------------------------- */ @@ -2310,11 +2827,13 @@ static guint _global_filter_id = 1; * is removed or %NULL. * * Adds a message filter. Filters are handlers that are run on all - * incoming messages, prior to standard dispatch. Filters are run in - * the order that they were added. The same handler can be added as a - * filter more than once, in which case it will be run more than once. - * Filters added during a filter callback won't be run on the message - * being processed. + * incoming and outgoing messages, prior to standard dispatch. Filters + * are run in the order that they were added. The same handler can be + * added as a filter more than once, in which case it will be run more + * than once. Filters added during a filter callback won't be run on + * the message being processed. Filter functions are allowed to modify + * and even drop messages - see the #GDBusMessageFilterResult + * enumeration for details. * * Note that filters are run in a dedicated message handling thread so * they can't block and, generally, can't do anything but signal a @@ -2323,6 +2842,13 @@ static guint _global_filter_id = 1; * g_dbus_connection_signal_subscribe() or * g_dbus_connection_call() instead. * + * If a filter consumes an incoming message the message is not + * dispatched anywhere else - not even the standard dispatch machinery + * (that API such as g_dbus_connection_signal_subscribe() and + * g_dbus_connection_send_message_with_reply() relies on) will see the + * message. Similary, if a filter consumes an outgoing message, the + * message will not be sent to the other peer. + * * Returns: A filter identifier that can be used with * g_dbus_connection_remove_filter(). * @@ -2345,7 +2871,7 @@ g_dbus_connection_add_filter (GDBusConnection *connection, data->filter_function = filter_function; data->user_data = user_data; data->user_data_free_func = user_data_free_func; - g_ptr_array_add (connection->priv->filters, data); + g_ptr_array_add (connection->filters, data); CONNECTION_UNLOCK (connection); return data->id; @@ -2356,9 +2882,9 @@ static void purge_all_filters (GDBusConnection *connection) { guint n; - for (n = 0; n < connection->priv->filters->len; n++) + for (n = 0; n < connection->filters->len; n++) { - FilterData *data = connection->priv->filters->pdata[n]; + FilterData *data = connection->filters->pdata[n]; if (data->user_data_free_func != NULL) data->user_data_free_func (data->user_data); g_free (data); @@ -2385,12 +2911,12 @@ g_dbus_connection_remove_filter (GDBusConnection *connection, CONNECTION_LOCK (connection); to_destroy = NULL; - for (n = 0; n < connection->priv->filters->len; n++) + for (n = 0; n < connection->filters->len; n++) { - FilterData *data = connection->priv->filters->pdata[n]; + FilterData *data = connection->filters->pdata[n]; if (data->id == filter_id) { - g_ptr_array_remove_index (connection->priv->filters, n); + g_ptr_array_remove_index (connection->filters, n); to_destroy = data; break; } @@ -2416,6 +2942,7 @@ typedef struct { gchar *rule; gchar *sender; + gchar *sender_unique_name; /* if sender is unique or org.freedesktop.DBus, then that name... otherwise blank */ gchar *interface_name; gchar *member; gchar *object_path; @@ -2433,16 +2960,17 @@ typedef struct } SignalSubscriber; static void -signal_data_free (SignalData *data) -{ - g_free (data->rule); - g_free (data->sender); - g_free (data->interface_name); - g_free (data->member); - g_free (data->object_path); - g_free (data->arg0); - g_array_free (data->subscribers, TRUE); - g_free (data); +signal_data_free (SignalData *signal_data) +{ + g_free (signal_data->rule); + g_free (signal_data->sender); + g_free (signal_data->sender_unique_name); + g_free (signal_data->interface_name); + g_free (signal_data->member); + g_free (signal_data->object_path); + g_free (signal_data->arg0); + g_array_free (signal_data->subscribers, TRUE); + g_free (signal_data); } static gchar * @@ -2488,10 +3016,10 @@ add_match_rule (GDBusConnection *connection, "org.freedesktop.DBus", /* interface */ "AddMatch"); g_dbus_message_set_body (message, g_variant_new ("(s)", match_rule)); - error = NULL; if (!g_dbus_connection_send_message_unlocked (connection, message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, &error)) { @@ -2520,6 +3048,7 @@ remove_match_rule (GDBusConnection *connection, error = NULL; if (!g_dbus_connection_send_message_unlocked (connection, message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, &error)) { @@ -2534,7 +3063,7 @@ remove_match_rule (GDBusConnection *connection, static gboolean is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) { - return g_strcmp0 (signal_data->sender, "org.freedesktop.DBus") == 0 && + return g_strcmp0 (signal_data->sender_unique_name, "org.freedesktop.DBus") == 0 && g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0 && g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0 && (g_strcmp0 (signal_data->member, "NameLost") == 0 || @@ -2546,11 +3075,12 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) /** * g_dbus_connection_signal_subscribe: * @connection: A #GDBusConnection. - * @sender: Sender name to match on. Must be either org.freedesktop.DBus (for listening to signals from the message bus daemon) or a unique name or %NULL to listen from all senders. + * @sender: Sender name to match on (unique or well-known name) or %NULL to listen from all senders. * @interface_name: D-Bus interface name to match on or %NULL to match on all interfaces. * @member: D-Bus signal name to match on or %NULL to match on all signals. * @object_path: Object path to match on or %NULL to match on all object paths. * @arg0: Contents of first string argument to match on or %NULL to match on all kinds of arguments. + * @flags: Flags describing how to subscribe to the signal (currently unused). * @callback: Callback to invoke when there is a signal matching the requested data. * @user_data: User data to pass to @callback. * @user_data_free_func: Function to free @user_data with when subscription is removed or %NULL. @@ -2561,14 +3091,15 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) * linkend="g-main-context-push-thread-default">thread-default main * loop of the thread you are calling this method from. * - * It is considered a programming error to use this function if @connection is closed. + * If @connection is not a message bus connection, @sender must be + * %NULL. * - * Note that if @sender is not org.freedesktop.DBus (for listening to signals from the - * message bus daemon), then it needs to be a unique bus name or %NULL (for listening to signals from any - * name) - you cannot pass a name like com.example.MyApp. - * Use e.g. g_bus_watch_name() to find the unique name for the owner of the name you are interested in. Also note - * that this function does not remove a subscription if @sender vanishes from the bus. You have to manually - * call g_dbus_connection_signal_unsubscribe() to remove a subscription. + * If @sender is a well-known name note that @callback is invoked with + * the unique name for the owner of @sender, not the well-known name + * as one would expect. This is because the message bus rewrites the + * name. As such, to avoid certain race conditions, users should be + * tracking the name owner of the well-known name and use that when + * processing the received signal. * * Returns: A subscription identifier that can be used with g_dbus_connection_signal_unsubscribe(). * @@ -2581,6 +3112,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, const gchar *member, const gchar *object_path, const gchar *arg0, + GDBusSignalFlags flags, GDBusSignalCallback callback, gpointer user_data, GDestroyNotify user_data_free_func) @@ -2589,6 +3121,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, SignalData *signal_data; SignalSubscriber subscriber; GPtrArray *signal_data_array; + const gchar *sender_unique_name; /* Right now we abort if AddMatch() fails since it can only fail with the bus being in * an OOM condition. We might want to change that but that would involve making @@ -2601,9 +3134,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, */ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); - g_return_val_if_fail (!g_dbus_connection_is_closed (connection), 0); - g_return_val_if_fail (sender == NULL || ((strcmp (sender, "org.freedesktop.DBus") == 0 || sender[0] == ':') && - (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)), 0); + g_return_val_if_fail (sender == NULL || (g_dbus_is_name (sender) && (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)), 0); g_return_val_if_fail (interface_name == NULL || g_dbus_is_interface_name (interface_name), 0); g_return_val_if_fail (member == NULL || g_dbus_is_member_name (member), 0); g_return_val_if_fail (object_path == NULL || g_variant_is_object_path (object_path), 0); @@ -2613,8 +3144,10 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, rule = args_to_rule (sender, interface_name, member, object_path, arg0); - if (sender == NULL) - sender = ""; + if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0)) + sender_unique_name = sender; + else + sender_unique_name = ""; subscriber.callback = callback; subscriber.user_data = user_data; @@ -2625,7 +3158,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, g_main_context_ref (subscriber.context); /* see if we've already have this rule */ - signal_data = g_hash_table_lookup (connection->priv->map_rule_to_signal_data, rule); + signal_data = g_hash_table_lookup (connection->map_rule_to_signal_data, rule); if (signal_data != NULL) { g_array_append_val (signal_data->subscribers, subscriber); @@ -2634,16 +3167,17 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, } signal_data = g_new0 (SignalData, 1); - signal_data->rule = rule; - signal_data->sender = g_strdup (sender); - signal_data->interface_name = g_strdup (interface_name); - signal_data->member = g_strdup (member); - signal_data->object_path = g_strdup (object_path); - signal_data->arg0 = g_strdup (arg0); - signal_data->subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); + signal_data->rule = rule; + signal_data->sender = g_strdup (sender); + signal_data->sender_unique_name = g_strdup (sender_unique_name); + signal_data->interface_name = g_strdup (interface_name); + signal_data->member = g_strdup (member); + signal_data->object_path = g_strdup (object_path); + signal_data->arg0 = g_strdup (arg0); + signal_data->subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); g_array_append_val (signal_data->subscribers, subscriber); - g_hash_table_insert (connection->priv->map_rule_to_signal_data, + g_hash_table_insert (connection->map_rule_to_signal_data, signal_data->rule, signal_data); @@ -2652,28 +3186,28 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, * Avoid adding match rules for NameLost and NameAcquired messages - the bus will * always send such messages to us. */ - if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) + if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) { if (!is_signal_data_for_name_lost_or_acquired (signal_data)) add_match_rule (connection, signal_data->rule); } - out: - g_hash_table_insert (connection->priv->map_id_to_signal_data, - GUINT_TO_POINTER (subscriber.id), - signal_data); - - signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, - signal_data->sender); + signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, + signal_data->sender_unique_name); if (signal_data_array == NULL) { signal_data_array = g_ptr_array_new (); - g_hash_table_insert (connection->priv->map_sender_to_signal_data_array, - g_strdup (signal_data->sender), + g_hash_table_insert (connection->map_sender_unique_name_to_signal_data_array, + g_strdup (signal_data->sender_unique_name), signal_data_array); } g_ptr_array_add (signal_data_array, signal_data); + out: + g_hash_table_insert (connection->map_id_to_signal_data, + GUINT_TO_POINTER (subscriber.id), + signal_data); + CONNECTION_UNLOCK (connection); return subscriber.id; @@ -2681,7 +3215,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ -/* must hold lock when calling this */ +/* must hold lock when calling this (except if connection->finalizing is TRUE) */ static void unsubscribe_id_internal (GDBusConnection *connection, guint subscription_id, @@ -2691,7 +3225,7 @@ unsubscribe_id_internal (GDBusConnection *connection, GPtrArray *signal_data_array; guint n; - signal_data = g_hash_table_lookup (connection->priv->map_id_to_signal_data, + signal_data = g_hash_table_lookup (connection->map_id_to_signal_data, GUINT_TO_POINTER (subscription_id)); if (signal_data == NULL) { @@ -2707,30 +3241,33 @@ unsubscribe_id_internal (GDBusConnection *connection, if (subscriber->id != subscription_id) continue; - g_warn_if_fail (g_hash_table_remove (connection->priv->map_id_to_signal_data, + g_warn_if_fail (g_hash_table_remove (connection->map_id_to_signal_data, GUINT_TO_POINTER (subscription_id))); g_array_append_val (out_removed_subscribers, *subscriber); g_array_remove_index (signal_data->subscribers, n); if (signal_data->subscribers->len == 0) - g_warn_if_fail (g_hash_table_remove (connection->priv->map_rule_to_signal_data, signal_data->rule)); + { + g_warn_if_fail (g_hash_table_remove (connection->map_rule_to_signal_data, signal_data->rule)); - signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, - signal_data->sender); - g_warn_if_fail (signal_data_array != NULL); - g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); + signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, + signal_data->sender_unique_name); + g_warn_if_fail (signal_data_array != NULL); + g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); - if (signal_data_array->len == 0) - { - g_warn_if_fail (g_hash_table_remove (connection->priv->map_sender_to_signal_data_array, signal_data->sender)); + if (signal_data_array->len == 0) + { + g_warn_if_fail (g_hash_table_remove (connection->map_sender_unique_name_to_signal_data_array, + signal_data->sender_unique_name)); + } /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */ - if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) + if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) { if (!is_signal_data_for_name_lost_or_acquired (signal_data)) - remove_match_rule (connection, signal_data->rule); + if (!connection->closed && !connection->finalizing) + remove_match_rule (connection, signal_data->rule); } - signal_data_free (signal_data); } @@ -2777,8 +3314,9 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, { SignalSubscriber *subscriber; subscriber = &(g_array_index (subscribers, SignalSubscriber, n)); - if (subscriber->user_data_free_func != NULL) - subscriber->user_data_free_func (subscriber->user_data); + call_destroy_notify (subscriber->context, + subscriber->user_data_free_func, + subscriber->user_data); if (subscriber->context != NULL) g_main_context_unref (subscriber->context); } @@ -2823,7 +3361,8 @@ emit_signal_instance_in_idle_cb (gpointer data) } #if 0 - g_debug ("in emit_signal_instance_in_idle_cb (sender=%s path=%s interface=%s member=%s params=%s)", + g_print ("in emit_signal_instance_in_idle_cb (id=%d sender=%s path=%s interface=%s member=%s params=%s)\n", + signal_instance->subscription_id, signal_instance->sender, signal_instance->path, signal_instance->interface, @@ -2834,7 +3373,7 @@ emit_signal_instance_in_idle_cb (gpointer data) /* Careful here, don't do the callback if we no longer has the subscription */ CONNECTION_LOCK (signal_instance->connection); has_subscription = FALSE; - if (g_hash_table_lookup (signal_instance->connection->priv->map_id_to_signal_data, + if (g_hash_table_lookup (signal_instance->connection->map_id_to_signal_data, GUINT_TO_POINTER (signal_instance->subscription_id)) != NULL) has_subscription = TRUE; CONNECTION_UNLOCK (signal_instance->connection); @@ -2886,11 +3425,17 @@ schedule_callbacks (GDBusConnection *connection, arg0 = g_dbus_message_get_arg0 (message); #if 0 - g_debug ("sender = `%s'", sender); - g_debug ("interface = `%s'", interface); - g_debug ("member = `%s'", member); - g_debug ("path = `%s'", path); - g_debug ("arg0 = `%s'", arg0); + g_print ("In schedule_callbacks:\n" + " sender = `%s'\n" + " interface = `%s'\n" + " member = `%s'\n" + " path = `%s'\n" + " arg0 = `%s'\n", + sender, + interface, + member, + path, + arg0); #endif /* TODO: if this is slow, then we can change signal_data_array into @@ -2953,16 +3498,31 @@ distribute_signals (GDBusConnection *connection, sender = g_dbus_message_get_sender (message); + if (G_UNLIKELY (_g_dbus_debug_signal ())) + { + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Signal:\n" + " <<<< RECEIVED SIGNAL %s.%s\n" + " on object %s\n" + " sent by name %s\n", + g_dbus_message_get_interface (message), + g_dbus_message_get_member (message), + g_dbus_message_get_path (message), + sender != NULL ? sender : "(none)"); + _g_dbus_debug_print_unlock (); + } + /* collect subscribers that match on sender */ if (sender != NULL) { - signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, sender); + signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, sender); if (signal_data_array != NULL) schedule_callbacks (connection, signal_data_array, message, sender); } /* collect subscribers not matching on sender */ - signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, ""); + signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, ""); if (signal_data_array != NULL) schedule_callbacks (connection, signal_data_array, message, sender); } @@ -2980,7 +3540,7 @@ purge_all_signal_subscriptions (GDBusConnection *connection) guint n; ids = g_array_new (FALSE, FALSE, sizeof (guint)); - g_hash_table_iter_init (&iter, connection->priv->map_id_to_signal_data); + g_hash_table_iter_init (&iter, connection->map_id_to_signal_data); while (g_hash_table_iter_next (&iter, &key, NULL)) { guint subscription_id = GPOINTER_TO_UINT (key); @@ -3002,8 +3562,9 @@ purge_all_signal_subscriptions (GDBusConnection *connection) { SignalSubscriber *subscriber; subscriber = &(g_array_index (subscribers, SignalSubscriber, n)); - if (subscriber->user_data_free_func != NULL) - subscriber->user_data_free_func (subscriber->user_data); + call_destroy_notify (subscriber->context, + subscriber->user_data_free_func, + subscriber->user_data); if (subscriber->context != NULL) g_main_context_unref (subscriber->context); } @@ -3013,6 +3574,40 @@ purge_all_signal_subscriptions (GDBusConnection *connection) /* ---------------------------------------------------------------------------------------------------- */ +static GDBusInterfaceVTable * +_g_dbus_interface_vtable_copy (const GDBusInterfaceVTable *vtable) +{ + /* Don't waste memory by copying padding - remember to update this + * when changing struct _GDBusInterfaceVTable in gdbusconnection.h + */ + return g_memdup ((gconstpointer) vtable, 3 * sizeof (gpointer)); +} + +static void +_g_dbus_interface_vtable_free (GDBusInterfaceVTable *vtable) +{ + g_free (vtable); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusSubtreeVTable * +_g_dbus_subtree_vtable_copy (const GDBusSubtreeVTable *vtable) +{ + /* Don't waste memory by copying padding - remember to update this + * when changing struct _GDBusSubtreeVTable in gdbusconnection.h + */ + return g_memdup ((gconstpointer) vtable, 3 * sizeof (gpointer)); +} + +static void +_g_dbus_subtree_vtable_free (GDBusSubtreeVTable *vtable) +{ + g_free (vtable); +} + +/* ---------------------------------------------------------------------------------------------------- */ + struct ExportedObject { gchar *object_path; @@ -3037,8 +3632,8 @@ typedef struct guint id; gchar *interface_name; - const GDBusInterfaceVTable *vtable; - const GDBusInterfaceInfo *introspection_data; + GDBusInterfaceVTable *vtable; + GDBusInterfaceInfo *interface_info; GMainContext *context; gpointer user_data; @@ -3049,15 +3644,53 @@ typedef struct static void exported_interface_free (ExportedInterface *ei) { - if (ei->user_data_free_func != NULL) - /* TODO: push to thread-default mainloop */ - ei->user_data_free_func (ei->user_data); + g_dbus_interface_info_unref ((GDBusInterfaceInfo *) ei->interface_info); + + call_destroy_notify (ei->context, + ei->user_data_free_func, + ei->user_data); if (ei->context != NULL) g_main_context_unref (ei->context); - g_free (ei->interface_name); - g_free (ei); + g_free (ei->interface_name); + _g_dbus_interface_vtable_free (ei->vtable); + g_free (ei); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* Convenience function to check if @registration_id (if not zero) or + * @subtree_registration_id (if not zero) has been unregistered. If + * so, returns %TRUE. + * + * Caller must *not* hold lock. + */ +static gboolean +has_object_been_unregistered (GDBusConnection *connection, + guint registration_id, + guint subtree_registration_id) +{ + gboolean ret; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + + ret = FALSE; + + CONNECTION_LOCK (connection); + if (registration_id != 0 && g_hash_table_lookup (connection->map_id_to_ei, + GUINT_TO_POINTER (registration_id)) == NULL) + { + ret = TRUE; + } + else if (subtree_registration_id != 0 && g_hash_table_lookup (connection->map_id_to_es, + GUINT_TO_POINTER (subtree_registration_id)) == NULL) + { + ret = TRUE; + } + CONNECTION_UNLOCK (connection); + + return ret; } /* ---------------------------------------------------------------------------------------------------- */ @@ -3067,10 +3700,12 @@ typedef struct GDBusConnection *connection; GDBusMessage *message; gpointer user_data; - const char *property_name; + const gchar *property_name; const GDBusInterfaceVTable *vtable; - const GDBusInterfaceInfo *interface_info; + GDBusInterfaceInfo *interface_info; const GDBusPropertyInfo *property_info; + guint registration_id; + guint subtree_registration_id; } PropertyData; static void @@ -3090,6 +3725,19 @@ invoke_get_property_in_idle_cb (gpointer _data) GError *error; GDBusMessage *reply; + if (has_object_been_unregistered (data->connection, + data->registration_id, + data->subtree_registration_id)) + { + reply = g_dbus_message_new_method_error (data->message, + "org.freedesktop.DBus.Error.UnknownMethod", + _("No such interface `org.freedesktop.DBus.Properties' on object at path %s"), + g_dbus_message_get_path (data->message)); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); + g_object_unref (reply); + goto out; + } + error = NULL; value = data->vtable->get_property (data->connection, g_dbus_message_get_sender (data->message), @@ -3104,29 +3752,29 @@ invoke_get_property_in_idle_cb (gpointer _data) { g_assert_no_error (error); - g_variant_ref_sink (value); + if (g_variant_is_floating (value)) + g_variant_ref_sink (value); reply = g_dbus_message_new_method_reply (data->message); g_dbus_message_set_body (reply, g_variant_new ("(v)", value)); - g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_variant_unref (value); g_object_unref (reply); } else { gchar *dbus_error_name; - g_assert (error != NULL); - dbus_error_name = g_dbus_error_encode_gerror (error); reply = g_dbus_message_new_method_error_literal (data->message, dbus_error_name, error->message); - g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_free (dbus_error_name); g_error_free (error); g_object_unref (reply); } + out: return FALSE; } @@ -3187,8 +3835,9 @@ invoke_set_property_in_idle_cb (gpointer _data) out: g_assert (reply != NULL); - g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); + g_variant_unref (value); return FALSE; } @@ -3197,8 +3846,10 @@ invoke_set_property_in_idle_cb (gpointer _data) static gboolean validate_and_maybe_schedule_property_getset (GDBusConnection *connection, GDBusMessage *message, + guint registration_id, + guint subtree_registration_id, gboolean is_get, - const GDBusInterfaceInfo *introspection_data, + GDBusInterfaceInfo *interface_info, const GDBusInterfaceVTable *vtable, GMainContext *main_context, gpointer user_data) @@ -3242,14 +3893,14 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect property_info = NULL; /* TODO: the cost of this is O(n) - it might be worth caching the result */ - property_info = g_dbus_interface_info_lookup_property (introspection_data, property_name); + property_info = g_dbus_interface_info_lookup_property (interface_info, property_name); if (property_info == NULL) { reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.InvalidArgs", _("No such property `%s'"), property_name); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -3261,7 +3912,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect "org.freedesktop.DBus.Error.InvalidArgs", _("Property `%s' is not readable"), property_name); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -3272,7 +3923,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect "org.freedesktop.DBus.Error.InvalidArgs", _("Property `%s' is not writable"), property_name); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -3285,8 +3936,10 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect property_data->user_data = user_data; property_data->property_name = property_name; property_data->vtable = vtable; - property_data->interface_info = introspection_data; + property_data->interface_info = interface_info; property_data->property_info = property_info; + property_data->registration_id = registration_id; + property_data->subtree_registration_id = subtree_registration_id; idle_source = g_idle_source_new (); g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); @@ -3340,7 +3993,7 @@ handle_getset_property (GDBusConnection *connection, "org.freedesktop.DBus.Error.InvalidArgs", _("No such interface `%s'"), interface_name); - g_dbus_connection_send_message_unlocked (eo->connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -3348,8 +4001,10 @@ handle_getset_property (GDBusConnection *connection, handled = validate_and_maybe_schedule_property_getset (eo->connection, message, + ei->id, + 0, is_get, - ei->introspection_data, + ei->interface_info, ei->vtable, ei->context, ei->user_data); @@ -3365,7 +4020,9 @@ typedef struct GDBusMessage *message; gpointer user_data; const GDBusInterfaceVTable *vtable; - const GDBusInterfaceInfo *interface_info; + GDBusInterfaceInfo *interface_info; + guint registration_id; + guint subtree_registration_id; } PropertyGetAllData; static void @@ -3381,13 +4038,24 @@ static gboolean invoke_get_all_properties_in_idle_cb (gpointer _data) { PropertyGetAllData *data = _data; - GVariantBuilder *builder; - GVariant *packed; - GVariant *result; + GVariantBuilder builder; GError *error; GDBusMessage *reply; guint n; + if (has_object_been_unregistered (data->connection, + data->registration_id, + data->subtree_registration_id)) + { + reply = g_dbus_message_new_method_error (data->message, + "org.freedesktop.DBus.Error.UnknownMethod", + _("No such interface `org.freedesktop.DBus.Properties' on object at path %s"), + g_dbus_message_get_path (data->message)); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); + g_object_unref (reply); + goto out; + } + error = NULL; /* TODO: Right now we never fail this call - we just omit values if @@ -3396,7 +4064,8 @@ invoke_get_all_properties_in_idle_cb (gpointer _data) * We could fail the whole call if just a single get_property() call * returns an error. We need clarification in the D-Bus spec about this. */ - builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a{sv})")); + g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}")); for (n = 0; data->interface_info->properties != NULL && data->interface_info->properties[n] != NULL; n++) { const GDBusPropertyInfo *property_info = data->interface_info->properties[n]; @@ -3416,24 +4085,22 @@ invoke_get_all_properties_in_idle_cb (gpointer _data) if (value == NULL) continue; - g_variant_ref_sink (value); - g_variant_builder_add (builder, + if (g_variant_is_floating (value)) + g_variant_ref_sink (value); + g_variant_builder_add (&builder, "{sv}", property_info->name, value); g_variant_unref (value); } - result = g_variant_builder_end (builder); - - builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE); - g_variant_builder_add_value (builder, result); /* steals result since result is floating */ - packed = g_variant_builder_end (builder); + g_variant_builder_close (&builder); reply = g_dbus_message_new_method_reply (data->message); - g_dbus_message_set_body (reply, packed); - g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_dbus_message_set_body (reply, g_variant_builder_end (&builder)); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); + out: return FALSE; } @@ -3441,7 +4108,9 @@ invoke_get_all_properties_in_idle_cb (gpointer _data) static gboolean validate_and_maybe_schedule_property_get_all (GDBusConnection *connection, GDBusMessage *message, - const GDBusInterfaceInfo *introspection_data, + guint registration_id, + guint subtree_registration_id, + GDBusInterfaceInfo *interface_info, const GDBusInterfaceVTable *vtable, GMainContext *main_context, gpointer user_data) @@ -3466,7 +4135,9 @@ validate_and_maybe_schedule_property_get_all (GDBusConnection *connec property_get_all_data->message = g_object_ref (message); property_get_all_data->user_data = user_data; property_get_all_data->vtable = vtable; - property_get_all_data->interface_info = introspection_data; + property_get_all_data->interface_info = interface_info; + property_get_all_data->registration_id = registration_id; + property_get_all_data->subtree_registration_id = subtree_registration_id; idle_source = g_idle_source_new (); g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); @@ -3510,7 +4181,7 @@ handle_get_all_properties (GDBusConnection *connection, "org.freedesktop.DBus.Error.InvalidArgs", _("No such interface"), interface_name); - g_dbus_connection_send_message_unlocked (eo->connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -3518,7 +4189,9 @@ handle_get_all_properties (GDBusConnection *connection, handled = validate_and_maybe_schedule_property_get_all (eo->connection, message, - ei->introspection_data, + ei->id, + 0, + ei->interface_info, ei->vtable, ei->context, ei->user_data); @@ -3586,7 +4259,7 @@ introspect_append_standard_interfaces (GString *s) static void maybe_add_path (const gchar *path, gsize path_len, const gchar *object_path, GHashTable *set) { - if (g_str_has_prefix (object_path, path) && strlen (object_path) >= path_len) + if (g_str_has_prefix (object_path, path) && strlen (object_path) > path_len && object_path[path_len-1] == '/') { const gchar *begin; const gchar *end; @@ -3594,7 +4267,6 @@ maybe_add_path (const gchar *path, gsize path_len, const gchar *object_path, GHa begin = object_path + path_len; end = strchr (begin, '/'); - if (end != NULL) s = g_strndup (begin, end - begin); else @@ -3629,11 +4301,11 @@ g_dbus_connection_list_registered_unlocked (GDBusConnection *connection, set = g_hash_table_new (g_str_hash, g_str_equal); - g_hash_table_iter_init (&hash_iter, connection->priv->map_object_path_to_eo); + g_hash_table_iter_init (&hash_iter, connection->map_object_path_to_eo); while (g_hash_table_iter_next (&hash_iter, (gpointer) &object_path, NULL)) maybe_add_path (path, path_len, object_path, set); - g_hash_table_iter_init (&hash_iter, connection->priv->map_object_path_to_es); + g_hash_table_iter_init (&hash_iter, connection->map_object_path_to_es); while (g_hash_table_iter_next (&hash_iter, (gpointer) &object_path, NULL)) maybe_add_path (path, path_len, object_path, set); @@ -3683,7 +4355,7 @@ handle_introspect (GDBusConnection *connection, /* then include the registered interfaces */ g_hash_table_iter_init (&hash_iter, eo->map_if_name_to_ei); while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &ei)) - g_dbus_interface_info_generate_xml (ei->introspection_data, 2, s); + g_dbus_interface_info_generate_xml (ei->interface_info, 2, s); /* finally include nodes registered below us */ registered = g_dbus_connection_list_registered_unlocked (connection, eo->object_path); @@ -3694,7 +4366,7 @@ handle_introspect (GDBusConnection *connection, reply = g_dbus_message_new_method_reply (message); g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); g_string_free (s, TRUE); @@ -3707,10 +4379,30 @@ call_in_idle_cb (gpointer user_data) { GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data); GDBusInterfaceVTable *vtable; + guint registration_id; + guint subtree_registration_id; vtable = g_object_get_data (G_OBJECT (invocation), "g-dbus-interface-vtable"); g_assert (vtable != NULL && vtable->method_call != NULL); + registration_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (invocation), "g-dbus-registration-id")); + subtree_registration_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (invocation), "g-dbus-subtree-registration-id")); + + if (has_object_been_unregistered (g_dbus_method_invocation_get_connection (invocation), + registration_id, + subtree_registration_id)) + { + GDBusMessage *reply; + reply = g_dbus_message_new_method_error (g_dbus_method_invocation_get_message (invocation), + "org.freedesktop.DBus.Error.UnknownMethod", + _("No such interface `%s' on object at path %s"), + g_dbus_method_invocation_get_interface_name (invocation), + g_dbus_method_invocation_get_object_path (invocation)); + g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); + g_object_unref (reply); + goto out; + } + vtable->method_call (g_dbus_method_invocation_get_connection (invocation), g_dbus_method_invocation_get_sender (invocation), g_dbus_method_invocation_get_object_path (invocation), @@ -3720,6 +4412,7 @@ call_in_idle_cb (gpointer user_data) g_object_ref (invocation), g_dbus_method_invocation_get_user_data (invocation)); + out: return FALSE; } @@ -3727,7 +4420,9 @@ call_in_idle_cb (gpointer user_data) static gboolean validate_and_maybe_schedule_method_call (GDBusConnection *connection, GDBusMessage *message, - const GDBusInterfaceInfo *introspection_data, + guint registration_id, + guint subtree_registration_id, + GDBusInterfaceInfo *interface_info, const GDBusInterfaceVTable *vtable, GMainContext *main_context, gpointer user_data) @@ -3738,12 +4433,12 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, GVariant *parameters; GSource *idle_source; gboolean handled; - gchar *in_signature; + GVariantType *in_type; handled = FALSE; /* TODO: the cost of this is O(n) - it might be worth caching the result */ - method_info = g_dbus_interface_info_lookup_method (introspection_data, g_dbus_message_get_member (message)); + method_info = g_dbus_interface_info_lookup_method (interface_info, g_dbus_message_get_member (message)); /* if the method doesn't exist, return the org.freedesktop.DBus.Error.UnknownMethod * error to the caller @@ -3754,32 +4449,11 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, "org.freedesktop.DBus.Error.UnknownMethod", _("No such method `%s'"), g_dbus_message_get_member (message)); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); - g_object_unref (reply); - handled = TRUE; - goto out; - } - - /* Check that the incoming args are of the right type - if they are not, return - * the org.freedesktop.DBus.Error.InvalidArgs error to the caller - * - * TODO: might also be worth caching the combined signature. - */ - in_signature = _g_dbus_compute_complete_signature (method_info->in_args, FALSE); - if (g_strcmp0 (g_dbus_message_get_signature (message), in_signature) != 0) - { - reply = g_dbus_message_new_method_error (message, - "org.freedesktop.DBus.Error.InvalidArgs", - _("Signature of message, `%s', does not match expected signature `%s'"), - g_dbus_message_get_signature (message), - in_signature); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); - g_free (in_signature); handled = TRUE; goto out; } - g_free (in_signature); parameters = g_dbus_message_get_body (message); if (parameters == NULL) @@ -3792,20 +4466,48 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, g_variant_ref (parameters); } + /* Check that the incoming args are of the right type - if they are not, return + * the org.freedesktop.DBus.Error.InvalidArgs error to the caller + */ + in_type = _g_dbus_compute_complete_signature (method_info->in_args); + if (!g_variant_is_of_type (parameters, in_type)) + { + gchar *type_string; + + type_string = g_variant_type_dup_string (in_type); + + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.InvalidArgs", + _("Type of message, `%s', does not match expected type `%s'"), + g_variant_get_type_string (parameters), + type_string); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); + g_variant_type_free (in_type); + g_variant_unref (parameters); + g_object_unref (reply); + g_free (type_string); + handled = TRUE; + goto out; + } + g_variant_type_free (in_type); + /* schedule the call in idle */ - invocation = g_dbus_method_invocation_new (g_dbus_message_get_sender (message), - g_dbus_message_get_path (message), - g_dbus_message_get_interface (message), - g_dbus_message_get_member (message), - method_info, - connection, - message, - parameters, - user_data); + invocation = _g_dbus_method_invocation_new (g_dbus_message_get_sender (message), + g_dbus_message_get_path (message), + g_dbus_message_get_interface (message), + g_dbus_message_get_member (message), + method_info, + connection, + message, + parameters, + user_data); g_variant_unref (parameters); - g_object_set_data (G_OBJECT (invocation), - "g-dbus-interface-vtable", - (gpointer) vtable); + + /* TODO: would be nicer with a real MethodData like we already + * have PropertyData and PropertyGetAllData... */ + g_object_set_data (G_OBJECT (invocation), "g-dbus-interface-vtable", (gpointer) vtable); + g_object_set_data (G_OBJECT (invocation), "g-dbus-registration-id", GUINT_TO_POINTER (registration_id)); + g_object_set_data (G_OBJECT (invocation), "g-dbus-subtree-registration-id", GUINT_TO_POINTER (subtree_registration_id)); idle_source = g_idle_source_new (); g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); @@ -3856,7 +4558,9 @@ obj_message_func (GDBusConnection *connection, handled = validate_and_maybe_schedule_method_call (connection, message, - ei->introspection_data, + ei->id, + 0, + ei->interface_info, ei->vtable, ei->context, ei->user_data); @@ -3901,26 +4605,26 @@ obj_message_func (GDBusConnection *connection, * g_dbus_connection_register_object: * @connection: A #GDBusConnection. * @object_path: The object path to register at. - * @introspection_data: Introspection data for the interface. + * @interface_info: Introspection data for the interface. * @vtable: A #GDBusInterfaceVTable to call into or %NULL. * @user_data: Data to pass to functions in @vtable. * @user_data_free_func: Function to call when the object path is unregistered. * @error: Return location for error or %NULL. * * Registers callbacks for exported objects at @object_path with the - * D-Bus interface that is described in @introspection_data. + * D-Bus interface that is described in @interface_info. * * Calls to functions in @vtable (and @user_data_free_func) will * happen in the thread-default main * loop of the thread you are calling this method from. * * Note that all #GVariant values passed to functions in @vtable will match - * the signature given in @introspection_data - if a remote caller passes + * the signature given in @interface_info - if a remote caller passes * incorrect values, the org.freedesktop.DBus.Error.InvalidArgs * is returned to the remote caller. * * Additionally, if the remote caller attempts to invoke methods or - * access properties not mentioned in @introspection_data the + * access properties not mentioned in @interface_info the * org.freedesktop.DBus.Error.UnknownMethod resp. * org.freedesktop.DBus.Error.InvalidArgs errors * are returned to the caller. @@ -3939,6 +4643,11 @@ obj_message_func (GDBusConnection *connection, * org.freedesktop.DBus.Properties yourself, e.g. to handle getting * and setting of properties asynchronously. * + * Note that the reference count on @interface_info will be + * incremented by 1 (unless allocated statically, e.g. if the + * reference count is -1, see g_dbus_interface_info_ref()) for as long + * as the object is exported. Also note that @vtable will be copied. + * * See for an example of how to use this method. * * Returns: 0 if @error is set, otherwise a registration id (never 0) @@ -3949,7 +4658,7 @@ obj_message_func (GDBusConnection *connection, guint g_dbus_connection_register_object (GDBusConnection *connection, const gchar *object_path, - const GDBusInterfaceInfo *introspection_data, + GDBusInterfaceInfo *interface_info, const GDBusInterfaceVTable *vtable, gpointer user_data, GDestroyNotify user_data_free_func, @@ -3960,17 +4669,16 @@ g_dbus_connection_register_object (GDBusConnection *connection, guint ret; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); - g_return_val_if_fail (!g_dbus_connection_is_closed (connection), 0); g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), 0); - g_return_val_if_fail (introspection_data != NULL, 0); - g_return_val_if_fail (g_dbus_is_interface_name (introspection_data->name), 0); + g_return_val_if_fail (interface_info != NULL, 0); + g_return_val_if_fail (g_dbus_is_interface_name (interface_info->name), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); ret = 0; CONNECTION_LOCK (connection); - eo = g_hash_table_lookup (connection->priv->map_object_path_to_eo, object_path); + eo = g_hash_table_lookup (connection->map_object_path_to_eo, object_path); if (eo == NULL) { eo = g_new0 (ExportedObject, 1); @@ -3980,17 +4688,17 @@ g_dbus_connection_register_object (GDBusConnection *connection, g_str_equal, NULL, (GDestroyNotify) exported_interface_free); - g_hash_table_insert (connection->priv->map_object_path_to_eo, eo->object_path, eo); + g_hash_table_insert (connection->map_object_path_to_eo, eo->object_path, eo); } - ei = g_hash_table_lookup (eo->map_if_name_to_ei, introspection_data->name); + ei = g_hash_table_lookup (eo->map_if_name_to_ei, interface_info->name); if (ei != NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, _("An object is already exported for the interface %s at %s"), - introspection_data->name, + interface_info->name, object_path); goto out; } @@ -4000,9 +4708,9 @@ g_dbus_connection_register_object (GDBusConnection *connection, ei->eo = eo; ei->user_data = user_data; ei->user_data_free_func = user_data_free_func; - ei->vtable = vtable; - ei->introspection_data = introspection_data; - ei->interface_name = g_strdup (introspection_data->name); + ei->vtable = _g_dbus_interface_vtable_copy (vtable); + ei->interface_info = g_dbus_interface_info_ref (interface_info); + ei->interface_name = g_strdup (interface_info->name); ei->context = g_main_context_get_thread_default (); if (ei->context != NULL) g_main_context_ref (ei->context); @@ -4010,7 +4718,7 @@ g_dbus_connection_register_object (GDBusConnection *connection, g_hash_table_insert (eo->map_if_name_to_ei, (gpointer) ei->interface_name, ei); - g_hash_table_insert (connection->priv->map_id_to_ei, + g_hash_table_insert (connection->map_id_to_ei, GUINT_TO_POINTER (ei->id), ei); @@ -4047,18 +4755,18 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, CONNECTION_LOCK (connection); - ei = g_hash_table_lookup (connection->priv->map_id_to_ei, + ei = g_hash_table_lookup (connection->map_id_to_ei, GUINT_TO_POINTER (registration_id)); if (ei == NULL) goto out; eo = ei->eo; - g_warn_if_fail (g_hash_table_remove (connection->priv->map_id_to_ei, GUINT_TO_POINTER (ei->id))); + g_warn_if_fail (g_hash_table_remove (connection->map_id_to_ei, GUINT_TO_POINTER (ei->id))); g_warn_if_fail (g_hash_table_remove (eo->map_if_name_to_ei, ei->interface_name)); /* unregister object path if we have no more exported interfaces */ if (g_hash_table_size (eo->map_if_name_to_ei) == 0) - g_warn_if_fail (g_hash_table_remove (connection->priv->map_object_path_to_eo, + g_warn_if_fail (g_hash_table_remove (connection->map_object_path_to_eo, eo->object_path)); ret = TRUE; @@ -4083,6 +4791,8 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, * * Emits a signal. * + * If the parameters GVariant is floating, it is consumed. + * * This can only fail if @parameters is not compatible with the D-Bus protocol. * * Returns: %TRUE unless @error is set. @@ -4111,6 +4821,20 @@ g_dbus_connection_emit_signal (GDBusConnection *connection, g_return_val_if_fail (signal_name != NULL && g_dbus_is_member_name (signal_name), FALSE); g_return_val_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), FALSE); + if (G_UNLIKELY (_g_dbus_debug_emission ())) + { + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Emission:\n" + " >>>> SIGNAL EMISSION %s.%s()\n" + " on object %s\n" + " destination %s\n", + interface_name, signal_name, + object_path, + destination_bus_name != NULL ? destination_bus_name : "(none)"); + _g_dbus_debug_print_unlock (); + } + message = g_dbus_message_new_signal (object_path, interface_name, signal_name); @@ -4123,7 +4847,7 @@ g_dbus_connection_emit_signal (GDBusConnection *connection, if (parameters != NULL) g_dbus_message_set_body (message, parameters); - ret = g_dbus_connection_send_message (connection, message, NULL, error); + ret = g_dbus_connection_send_message (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, error); g_object_unref (message); return ret; @@ -4137,6 +4861,130 @@ add_call_flags (GDBusMessage *message, g_dbus_message_set_flags (message, G_DBUS_MESSAGE_FLAGS_NO_AUTO_START); } +static GVariant * +decode_method_reply (GDBusMessage *reply, + const gchar *method_name, + const GVariantType *reply_type, + GError **error) +{ + GVariant *result; + + result = NULL; + switch (g_dbus_message_get_message_type (reply)) + { + case G_DBUS_MESSAGE_TYPE_METHOD_RETURN: + result = g_dbus_message_get_body (reply); + if (result == NULL) + { + result = g_variant_new ("()"); + g_variant_ref_sink (result); + } + else + { + g_variant_ref (result); + } + + if (!g_variant_is_of_type (result, reply_type)) + { + gchar *type_string = g_variant_type_dup_string (reply_type); + + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Method `%s' returned type `%s', but expected `%s'"), + method_name, g_variant_get_type_string (result), type_string); + + g_variant_unref (result); + g_free (type_string); + result = NULL; + } + break; + + case G_DBUS_MESSAGE_TYPE_ERROR: + g_dbus_message_to_gerror (reply, error); + break; + + default: + g_assert_not_reached (); + break; + } + + return result; +} + + +typedef struct +{ + GSimpleAsyncResult *simple; + GVariantType *reply_type; + gchar *method_name; /* for error message */ + guint32 serial; +} CallState; + +static void +g_dbus_connection_call_done (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (source); + CallState *state = user_data; + GError *error; + GDBusMessage *reply; + GVariant *value; + + error = NULL; + reply = g_dbus_connection_send_message_with_reply_finish (connection, + result, + &error); + + if (G_UNLIKELY (_g_dbus_debug_call ())) + { + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Call:\n" + " <<<< ASYNC COMPLETE %s() (serial %d)\n" + " ", + state->method_name, + state->serial); + if (reply != NULL) + { + g_print ("SUCCESS\n"); + } + else + { + g_print ("FAILED: %s\n", + error->message); + } + _g_dbus_debug_print_unlock (); + } + + + if (reply != NULL) + { + value = decode_method_reply (reply, state->method_name, + state->reply_type, &error); + g_object_unref (reply); + } + else + value = NULL; + + if (value == NULL) + { + g_simple_async_result_set_from_error (state->simple, error); + g_error_free (error); + } + else + g_simple_async_result_set_op_res_gpointer (state->simple, value, + (GDestroyNotify) g_variant_unref); + + g_simple_async_result_complete (state->simple); + g_variant_type_free (state->reply_type); + g_object_unref (state->simple); + g_free (state->method_name); + + g_slice_free (CallState, state); +} + /** * g_dbus_connection_call: * @connection: A #GDBusConnection. @@ -4145,6 +4993,7 @@ add_call_flags (GDBusMessage *message, * @interface_name: D-Bus interface to invoke method on. * @method_name: The name of the method to invoke. * @parameters: A #GVariant tuple with parameters for the method or %NULL if not passing parameters. + * @reply_type: The expected type of the reply, or %NULL. * @flags: Flags from the #GDBusCallFlags enumeration. * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. * @cancellable: A #GCancellable or %NULL. @@ -4162,6 +5011,10 @@ add_call_flags (GDBusMessage *message, * not compatible with the D-Bus protocol, the operation fails with * %G_IO_ERROR_INVALID_ARGUMENT. * + * If @reply_type is non-%NULL then the reply will be checked for having this type and an + * error will be raised if it does not match. Said another way, if you give a @reply_type + * then any non-%NULL return value will be of this type. + * * If the @parameters #GVariant is floating, it is consumed. This allows * convenient 'inline' use of g_variant_new(), e.g.: * |[ @@ -4173,6 +5026,7 @@ add_call_flags (GDBusMessage *message, * g_variant_new ("(ss)", * "Thing One", * "Thing Two"), + * NULL, * G_DBUS_CALL_FLAGS_NONE, * -1, * NULL, @@ -4196,6 +5050,7 @@ g_dbus_connection_call (GDBusConnection *connection, const gchar *interface_name, const gchar *method_name, GVariant *parameters, + const GVariantType *reply_type, GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, @@ -4203,6 +5058,7 @@ g_dbus_connection_call (GDBusConnection *connection, gpointer user_data) { GDBusMessage *message; + CallState *state; g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); g_return_if_fail (bus_name == NULL || g_dbus_is_name (bus_name)); @@ -4212,6 +5068,17 @@ g_dbus_connection_call (GDBusConnection *connection, g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1); g_return_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE)); + state = g_slice_new (CallState); + state->simple = g_simple_async_result_new (G_OBJECT (connection), + callback, user_data, + g_dbus_connection_call); + state->method_name = g_strjoin (".", interface_name, method_name, NULL); + + if (reply_type == NULL) + reply_type = G_VARIANT_TYPE_ANY; + + state->reply_type = g_variant_type_copy (reply_type); + message = g_dbus_message_new_method_call (bus_name, object_path, interface_name, @@ -4222,48 +5089,31 @@ g_dbus_connection_call (GDBusConnection *connection, g_dbus_connection_send_message_with_reply (connection, message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, timeout_msec, - NULL, /* volatile guint32 *out_serial */ + &state->serial, cancellable, - callback, - user_data); - - if (message != NULL) - g_object_unref (message); -} - -static GVariant * -decode_method_reply (GDBusMessage *reply, - GError **error) -{ - GVariant *result; + g_dbus_connection_call_done, + state); - result = NULL; - switch (g_dbus_message_get_message_type (reply)) + if (G_UNLIKELY (_g_dbus_debug_call ())) { - case G_DBUS_MESSAGE_TYPE_METHOD_RETURN: - result = g_dbus_message_get_body (reply); - if (result == NULL) - { - result = g_variant_new ("()"); - g_variant_ref_sink (result); - } - else - { - g_variant_ref (result); - } - break; - - case G_DBUS_MESSAGE_TYPE_ERROR: - g_dbus_message_to_gerror (reply, error); - break; - - default: - g_assert_not_reached (); - break; + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Call:\n" + " >>>> ASYNC %s.%s()\n" + " on object %s\n" + " owned by name %s (serial %d)\n", + interface_name, + method_name, + object_path, + bus_name != NULL ? bus_name : "(none)", + state->serial); + _g_dbus_debug_print_unlock (); } - return result; + if (message != NULL) + g_object_unref (message); } /** @@ -4284,25 +5134,19 @@ g_dbus_connection_call_finish (GDBusConnection *connection, GAsyncResult *res, GError **error) { - GDBusMessage *reply; - GVariant *result; + GSimpleAsyncResult *simple; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); - g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL); + g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (connection), + g_dbus_connection_call), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - result = NULL; - - reply = g_dbus_connection_send_message_with_reply_finish (connection, res, error); - if (reply == NULL) - goto out; - - result = decode_method_reply (reply, error); + simple = G_SIMPLE_ASYNC_RESULT (res); - g_object_unref (reply); + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; - out: - return result; + return g_variant_ref (g_simple_async_result_get_op_res_gpointer (simple)); } /* ---------------------------------------------------------------------------------------------------- */ @@ -4315,6 +5159,7 @@ g_dbus_connection_call_finish (GDBusConnection *connection, * @interface_name: D-Bus interface to invoke method on. * @method_name: The name of the method to invoke. * @parameters: A #GVariant tuple with parameters for the method or %NULL if not passing parameters. + * @reply_type: The expected type of the reply, or %NULL. * @flags: Flags from the #GDBusCallFlags enumeration. * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. * @cancellable: A #GCancellable or %NULL. @@ -4329,6 +5174,11 @@ g_dbus_connection_call_finish (GDBusConnection *connection, * operation will fail with %G_IO_ERROR_CANCELLED. If @parameters * contains a value not compatible with the D-Bus protocol, the operation * fails with %G_IO_ERROR_INVALID_ARGUMENT. + + * If @reply_type is non-%NULL then the reply will be checked for having + * this type and an error will be raised if it does not match. Said + * another way, if you give a @reply_type then any non-%NULL return + * value will be of this type. * * If the @parameters #GVariant is floating, it is consumed. * This allows convenient 'inline' use of g_variant_new(), e.g.: @@ -4341,6 +5191,7 @@ g_dbus_connection_call_finish (GDBusConnection *connection, * g_variant_new ("(ss)", * "Thing One", * "Thing Two"), + * NULL, * G_DBUS_CALL_FLAGS_NONE, * -1, * NULL, @@ -4363,6 +5214,7 @@ g_dbus_connection_call_sync (GDBusConnection *connection, const gchar *interface_name, const gchar *method_name, GVariant *parameters, + const GVariantType *reply_type, GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, @@ -4371,6 +5223,7 @@ g_dbus_connection_call_sync (GDBusConnection *connection, GDBusMessage *message; GDBusMessage *reply; GVariant *result; + GError *local_error; message = NULL; reply = NULL; @@ -4384,6 +5237,9 @@ g_dbus_connection_call_sync (GDBusConnection *connection, g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL); g_return_val_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL); + if (reply_type == NULL) + reply_type = G_VARIANT_TYPE_ANY; + message = g_dbus_message_new_method_call (bus_name, object_path, interface_name, @@ -4392,17 +5248,61 @@ g_dbus_connection_call_sync (GDBusConnection *connection, if (parameters != NULL) g_dbus_message_set_body (message, parameters); + if (G_UNLIKELY (_g_dbus_debug_call ())) + { + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Call:\n" + " >>>> SYNC %s.%s()\n" + " on object %s\n" + " owned by name %s\n", + interface_name, + method_name, + object_path, + bus_name != NULL ? bus_name : "(none)"); + _g_dbus_debug_print_unlock (); + } + + local_error = NULL; reply = g_dbus_connection_send_message_with_reply_sync (connection, message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, timeout_msec, NULL, /* volatile guint32 *out_serial */ cancellable, - error); + &local_error); + + if (G_UNLIKELY (_g_dbus_debug_call ())) + { + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Call:\n" + " <<<< SYNC COMPLETE %s.%s()\n" + " ", + interface_name, + method_name); + if (reply != NULL) + { + g_print ("SUCCESS\n"); + } + else + { + g_print ("FAILED: %s\n", + local_error->message); + } + _g_dbus_debug_print_unlock (); + } if (reply == NULL) - goto out; + { + if (error != NULL) + *error = local_error; + else + g_error_free (local_error); + goto out; + } - result = decode_method_reply (reply, error); + result = decode_method_reply (reply, method_name, reply_type, error); out: if (message != NULL) @@ -4420,7 +5320,7 @@ struct ExportedSubtree guint id; gchar *object_path; GDBusConnection *connection; - const GDBusSubtreeVTable *vtable; + GDBusSubtreeVTable *vtable; GDBusSubtreeFlags flags; GMainContext *context; @@ -4431,13 +5331,14 @@ struct ExportedSubtree static void exported_subtree_free (ExportedSubtree *es) { - if (es->user_data_free_func != NULL) - /* TODO: push to thread-default mainloop */ - es->user_data_free_func (es->user_data); + call_destroy_notify (es->context, + es->user_data_free_func, + es->user_data); if (es->context != NULL) g_main_context_unref (es->context); + _g_dbus_subtree_vtable_free (es->vtable); g_free (es->object_path); g_free (es); } @@ -4456,7 +5357,7 @@ handle_subtree_introspect (GDBusConnection *connection, const gchar *sender; const gchar *requested_object_path; const gchar *requested_node; - GPtrArray *interfaces; + GDBusInterfaceInfo **interfaces; guint n; gchar **subnode_paths; @@ -4488,7 +5389,7 @@ handle_subtree_introspect (GDBusConnection *connection, } else { - requested_node = "/"; + requested_node = NULL; } interfaces = es->vtable->introspect (es->connection, @@ -4498,18 +5399,14 @@ handle_subtree_introspect (GDBusConnection *connection, es->user_data); if (interfaces != NULL) { - if (interfaces->len > 0) - { - /* we're in business */ - introspect_append_standard_interfaces (s); + introspect_append_standard_interfaces (s); - for (n = 0; n < interfaces->len; n++) - { - const GDBusInterfaceInfo *interface_info = interfaces->pdata[n]; - g_dbus_interface_info_generate_xml (interface_info, 2, s); - } + for (n = 0; interfaces[n] != NULL; n++) + { + g_dbus_interface_info_generate_xml (interfaces[n], 2, s); + g_dbus_interface_info_unref (interfaces[n]); } - g_ptr_array_unref (interfaces); + g_free (interfaces); } /* then include entries from the Subtree for the root */ @@ -4529,7 +5426,7 @@ handle_subtree_introspect (GDBusConnection *connection, reply = g_dbus_message_new_method_reply (message); g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); - g_dbus_connection_send_message (connection, reply, NULL, NULL); + g_dbus_connection_send_message (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; @@ -4546,7 +5443,7 @@ handle_subtree_method_invocation (GDBusConnection *connection, ExportedSubtree *es, GDBusMessage *message) { - gboolean handled;; + gboolean handled; const gchar *sender; const gchar *interface_name; const gchar *member; @@ -4554,12 +5451,11 @@ handle_subtree_method_invocation (GDBusConnection *connection, const gchar *requested_object_path; const gchar *requested_node; gboolean is_root; - gchar **children; - const GDBusInterfaceInfo *introspection_data; + GDBusInterfaceInfo *interface_info; const GDBusInterfaceVTable *interface_vtable; gpointer interface_user_data; guint n; - GPtrArray *interfaces; + GDBusInterfaceInfo **interfaces; gboolean is_property_get; gboolean is_property_set; gboolean is_property_get_all; @@ -4587,23 +5483,33 @@ handle_subtree_method_invocation (GDBusConnection *connection, is_property_get_all = TRUE; } - children = es->vtable->enumerate (es->connection, - sender, - es->object_path, - es->user_data); - if (!is_root) { requested_node = strrchr (requested_object_path, '/') + 1; - /* If not dynamic, skip if requested node is not part of children */ - if (!(es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) && - !_g_strv_has_string ((const gchar * const *) children, requested_node)) - goto out; + if (~es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) + { + /* We don't want to dispatch to unenumerated + * nodes, so ensure that the child exists. + */ + gchar **children; + gboolean exists; + + children = es->vtable->enumerate (es->connection, + sender, + es->object_path, + es->user_data); + + exists = _g_strv_has_string ((const gchar * const *) children, requested_node); + g_strfreev (children); + + if (!exists) + goto out; + } } else { - requested_node = "/"; + requested_node = NULL; } /* get introspection data for the node */ @@ -4612,17 +5518,19 @@ handle_subtree_method_invocation (GDBusConnection *connection, requested_object_path, requested_node, es->user_data); - g_assert (interfaces != NULL); - introspection_data = NULL; - for (n = 0; n < interfaces->len; n++) + + if (interfaces == NULL) + goto out; + + interface_info = NULL; + for (n = 0; interfaces[n] != NULL; n++) { - const GDBusInterfaceInfo *id_n = (const GDBusInterfaceInfo *) interfaces->pdata[n]; - if (g_strcmp0 (id_n->name, interface_name) == 0) - introspection_data = id_n; + if (g_strcmp0 (interfaces[n]->name, interface_name) == 0) + interface_info = interfaces[n]; } /* dispatch the call if the user wants to handle it */ - if (introspection_data != NULL) + if (interface_info != NULL) { /* figure out where to dispatch the method call */ interface_user_data = NULL; @@ -4639,7 +5547,9 @@ handle_subtree_method_invocation (GDBusConnection *connection, CONNECTION_LOCK (connection); handled = validate_and_maybe_schedule_method_call (es->connection, message, - introspection_data, + 0, + es->id, + interface_info, interface_vtable, es->context, interface_user_data); @@ -4658,24 +5568,23 @@ handle_subtree_method_invocation (GDBusConnection *connection, g_assert_not_reached (); /* see if the object supports this interface at all */ - for (n = 0; n < interfaces->len; n++) + for (n = 0; interfaces[n] != NULL; n++) { - const GDBusInterfaceInfo *id_n = (const GDBusInterfaceInfo *) interfaces->pdata[n]; - if (g_strcmp0 (id_n->name, interface_name) == 0) - introspection_data = id_n; + if (g_strcmp0 (interfaces[n]->name, interface_name) == 0) + interface_info = interfaces[n]; } /* Fail with org.freedesktop.DBus.Error.InvalidArgs if the user-code * claims it won't support the interface */ - if (introspection_data == NULL) + if (interface_info == NULL) { GDBusMessage *reply; reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.InvalidArgs", _("No such interface `%s'"), interface_name); - g_dbus_connection_send_message (es->connection, reply, NULL, NULL); + g_dbus_connection_send_message (es->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -4691,15 +5600,22 @@ handle_subtree_method_invocation (GDBusConnection *connection, &interface_user_data, es->user_data); if (interface_vtable == NULL) - goto out; + { + g_warning ("The subtree introspection function indicates that '%s' " + "is a valid interface name, but calling the dispatch " + "function on that interface gave us NULL", interface_name); + goto out; + } if (is_property_get || is_property_set) { CONNECTION_LOCK (connection); handled = validate_and_maybe_schedule_property_getset (es->connection, message, + 0, + es->id, is_property_get, - introspection_data, + interface_info, interface_vtable, es->context, interface_user_data); @@ -4710,7 +5626,9 @@ handle_subtree_method_invocation (GDBusConnection *connection, CONNECTION_LOCK (connection); handled = validate_and_maybe_schedule_property_get_all (es->connection, message, - introspection_data, + 0, + es->id, + interface_info, interface_vtable, es->context, interface_user_data); @@ -4720,8 +5638,12 @@ handle_subtree_method_invocation (GDBusConnection *connection, out: if (interfaces != NULL) - g_ptr_array_unref (interfaces); - g_strfreev (children); + { + for (n = 0; interfaces[n] != NULL; n++) + g_dbus_interface_info_unref (interfaces[n]); + g_free (interfaces); + } + return handled; } @@ -4775,7 +5697,7 @@ process_subtree_vtable_message_in_idle_cb (gpointer _data) g_dbus_message_get_member (data->message), g_dbus_message_get_interface (data->message), g_dbus_message_get_signature (data->message)); - g_dbus_connection_send_message (data->es->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->es->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); } @@ -4852,6 +5774,9 @@ subtree_message_func (GDBusConnection *connection, * for object paths not registered via g_dbus_connection_register_object() * or other bindings. * + * Note that @vtable will be copied so you cannot change it after + * registration. + * * See for an example of how to use this method. * * Returns: 0 if @error is set, otherwise a subtree registration id (never 0) @@ -4880,7 +5805,7 @@ g_dbus_connection_register_subtree (GDBusConnection *connection, CONNECTION_LOCK (connection); - es = g_hash_table_lookup (connection->priv->map_object_path_to_es, object_path); + es = g_hash_table_lookup (connection->map_object_path_to_es, object_path); if (es != NULL) { g_set_error (error, @@ -4895,7 +5820,7 @@ g_dbus_connection_register_subtree (GDBusConnection *connection, es->object_path = g_strdup (object_path); es->connection = connection; - es->vtable = vtable; + es->vtable = _g_dbus_subtree_vtable_copy (vtable); es->flags = flags; es->id = _global_subtree_registration_id++; /* TODO: overflow etc. */ es->user_data = user_data; @@ -4904,8 +5829,8 @@ g_dbus_connection_register_subtree (GDBusConnection *connection, if (es->context != NULL) g_main_context_ref (es->context); - g_hash_table_insert (connection->priv->map_object_path_to_es, es->object_path, es); - g_hash_table_insert (connection->priv->map_id_to_es, + g_hash_table_insert (connection->map_object_path_to_es, es->object_path, es); + g_hash_table_insert (connection->map_id_to_es, GUINT_TO_POINTER (es->id), es); @@ -4943,13 +5868,13 @@ g_dbus_connection_unregister_subtree (GDBusConnection *connection, CONNECTION_LOCK (connection); - es = g_hash_table_lookup (connection->priv->map_id_to_es, + es = g_hash_table_lookup (connection->map_id_to_es, GUINT_TO_POINTER (registration_id)); if (es == NULL) goto out; - g_warn_if_fail (g_hash_table_remove (connection->priv->map_id_to_es, GUINT_TO_POINTER (es->id))); - g_warn_if_fail (g_hash_table_remove (connection->priv->map_object_path_to_es, es->object_path)); + g_warn_if_fail (g_hash_table_remove (connection->map_id_to_es, GUINT_TO_POINTER (es->id))); + g_warn_if_fail (g_hash_table_remove (connection->map_object_path_to_es, es->object_path)); ret = TRUE; @@ -4969,7 +5894,7 @@ handle_generic_ping_unlocked (GDBusConnection *connection, { GDBusMessage *reply; reply = g_dbus_message_new_method_reply (message); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); } @@ -4982,35 +5907,27 @@ handle_generic_get_machine_id_unlocked (GDBusConnection *connection, GDBusMessage *reply; reply = NULL; - if (connection->priv->machine_id == NULL) + if (connection->machine_id == NULL) { GError *error; + error = NULL; - /* TODO: use PACKAGE_LOCALSTATEDIR ? */ - if (!g_file_get_contents ("/var/lib/dbus/machine-id", - &connection->priv->machine_id, - NULL, - &error)) + connection->machine_id = _g_dbus_get_machine_id (&error); + if (connection->machine_id == NULL) { - reply = g_dbus_message_new_method_error (message, - "org.freedesktop.DBus.Error.Failed", - _("Unable to load /var/lib/dbus/machine-id: %s"), - error->message); + reply = g_dbus_message_new_method_error_literal (message, + "org.freedesktop.DBus.Error.Failed", + error->message); g_error_free (error); } - else - { - g_strstrip (connection->priv->machine_id); - /* TODO: validate value */ - } } if (reply == NULL) { reply = g_dbus_message_new_method_reply (message); - g_dbus_message_set_body (reply, g_variant_new ("(s)", connection->priv->machine_id)); + g_dbus_message_set_body (reply, g_variant_new ("(s)", connection->machine_id)); } - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); } @@ -5037,7 +5954,7 @@ handle_generic_introspect_unlocked (GDBusConnection *connection, reply = g_dbus_message_new_method_reply (message); g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); g_string_free (s, TRUE); } @@ -5094,6 +6011,7 @@ static void distribute_method_call (GDBusConnection *connection, GDBusMessage *message) { + GDBusMessage *reply; ExportedObject *eo; ExportedSubtree *es; const gchar *object_path; @@ -5122,6 +6040,23 @@ distribute_method_call (GDBusConnection *connection, subtree_path = NULL; } + + if (G_UNLIKELY (_g_dbus_debug_incoming ())) + { + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Incoming:\n" + " <<<< METHOD INVOCATION %s.%s()\n" + " on object %s\n" + " invoked by name %s\n" + " serial %d\n", + interface_name, member, + path, + g_dbus_message_get_sender (message) != NULL ? g_dbus_message_get_sender (message) : "(none)", + g_dbus_message_get_serial (message)); + _g_dbus_debug_print_unlock (); + } + #if 0 g_debug ("interface = `%s'", interface_name); g_debug ("member = `%s'", member); @@ -5133,14 +6068,14 @@ distribute_method_call (GDBusConnection *connection, object_path = g_dbus_message_get_path (message); g_assert (object_path != NULL); - eo = g_hash_table_lookup (connection->priv->map_object_path_to_eo, object_path); + eo = g_hash_table_lookup (connection->map_object_path_to_eo, object_path); if (eo != NULL) { if (obj_message_func (connection, eo, message)) goto out; } - es = g_hash_table_lookup (connection->priv->map_object_path_to_es, object_path); + es = g_hash_table_lookup (connection->map_object_path_to_es, object_path); if (es != NULL) { if (subtree_message_func (connection, es, message)) @@ -5149,7 +6084,7 @@ distribute_method_call (GDBusConnection *connection, if (subtree_path != NULL) { - es = g_hash_table_lookup (connection->priv->map_object_path_to_es, subtree_path); + es = g_hash_table_lookup (connection->map_object_path_to_es, subtree_path); if (es != NULL) { if (subtree_message_func (connection, es, message)) @@ -5160,7 +6095,14 @@ distribute_method_call (GDBusConnection *connection, if (handle_generic_unlocked (connection, message)) goto out; - /* if we end up here, the message has not been not handled */ + /* if we end up here, the message has not been not handled - so return an error saying this */ + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.UnknownMethod", + _("No such interface `%s' on object at path %s"), + interface_name, + object_path); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); + g_object_unref (reply); out: g_free (subtree_path); @@ -5257,6 +6199,7 @@ get_uninitialized_connection (GBusType bus_type, G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, "exit-on-close", TRUE, NULL); + g_free (address); } else { @@ -5446,6 +6389,3 @@ g_bus_get_finish (GAsyncResult *res, } /* ---------------------------------------------------------------------------------------------------- */ - -#define __G_DBUS_CONNECTION_C__ -#include "gioaliasdef.c"