GDBus: Don't use abstract sockets in test code
[platform/upstream/glib.git] / gio / gdbusconnection.c
index 9551b49..a4abc14 100644 (file)
 /*
  * 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
  *      kind of pitfalls it avoids
  *      - Export objects before claiming names
  *    - Talk about auto-starting services (cf. GBusNameWatcherFlags)
+ *
+ *  - 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
+ *
+ *     or similar.
  */
 
 #include "config.h"
@@ -185,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: <internal>
+ * @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)
@@ -380,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;
@@ -395,11 +471,20 @@ g_dbus_connection_dispose (GObject *object)
     {
       the_system_bus = 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
+    {
+      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)
@@ -424,6 +509,9 @@ g_dbus_connection_finalize (GObject *object)
   if (connection->auth != NULL)
     g_object_unref (connection->auth);
 
+  if (connection->credentials)
+    g_object_unref (connection->credentials);
+
   if (connection->stream != NULL)
     {
       /* We don't really care if closing the stream succeeds or not */
@@ -831,7 +919,7 @@ g_dbus_connection_init (GDBusConnection *connection)
   connection->map_sender_unique_name_to_signal_data_array = g_hash_table_new_full (g_str_hash,
                                                                                    g_str_equal,
                                                                                    g_free,
-                                                                                   NULL);
+                                                                                   (GDestroyNotify) g_ptr_array_unref);
 
   connection->map_object_path_to_eo = g_hash_table_new_full (g_str_hash,
                                                              g_str_equal,
@@ -1372,6 +1460,7 @@ g_dbus_connection_send_message_unlocked (GDBusConnection   *connection,
 
   g_dbus_message_set_serial (message, serial_to_use);
 
+  g_dbus_message_lock (message);
   _g_dbus_worker_send_message (connection->worker,
                                message,
                                (gchar*) blob,
@@ -1411,6 +1500,9 @@ g_dbus_connection_send_message_unlocked (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.
  *
+ * 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.
  *
@@ -1427,6 +1519,7 @@ 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);
@@ -1732,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 <xref linkend="gdbus-server"/> and <xref
  * linkend="gdbus-unix-fd-client"/> for an example of how to use this
  * low-level API to send and receive UNIX file descriptors.
@@ -1750,6 +1846,7 @@ 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);
@@ -1781,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
  */
@@ -1874,7 +1971,10 @@ 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
  */
@@ -1892,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);
 
@@ -1946,15 +2047,31 @@ 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);
@@ -1968,20 +2085,24 @@ on_worker_message_received (GDBusWorker  *worker,
     }
   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,
-                                            TRUE,
-                                            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;
 
@@ -2020,25 +2141,36 @@ 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 gboolean
+static GDBusMessage *
 on_worker_message_about_to_be_sent (GDBusWorker  *worker,
                                     GDBusMessage *message,
                                     gpointer      user_data)
 {
-  GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
+  GDBusConnection *connection;
   FilterCallback *filters;
-  gboolean consumed_by_filter;
   guint num_filters;
   guint n;
+  gboolean alive;
 
-  //g_debug ("in on_worker_message_about_to_be_sent");
-
+  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);
@@ -2052,22 +2184,22 @@ on_worker_message_about_to_be_sent (GDBusWorker  *worker,
     }
   CONNECTION_UNLOCK (connection);
 
-  /* the call the filters in order (without holding the lock) */
-  consumed_by_filter = FALSE;
+  /* then call the filters in order (without holding the lock) */
   for (n = 0; n < num_filters; n++)
     {
-      consumed_by_filter = filters[n].func (connection,
-                                            message,
-                                            FALSE,
-                                            filters[n].user_data);
-      if (consumed_by_filter)
+      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 consumed_by_filter;
+  return message;
 }
 
 /* Called in worker's thread - we must not block */
@@ -2077,7 +2209,19 @@ 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);
 
@@ -2085,6 +2229,8 @@ on_worker_closed (GDBusWorker *worker,
   if (!connection->closed)
     set_closed_unlocked (connection, remote_peer_vanished, error);
   CONNECTION_UNLOCK (connection);
+
+  g_object_unref (connection);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -2225,6 +2371,12 @@ initable_init (GInitable     *initable,
     }
 #endif
 
+  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);
+
   connection->worker = _g_dbus_worker_new (connection->stream,
                                            connection->capabilities,
                                            (connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING),
@@ -2679,7 +2831,9 @@ static guint _global_filter_id = 1;
  * 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.
+ * 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
@@ -2688,10 +2842,9 @@ static guint _global_filter_id = 1;
  * g_dbus_connection_signal_subscribe() or
  * g_dbus_connection_call() instead.
  *
- * If a filter consumes an incoming message (by returning %TRUE), the
- * message is not dispatched anywhere else - not even the standard
- * dispatch machinery (that API such as
- * g_dbus_connection_signal_subscribe() and
+ * 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.
@@ -3161,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);
     }
@@ -3408,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);
     }
@@ -3491,9 +3646,9 @@ exported_interface_free (ExportedInterface *ei)
 {
   g_dbus_interface_info_unref ((GDBusInterfaceInfo *) ei->interface_info);
 
-  if (ei->user_data_free_func != NULL)
-    /* TODO: push to thread-default mainloop */
-    ei->user_data_free_func (ei->user_data);
+  call_destroy_notify (ei->context,
+                       ei->user_data_free_func,
+                       ei->user_data);
 
   if (ei->context != NULL)
     g_main_context_unref (ei->context);
@@ -3597,7 +3752,8 @@ 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, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
@@ -3681,6 +3837,7 @@ invoke_set_property_in_idle_cb (gpointer _data)
   g_assert (reply != 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;
 }
@@ -3928,10 +4085,13 @@ invoke_get_all_properties_in_idle_cb (gpointer _data)
       if (value == NULL)
         continue;
 
+      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);
     }
   g_variant_builder_close (&builder);
 
@@ -4332,15 +4492,15 @@ validate_and_maybe_schedule_method_call (GDBusConnection            *connection,
   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);
 
   /* TODO: would be nicer with a real MethodData like we already
@@ -5171,9 +5331,9 @@ 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);
@@ -5888,10 +6048,12 @@ distribute_method_call (GDBusConnection *connection,
                "GDBus-debug:Incoming:\n"
                " <<<< METHOD INVOCATION %s.%s()\n"
                "      on object %s\n"
-               "      invoked by name %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_sender (message) != NULL ? g_dbus_message_get_sender (message) : "(none)",
+               g_dbus_message_get_serial (message));
       _g_dbus_debug_print_unlock ();
     }