1diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c 2index 1169df2..b40813c 100644 3--- a/gio/gdbusconnection.c 4+++ b/gio/gdbusconnection.c 5@@ -95,6 +95,7 @@ 6 #include <stdlib.h> 7 #include <string.h> 8 9+#include "glib-private.h" 10 #include "gdbusauth.h" 11 #include "gdbusutils.h" 12 #include "gdbusaddress.h" 13@@ -282,6 +283,153 @@ call_destroy_notify (GMainContext *context, 14 15 /* ---------------------------------------------------------------------------------------------------- */ 16 17+typedef struct 18+{ 19+ /* All fields are immutable after construction. */ 20+ gatomicrefcount ref_count; 21+ GDBusSignalCallback callback; 22+ gpointer user_data; 23+ GDestroyNotify user_data_free_func; 24+ guint id; 25+ GMainContext *context; 26+} SignalSubscriber; 27+ 28+static SignalSubscriber * 29+signal_subscriber_ref (SignalSubscriber *subscriber) 30+{ 31+ g_atomic_ref_count_inc (&subscriber->ref_count); 32+ return subscriber; 33+} 34+ 35+static void 36+signal_subscriber_unref (SignalSubscriber *subscriber) 37+{ 38+ if (g_atomic_ref_count_dec (&subscriber->ref_count)) 39+ { 40+ /* Destroy the user data. It doesn't matter which thread 41+ * signal_subscriber_unref() is called in (or whether it's called with a 42+ * lock held), as call_destroy_notify() always defers to the next 43+ * #GMainContext iteration. */ 44+ call_destroy_notify (subscriber->context, 45+ subscriber->user_data_free_func, 46+ subscriber->user_data); 47+ 48+ g_main_context_unref (subscriber->context); 49+ g_free (subscriber); 50+ } 51+} 52+ 53+typedef struct 54+{ 55+ /* 56+ * 1 reference while waiting for GetNameOwner() to finish 57+ * 1 reference for each SignalData that points to this one as its 58+ * shared_name_watcher 59+ */ 60+ grefcount ref_count; 61+ 62+ gchar *owner; 63+ guint32 get_name_owner_serial; 64+} WatchedName; 65+ 66+static WatchedName * 67+watched_name_new (void) 68+{ 69+ WatchedName *watched_name = g_new0 (WatchedName, 1); 70+ 71+ g_ref_count_init (&watched_name->ref_count); 72+ watched_name->owner = NULL; 73+ return g_steal_pointer (&watched_name); 74+} 75+ 76+typedef struct SignalData SignalData; 77+ 78+struct SignalData 79+{ 80+ gchar *rule; 81+ gchar *sender; 82+ gchar *interface_name; 83+ gchar *member; 84+ gchar *object_path; 85+ gchar *arg0; 86+ GDBusSignalFlags flags; 87+ GPtrArray *subscribers; /* (owned) (element-type SignalSubscriber) */ 88+ 89+ /* 90+ * If the sender is a well-known name, this is an unowned SignalData 91+ * representing the NameOwnerChanged signal that tracks its owner. 92+ * NULL if sender is NULL. 93+ * NULL if sender is its own owner (a unique name or DBUS_SERVICE_DBUS). 94+ * 95+ * Invariants: if not NULL, then 96+ * shared_name_watcher->sender == DBUS_SERVICE_DBUS 97+ * shared_name_watcher->interface_name == DBUS_INTERFACE_DBUS 98+ * shared_name_watcher->member == "NameOwnerChanged" 99+ * shared_name_watcher->object_path == DBUS_PATH_DBUS 100+ * shared_name_watcher->arg0 == sender 101+ * shared_name_watcher->flags == NONE 102+ * shared_name_watcher->watched_name == NULL 103+ */ 104+ SignalData *shared_name_watcher; 105+ 106+ /* 107+ * Non-NULL if this SignalData is another SignalData's shared_name_watcher. 108+ * One reference for each SignalData that has this one as its 109+ * shared_name_watcher. 110+ * Otherwise NULL. 111+ */ 112+ WatchedName *watched_name; 113+}; 114+ 115+static SignalData * 116+signal_data_new_take (gchar *rule, 117+ gchar *sender, 118+ gchar *interface_name, 119+ gchar *member, 120+ gchar *object_path, 121+ gchar *arg0, 122+ GDBusSignalFlags flags) 123+{ 124+ SignalData *signal_data = g_new0 (SignalData, 1); 125+ 126+ signal_data->rule = rule; 127+ signal_data->sender = sender; 128+ signal_data->interface_name = interface_name; 129+ signal_data->member = member; 130+ signal_data->object_path = object_path; 131+ signal_data->arg0 = arg0; 132+ signal_data->flags = flags; 133+ signal_data->subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref); 134+ return g_steal_pointer (&signal_data); 135+} 136+ 137+static void 138+signal_data_free (SignalData *signal_data) 139+{ 140+ /* The SignalData should not be freed while it still has subscribers */ 141+ g_assert (signal_data->subscribers->len == 0); 142+ 143+ /* The SignalData should not be freed while it is watching for 144+ * NameOwnerChanged on behalf of another SignalData */ 145+ g_assert (signal_data->watched_name == NULL); 146+ 147+ /* The SignalData should be detached from its name watcher, if any, 148+ * before it is freed */ 149+ g_assert (signal_data->shared_name_watcher == NULL); 150+ 151+ g_free (signal_data->rule); 152+ g_free (signal_data->sender); 153+ g_free (signal_data->interface_name); 154+ g_free (signal_data->member); 155+ g_free (signal_data->object_path); 156+ g_free (signal_data->arg0); 157+ g_ptr_array_unref (signal_data->subscribers); 158+ 159+ g_free (signal_data); 160+} 161+ 162+/* ---------------------------------------------------------------------------------------------------- */ 163+ 164 #ifdef G_OS_WIN32 165 #define CONNECTION_ENSURE_LOCK(obj) do { ; } while (FALSE) 166 #else 167@@ -408,6 +556,7 @@ struct _GDBusConnection 168 169 /* Map used for managing method replies, protected by @lock */ 170 GHashTable *map_method_serial_to_task; /* guint32 -> owned GTask* */ 171+ GHashTable *map_method_serial_to_name_watcher; /* guint32 -> unowned SignalData* */ 172 173 /* Maps used for managing signal subscription, protected by @lock */ 174 GHashTable *map_rule_to_signal_data; /* match rule (gchar*) -> SignalData */ 175@@ -656,6 +805,7 @@ g_dbus_connection_finalize (GObject *object) 176 g_error_free (connection->initialization_error); 177 178 g_hash_table_unref (connection->map_method_serial_to_task); 179+ g_hash_table_unref (connection->map_method_serial_to_name_watcher); 180 181 g_hash_table_unref (connection->map_rule_to_signal_data); 182 g_hash_table_unref (connection->map_id_to_signal_data); 183@@ -1060,6 +1210,7 @@ g_dbus_connection_init (GDBusConnection *connection) 184 g_mutex_init (&connection->init_lock); 185 186 connection->map_method_serial_to_task = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); 187+ connection->map_method_serial_to_name_watcher = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); 188 189 connection->map_rule_to_signal_data = g_hash_table_new (g_str_hash, 190 g_str_equal); 191@@ -2178,6 +2329,191 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connecti 192 193 /* ---------------------------------------------------------------------------------------------------- */ 194 195+/* 196+ * Called in any thread. 197+ * Must hold the connection lock when calling this, unless 198+ * connection->finalizing is TRUE. 199+ */ 200+static void 201+name_watcher_unref_watched_name (GDBusConnection *connection, 202+ SignalData *name_watcher) 203+{ 204+ WatchedName *watched_name = name_watcher->watched_name; 205+ 206+ g_assert (watched_name != NULL); 207+ 208+ if (!g_ref_count_dec (&watched_name->ref_count)) 209+ return; 210+ 211+ /* Removing watched_name from the name_watcher may result in 212+ * name_watcher being freed, so we must make sure name_watcher is no 213+ * longer in map_method_serial_to_name_watcher. 214+ * 215+ * If we stop watching the name while our GetNameOwner call was still 216+ * in-flight, then when the reply eventually arrives, we will not find 217+ * its serial number in the map and harmlessly ignore it as a result. */ 218+ if (watched_name->get_name_owner_serial != 0) 219+ g_hash_table_remove (connection->map_method_serial_to_name_watcher, 220+ GUINT_TO_POINTER (watched_name->get_name_owner_serial)); 221+ 222+ name_watcher->watched_name = NULL; 223+ g_free (watched_name->owner); 224+ g_free (watched_name); 225+} 226+ 227+/* called in GDBusWorker thread with lock held */ 228+static void 229+name_watcher_set_name_owner_unlocked (SignalData *name_watcher, 230+ const char *new_owner) 231+{ 232+ if (new_owner != NULL && new_owner[0] == '\0') 233+ new_owner = NULL; 234+ 235+ g_assert (name_watcher->watched_name != NULL); 236+ g_set_str (&name_watcher->watched_name->owner, new_owner); 237+} 238+ 239+/* called in GDBusWorker thread with lock held */ 240+static void 241+name_watcher_deliver_name_owner_changed_unlocked (SignalData *name_watcher, 242+ GDBusMessage *message) 243+{ 244+ GVariant *body; 245+ 246+ body = g_dbus_message_get_body (message); 247+ 248+ if (G_LIKELY (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(sss)")))) 249+ { 250+ const char *name; 251+ const char *new_owner; 252+ 253+ g_variant_get (body, "(&s&s&s)", &name, NULL, &new_owner); 254+ 255+ /* Our caller already checked this */ 256+ g_assert (g_strcmp0 (name_watcher->arg0, name) == 0); 257+ 258+ if (G_LIKELY (new_owner[0] == '\0' || g_dbus_is_unique_name (new_owner))) 259+ name_watcher_set_name_owner_unlocked (name_watcher, new_owner); 260+ else 261+ g_warning ("Received NameOwnerChanged signal with invalid owner \"%s\" for \"%s\"", 262+ new_owner, name); 263+ } 264+ else 265+ { 266+ g_warning ("Received NameOwnerChanged signal with unexpected " 267+ "signature %s", 268+ body == NULL ? "()" : g_variant_get_type_string (body)); 269+ 270+ } 271+} 272+ 273+/* called in GDBusWorker thread with lock held */ 274+static void 275+name_watcher_deliver_get_name_owner_reply_unlocked (SignalData *name_watcher, 276+ GDBusConnection *connection, 277+ GDBusMessage *message) 278+{ 279+ GDBusMessageType type; 280+ GVariant *body; 281+ WatchedName *watched_name; 282+ 283+ watched_name = name_watcher->watched_name; 284+ g_assert (watched_name != NULL); 285+ g_assert (watched_name->get_name_owner_serial != 0); 286+ 287+ type = g_dbus_message_get_message_type (message); 288+ body = g_dbus_message_get_body (message); 289+ 290+ if (type == G_DBUS_MESSAGE_TYPE_ERROR) 291+ { 292+ if (g_strcmp0 (g_dbus_message_get_error_name (message), 293+ "org.freedesktop.DBus.Error.NameHasNoOwner")) 294+ name_watcher_set_name_owner_unlocked (name_watcher, NULL); 295+ /* else it's something like NoReply or AccessDenied, which tells 296+ * us nothing - leave the owner set to whatever we most recently 297+ * learned from NameOwnerChanged, or NULL */ 298+ } 299+ else if (type != G_DBUS_MESSAGE_TYPE_METHOD_RETURN) 300+ { 301+ g_warning ("Received GetNameOwner reply with unexpected type %d", 302+ type); 303+ } 304+ else if (G_LIKELY (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))) 305+ { 306+ const char *new_owner; 307+ 308+ g_variant_get (body, "(&s)", &new_owner); 309+ 310+ if (G_LIKELY (g_dbus_is_unique_name (new_owner))) 311+ name_watcher_set_name_owner_unlocked (name_watcher, new_owner); 312+ else 313+ g_warning ("Received GetNameOwner reply with invalid owner \"%s\" for \"%s\"", 314+ new_owner, name_watcher->arg0); 315+ } 316+ else 317+ { 318+ g_warning ("Received GetNameOwner reply with unexpected signature %s", 319+ body == NULL ? "()" : g_variant_get_type_string (body)); 320+ } 321+ 322+ g_hash_table_remove (connection->map_method_serial_to_name_watcher, 323+ GUINT_TO_POINTER (watched_name->get_name_owner_serial)); 324+ watched_name->get_name_owner_serial = 0; 325+} 326+ 327+/* Called in a user thread, lock is held */ 328+static void 329+name_watcher_call_get_name_owner_unlocked (GDBusConnection *connection, 330+ SignalData *name_watcher) 331+{ 332+ GDBusMessage *message; 333+ GError *local_error = NULL; 334+ WatchedName *watched_name; 335+ 336+ g_assert (g_strcmp0 (name_watcher->sender, DBUS_SERVICE_DBUS) == 0); 337+ g_assert (g_strcmp0 (name_watcher->interface_name, DBUS_INTERFACE_DBUS) == 0); 338+ g_assert (g_strcmp0 (name_watcher->member, "NameOwnerChanged") == 0); 339+ g_assert (g_strcmp0 (name_watcher->object_path, DBUS_PATH_DBUS) == 0); 340+ /* arg0 of the NameOwnerChanged message is the well-known name whose owner 341+ * we are interested in */ 342+ g_assert (g_dbus_is_name (name_watcher->arg0)); 343+ g_assert (name_watcher->flags == G_DBUS_SIGNAL_FLAGS_NONE); 344+ 345+ watched_name = name_watcher->watched_name; 346+ g_assert (watched_name != NULL); 347+ g_assert (watched_name->owner == NULL); 348+ g_assert (watched_name->get_name_owner_serial == 0); 349+ g_assert (name_watcher->shared_name_watcher == NULL); 350+ 351+ message = g_dbus_message_new_method_call (DBUS_SERVICE_DBUS, 352+ DBUS_PATH_DBUS, 353+ DBUS_INTERFACE_DBUS, 354+ "GetNameOwner"); 355+ g_dbus_message_set_body (message, g_variant_new ("(s)", name_watcher->arg0)); 356+ 357+ if (g_dbus_connection_send_message_unlocked (connection, message, 358+ G_DBUS_SEND_MESSAGE_FLAGS_NONE, 359+ &watched_name->get_name_owner_serial, 360+ &local_error)) 361+ { 362+ g_assert (watched_name->get_name_owner_serial != 0); 363+ g_hash_table_insert (connection->map_method_serial_to_name_watcher, 364+ GUINT_TO_POINTER (watched_name->get_name_owner_serial), 365+ name_watcher); 366+ } 367+ else 368+ { 369+ g_critical ("Error while sending GetNameOwner() message: %s", 370+ local_error->message); 371+ g_clear_error (&local_error); 372+ g_assert (watched_name->get_name_owner_serial == 0); 373+ } 374+ 375+ g_object_unref (message); 376+} 377+ 378+/* ---------------------------------------------------------------------------------------------------- */ 379+ 380 typedef struct 381 { 382 guint id; 383@@ -3239,69 +3575,6 @@ g_dbus_connection_remove_filter (GDBusConnection *connection, 384 385 /* ---------------------------------------------------------------------------------------------------- */ 386 387-typedef struct 388-{ 389- gchar *rule; 390- gchar *sender; 391- gchar *sender_unique_name; /* if sender is unique or org.freedesktop.DBus, then that name... otherwise blank */ 392- gchar *interface_name; 393- gchar *member; 394- gchar *object_path; 395- gchar *arg0; 396- GDBusSignalFlags flags; 397- GPtrArray *subscribers; /* (owned) (element-type SignalSubscriber) */ 398-} SignalData; 399- 400-static void 401-signal_data_free (SignalData *signal_data) 402-{ 403- g_free (signal_data->rule); 404- g_free (signal_data->sender); 405- g_free (signal_data->sender_unique_name); 406- g_free (signal_data->interface_name); 407- g_free (signal_data->member); 408- g_free (signal_data->object_path); 409- g_free (signal_data->arg0); 410- g_ptr_array_unref (signal_data->subscribers); 411- g_free (signal_data); 412-} 413- 414-typedef struct 415-{ 416- /* All fields are immutable after construction. */ 417- gatomicrefcount ref_count; 418- GDBusSignalCallback callback; 419- gpointer user_data; 420- GDestroyNotify user_data_free_func; 421- guint id; 422- GMainContext *context; 423-} SignalSubscriber; 424- 425-static SignalSubscriber * 426-signal_subscriber_ref (SignalSubscriber *subscriber) 427-{ 428- g_atomic_ref_count_inc (&subscriber->ref_count); 429- return subscriber; 430-} 431- 432-static void 433-signal_subscriber_unref (SignalSubscriber *subscriber) 434-{ 435- if (g_atomic_ref_count_dec (&subscriber->ref_count)) 436- { 437- /* Destroy the user data. It doesn’t matter which thread 438- * signal_subscriber_unref() is called in (or whether it’s called with a 439- * lock held), as call_destroy_notify() always defers to the next 440- * #GMainContext iteration. */ 441- call_destroy_notify (subscriber->context, 442- subscriber->user_data_free_func, 443- subscriber->user_data); 444- 445- g_main_context_unref (subscriber->context); 446- g_free (subscriber); 447- } 448-} 449- 450 static gchar * 451 args_to_rule (const gchar *sender, 452 const gchar *interface_name, 453@@ -3413,7 +3686,7 @@ remove_match_rule (GDBusConnection *connection, 454 static gboolean 455 is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) 456 { 457- return g_strcmp0 (signal_data->sender_unique_name, "org.freedesktop.DBus") == 0 && 458+ return g_strcmp0 (signal_data->sender, "org.freedesktop.DBus") == 0 && 459 g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0 && 460 g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0 && 461 (g_strcmp0 (signal_data->member, "NameLost") == 0 || 462@@ -3422,6 +3695,43 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) 463 464 /* ---------------------------------------------------------------------------------------------------- */ 465 466+/* called in any thread, connection lock is held */ 467+static void 468+add_signal_data (GDBusConnection *connection, 469+ SignalData *signal_data, 470+ const char *sender_unique_name) 471+{ 472+ GPtrArray *signal_data_array; 473+ 474+ g_hash_table_insert (connection->map_rule_to_signal_data, 475+ signal_data->rule, 476+ signal_data); 477+ 478+ /* Add the match rule to the bus... 479+ * 480+ * Avoid adding match rules for NameLost and NameAcquired messages - the bus will 481+ * always send such messages to us. 482+ */ 483+ if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) 484+ { 485+ if (!is_signal_data_for_name_lost_or_acquired (signal_data)) 486+ add_match_rule (connection, signal_data->rule); 487+ } 488+ 489+ signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, 490+ sender_unique_name); 491+ if (signal_data_array == NULL) 492+ { 493+ signal_data_array = g_ptr_array_new (); 494+ g_hash_table_insert (connection->map_sender_unique_name_to_signal_data_array, 495+ g_strdup (sender_unique_name), 496+ signal_data_array); 497+ } 498+ g_ptr_array_add (signal_data_array, signal_data); 499+} 500+ 501+/* ---------------------------------------------------------------------------------------------------- */ 502+ 503 /** 504 * g_dbus_connection_signal_subscribe: 505 * @connection: a #GDBusConnection 506@@ -3510,8 +3820,9 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, 507 { 508 gchar *rule; 509 SignalData *signal_data; 510+ SignalData *name_watcher = NULL; 511 SignalSubscriber *subscriber; 512- GPtrArray *signal_data_array; 513+ gboolean sender_is_its_own_owner; 514 const gchar *sender_unique_name; 515 516 /* Right now we abort if AddMatch() fails since it can only fail with the bus being in 517@@ -3547,6 +3858,11 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, 518 rule = args_to_rule (sender, interface_name, member, object_path, arg0, flags); 519 520 if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0)) 521+ sender_is_its_own_owner = TRUE; 522+ else 523+ sender_is_its_own_owner = FALSE; 524+ 525+ if (sender_is_its_own_owner) 526 sender_unique_name = sender; 527 else 528 sender_unique_name = ""; 529@@ -3568,43 +3884,61 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, 530 goto out; 531 } 532 533- signal_data = g_new0 (SignalData, 1); 534- signal_data->rule = rule; 535- signal_data->sender = g_strdup (sender); 536- signal_data->sender_unique_name = g_strdup (sender_unique_name); 537- signal_data->interface_name = g_strdup (interface_name); 538- signal_data->member = g_strdup (member); 539- signal_data->object_path = g_strdup (object_path); 540- signal_data->arg0 = g_strdup (arg0); 541- signal_data->flags = flags; 542- signal_data->subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref); 543+ signal_data = signal_data_new_take (g_steal_pointer (&rule), 544+ g_strdup (sender), 545+ g_strdup (interface_name), 546+ g_strdup (member), 547+ g_strdup (object_path), 548+ g_strdup (arg0), 549+ flags); 550 g_ptr_array_add (signal_data->subscribers, subscriber); 551 552- g_hash_table_insert (connection->map_rule_to_signal_data, 553- signal_data->rule, 554- signal_data); 555+ /* If subscribing to a signal from a specific sender with a well-known 556+ * name, we must first subscribe to NameOwnerChanged signals for that 557+ * well-known name, so that we can match the current owner of the name 558+ * against the sender of each signal. */ 559+ if (sender != NULL && !sender_is_its_own_owner) 560+ { 561+ gchar *name_owner_rule = NULL; 562+ /* We already checked that sender != NULL implies MESSAGE_BUS_CONNECTION */ 563+ g_assert (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION); 564+ 565+ name_owner_rule = args_to_rule (DBUS_SERVICE_DBUS, 566+ DBUS_INTERFACE_DBUS, 567+ "NameOwnerChanged", 568+ DBUS_PATH_DBUS, 569+ sender, 570+ G_DBUS_SIGNAL_FLAGS_NONE); 571+ name_watcher = g_hash_table_lookup (connection->map_rule_to_signal_data, name_owner_rule); 572+ 573+ if (name_watcher == NULL) 574+ { 575+ name_watcher = signal_data_new_take (g_steal_pointer (&name_owner_rule), 576+ g_strdup (DBUS_SERVICE_DBUS), 577+ g_strdup (DBUS_INTERFACE_DBUS), 578+ g_strdup ("NameOwnerChanged"), 579+ g_strdup (DBUS_PATH_DBUS), 580+ g_strdup (sender), 581+ G_DBUS_SIGNAL_FLAGS_NONE); 582+ add_signal_data (connection, name_watcher, DBUS_SERVICE_DBUS); 583+ } 584 585- /* Add the match rule to the bus... 586- * 587- * Avoid adding match rules for NameLost and NameAcquired messages - the bus will 588- * always send such messages to us. 589- */ 590- if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) 591- { 592- if (!is_signal_data_for_name_lost_or_acquired (signal_data)) 593- add_match_rule (connection, signal_data->rule); 594- } 595+ if (name_watcher->watched_name == NULL) 596+ { 597+ name_watcher->watched_name = watched_name_new (); 598+ name_watcher_call_get_name_owner_unlocked (connection, name_watcher); 599+ } 600+ else 601+ { 602+ g_ref_count_inc (&name_watcher->watched_name->ref_count); 603+ } 604 605- signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, 606- signal_data->sender_unique_name); 607- if (signal_data_array == NULL) 608- { 609- signal_data_array = g_ptr_array_new (); 610- g_hash_table_insert (connection->map_sender_unique_name_to_signal_data_array, 611- g_strdup (signal_data->sender_unique_name), 612- signal_data_array); 613+ signal_data->shared_name_watcher = name_watcher; 614+ 615+ g_clear_pointer (&name_owner_rule, g_free); 616 } 617- g_ptr_array_add (signal_data_array, signal_data); 618+ 619+ add_signal_data (connection, signal_data, sender_unique_name); 620 621 out: 622 g_hash_table_insert (connection->map_id_to_signal_data, 623@@ -3618,6 +3952,75 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, 624 625 /* ---------------------------------------------------------------------------------------------------- */ 626 627+/* 628+ * Called in any thread. 629+ * Must hold the connection lock when calling this, unless 630+ * connection->finalizing is TRUE. 631+ * May free signal_data, so do not dereference it after this. 632+ */ 633+static void 634+remove_signal_data_if_unused (GDBusConnection *connection, 635+ SignalData *signal_data) 636+{ 637+ const gchar *sender_unique_name; 638+ GPtrArray *signal_data_array; 639+ 640+ /* Cannot remove while there are still subscribers */ 641+ if (signal_data->subscribers->len != 0) 642+ return; 643+ 644+ /* Cannot remove while another SignalData is still using this one 645+ * as its shared_name_watcher, which holds watched_name->ref_count > 0 */ 646+ if (signal_data->watched_name != NULL) 647+ return; 648+ 649+ /* Point of no return: we have committed to removing it */ 650+ 651+ if (signal_data->sender != NULL && signal_data->shared_name_watcher == NULL) 652+ sender_unique_name = signal_data->sender; 653+ else 654+ sender_unique_name = ""; 655+ 656+ g_warn_if_fail (g_hash_table_remove (connection->map_rule_to_signal_data, signal_data->rule)); 657+ 658+ signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, 659+ sender_unique_name); 660+ g_warn_if_fail (signal_data_array != NULL); 661+ g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); 662+ 663+ if (signal_data_array->len == 0) 664+ { 665+ g_warn_if_fail (g_hash_table_remove (connection->map_sender_unique_name_to_signal_data_array, 666+ sender_unique_name)); 667+ } 668+ 669+ /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */ 670+ if ((connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) && 671+ !is_signal_data_for_name_lost_or_acquired (signal_data) && 672+ !g_dbus_connection_is_closed (connection) && 673+ !connection->finalizing) 674+ { 675+ /* The check for g_dbus_connection_is_closed() means that 676+ * sending the RemoveMatch message can't fail with 677+ * G_IO_ERROR_CLOSED, because we're holding the lock, 678+ * so on_worker_closed() can't happen between the check we just 679+ * did, and releasing the lock later. 680+ */ 681+ remove_match_rule (connection, signal_data->rule); 682+ } 683+ 684+ if (signal_data->shared_name_watcher != NULL) 685+ { 686+ SignalData *name_watcher = g_steal_pointer (&signal_data->shared_name_watcher); 687+ 688+ name_watcher_unref_watched_name (connection, name_watcher); 689+ /* May free signal_data */ 690+ remove_signal_data_if_unused (connection, name_watcher); 691+ } 692+ 693+ signal_data_free (signal_data); 694+} 695+ 696 /* called in any thread */ 697 /* must hold lock when calling this (except if connection->finalizing is TRUE) 698 * returns the number of removed subscribers */ 699@@ -3626,7 +4029,6 @@ unsubscribe_id_internal (GDBusConnection *connection, 700 guint subscription_id) 701 { 702 SignalData *signal_data; 703- GPtrArray *signal_data_array; 704 guint n; 705 guint n_removed = 0; 706 707@@ -3654,39 +4056,8 @@ unsubscribe_id_internal (GDBusConnection *connection, 708 n_removed++; 709 g_ptr_array_remove_index_fast (signal_data->subscribers, n); 710 711- if (signal_data->subscribers->len == 0) 712- { 713- g_warn_if_fail (g_hash_table_remove (connection->map_rule_to_signal_data, signal_data->rule)); 714- 715- signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, 716- signal_data->sender_unique_name); 717- g_warn_if_fail (signal_data_array != NULL); 718- g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); 719- 720- if (signal_data_array->len == 0) 721- { 722- g_warn_if_fail (g_hash_table_remove (connection->map_sender_unique_name_to_signal_data_array, 723- signal_data->sender_unique_name)); 724- } 725- 726- /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */ 727- if ((connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) && 728- !is_signal_data_for_name_lost_or_acquired (signal_data) && 729- !g_dbus_connection_is_closed (connection) && 730- !connection->finalizing) 731- { 732- /* The check for g_dbus_connection_is_closed() means that 733- * sending the RemoveMatch message can't fail with 734- * G_IO_ERROR_CLOSED, because we're holding the lock, 735- * so on_worker_closed() can't happen between the check we just 736- * did, and releasing the lock later. 737- */ 738- remove_match_rule (connection, signal_data->rule); 739- } 740- 741- signal_data_free (signal_data); 742- } 743- 744+ /* May free signal_data */ 745+ remove_signal_data_if_unused (connection, signal_data); 746 goto out; 747 } 748 749@@ -3901,6 +4272,46 @@ schedule_callbacks (GDBusConnection *connection, 750 if (signal_data->object_path != NULL && g_strcmp0 (signal_data->object_path, path) != 0) 751 continue; 752 753+ if (signal_data->shared_name_watcher != NULL) 754+ { 755+ /* We want signals from a specified well-known name, which means 756+ * the signal's sender needs to be the unique name that currently 757+ * owns that well-known name, and we will have found this 758+ * SignalData in 759+ * connection->map_sender_unique_name_to_signal_data_array[""]. */ 760+ const WatchedName *watched_name; 761+ const char *current_owner; 762+ 763+ g_assert (signal_data->sender != NULL); 764+ /* Invariant: We never need to watch for the owner of a unique 765+ * name, or for the owner of DBUS_SERVICE_DBUS, either of which 766+ * is always its own owner */ 767+ g_assert (!g_dbus_is_unique_name (signal_data->sender)); 768+ g_assert (g_strcmp0 (signal_data->sender, DBUS_SERVICE_DBUS) != 0); 769+ 770+ watched_name = signal_data->shared_name_watcher->watched_name; 771+ g_assert (watched_name != NULL); 772+ current_owner = watched_name->owner; 773+ 774+ /* Skip the signal if the actual sender is not known to own 775+ * the required name */ 776+ if (current_owner == NULL || g_strcmp0 (current_owner, sender) != 0) 777+ continue; 778+ } 779+ else if (signal_data->sender != NULL) 780+ { 781+ /* We want signals from a unique name or o.fd.DBus... */ 782+ g_assert (g_dbus_is_unique_name (signal_data->sender) 783+ || g_str_equal (signal_data->sender, DBUS_SERVICE_DBUS)); 784+ 785+ /* ... which means we must have found this SignalData in 786+ * connection->map_sender_unique_name_to_signal_data_array[signal_data->sender], 787+ * therefore we would only have found it if the signal's 788+ * actual sender matches the required signal_data->sender */ 789+ g_assert (g_strcmp0 (signal_data->sender, sender) == 0); 790+ } 791+ /* else the sender is unspecified and we will accept anything */ 792+ 793 if (signal_data->arg0 != NULL) 794 { 795 if (arg0 == NULL) 796@@ -3920,6 +4331,17 @@ schedule_callbacks (GDBusConnection *connection, 797 continue; 798 } 799 800+ if (signal_data->watched_name != NULL) 801+ { 802+ /* Invariant: SignalData should only have a watched_name if it 803+ * represents the NameOwnerChanged signal */ 804+ g_assert (g_strcmp0 (sender, DBUS_SERVICE_DBUS) == 0); 805+ g_assert (g_strcmp0 (interface, DBUS_INTERFACE_DBUS) == 0); 806+ g_assert (g_strcmp0 (path, DBUS_PATH_DBUS) == 0); 807+ g_assert (g_strcmp0 (member, "NameOwnerChanged") == 0); 808+ name_watcher_deliver_name_owner_changed_unlocked (signal_data, message); 809+ } 810+ 811 for (m = 0; m < signal_data->subscribers->len; m++) 812 { 813 SignalSubscriber *subscriber = signal_data->subscribers->pdata[m]; 814@@ -3981,7 +4403,7 @@ distribute_signals (GDBusConnection *connection, 815 schedule_callbacks (connection, signal_data_array, message, sender); 816 } 817 818- /* collect subscribers not matching on sender */ 819+ /* collect subscribers not matching on sender, or matching a well-known name */ 820 signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, ""); 821 if (signal_data_array != NULL) 822 schedule_callbacks (connection, signal_data_array, message, sender); 823diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c 824index ecef6cd..d4d0cd9 100644 825--- a/gio/gdbusmessage.c 826+++ b/gio/gdbusmessage.c 827@@ -506,6 +506,7 @@ struct _GDBusMessage 828 guint32 serial; 829 GHashTable *headers; 830 GVariant *body; 831+ GVariant *arg0_cache; /* (nullable) (owned) */ 832 #ifdef G_OS_UNIX 833 GUnixFDList *fd_list; 834 #endif 835@@ -528,6 +529,7 @@ g_dbus_message_finalize (GObject *object) 836 g_hash_table_unref (message->headers); 837 if (message->body != NULL) 838 g_variant_unref (message->body); 839+ g_clear_pointer (&message->arg0_cache, g_variant_unref); 840 #ifdef G_OS_UNIX 841 if (message->fd_list != NULL) 842 g_object_unref (message->fd_list); 843@@ -1163,6 +1165,7 @@ g_dbus_message_set_body (GDBusMessage *message, 844 if (body == NULL) 845 { 846 message->body = NULL; 847+ message->arg0_cache = NULL; 848 g_dbus_message_set_signature (message, NULL); 849 } 850 else 851@@ -1173,6 +1176,12 @@ g_dbus_message_set_body (GDBusMessage *message, 852 853 message->body = g_variant_ref_sink (body); 854 855+ if (g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE) && 856+ g_variant_n_children (message->body) > 0) 857+ message->arg0_cache = g_variant_get_child_value (message->body, 0); 858+ else 859+ message->arg0_cache = NULL; 860+ 861 type_string = g_variant_get_type_string (body); 862 type_string_len = strlen (type_string); 863 g_assert (type_string_len >= 2); 864@@ -2325,6 +2334,14 @@ g_dbus_message_new_from_blob (guchar *blob, 865 2, 866 &local_error); 867 g_variant_type_free (variant_type); 868+ 869+ if (message->body != NULL && 870+ g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE) && 871+ g_variant_n_children (message->body) > 0) 872+ message->arg0_cache = g_variant_get_child_value (message->body, 0); 873+ else 874+ message->arg0_cache = NULL; 875+ 876 if (message->body == NULL) 877 goto fail; 878 } 879@@ -3364,22 +3381,12 @@ g_dbus_message_set_signature (GDBusMessage *message, 880 const gchar * 881 g_dbus_message_get_arg0 (GDBusMessage *message) 882 { 883- const gchar *ret; 884- 885 g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); 886 887- ret = NULL; 888- 889- if (message->body != NULL && g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE)) 890- { 891- GVariant *item; 892- item = g_variant_get_child_value (message->body, 0); 893- if (g_variant_is_of_type (item, G_VARIANT_TYPE_STRING)) 894- ret = g_variant_get_string (item, NULL); 895- g_variant_unref (item); 896- } 897- 898- return ret; 899+ if (message->arg0_cache != NULL && 900+ g_variant_is_of_type (message->arg0_cache, G_VARIANT_TYPE_STRING)) 901+ return g_variant_get_string (message->arg0_cache, NULL); 902+ return NULL; 903 } 904 905 /* ---------------------------------------------------------------------------------------------------- */ 906@@ -3822,6 +3829,8 @@ g_dbus_message_copy (GDBusMessage *message, 907 * to just ref (as opposed to deep-copying) the GVariant instances 908 */ 909 ret->body = message->body != NULL ? g_variant_ref (message->body) : NULL; 910+ 911+ ret->arg0_cache = message->arg0_cache != NULL ? g_variant_ref (message->arg0_cache) : NULL; 912 g_hash_table_iter_init (&iter, message->headers); 913 while (g_hash_table_iter_next (&iter, &header_key, (gpointer) &header_value)) 914 g_hash_table_insert (ret->headers, header_key, g_variant_ref (header_value)); 915diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h 916index 72d2c32..ac737bd 100644 917--- a/gio/gdbusprivate.h 918+++ b/gio/gdbusprivate.h 919@@ -29,6 +29,11 @@ 920 921 G_BEGIN_DECLS 922 923+/* Bus name, interface and object path of the message bus itself */ 924+#define DBUS_SERVICE_DBUS "org.freedesktop.DBus" 925+#define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS 926+#define DBUS_PATH_DBUS "/org/freedesktop/DBus" 927+ 928 /* ---------------------------------------------------------------------------------------------------- */ 929 930 typedef struct GDBusWorker GDBusWorker; 931diff --git a/gio/tests/gdbus-proxy.c b/gio/tests/gdbus-proxy.c 932index eed75ac..9303d00 100644 933--- a/gio/tests/gdbus-proxy.c 934+++ b/gio/tests/gdbus-proxy.c 935@@ -778,6 +778,12 @@ kill_test_service (GDBusConnection *connection) 936 while (!name_disappeared) 937 g_main_context_iteration (NULL, TRUE); 938 939+ /* GDBusConnection doesn't guarantee that different subscriptions to the 940+ * same signal will get their callbacks scheduled in any particular order, 941+ * so make sure they have all happened */ 942+ while (g_main_context_iteration (NULL, FALSE)) 943+ continue; 944+ 945 g_bus_unwatch_name (watch_id); 946 #else 947 g_warning ("Can't kill com.example.TestService"); 948diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c 949new file mode 100644 950index 0000000..4487a92 951--- /dev/null 952+++ b/gio/tests/gdbus-subscribe.c 953@@ -0,0 +1,1342 @@ 954+/* 955+ * Copyright 2024 Collabora Ltd. 956+ * SPDX-License-Identifier: LGPL-2.1-or-later 957+ */ 958+ 959+#include <gio/gio.h> 960+ 961+#include "gdbus-tests.h" 962+ 963+/* From the D-Bus Specification */ 964+#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 965+ 966+#define DBUS_SERVICE_DBUS "org.freedesktop.DBus" 967+#define DBUS_PATH_DBUS "/org/freedesktop/DBus" 968+#define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS 969+#define NAME_OWNER_CHANGED "NameOwnerChanged" 970+ 971+/* A signal that each connection emits to indicate that it has finished 972+ * emitting other signals */ 973+#define FINISHED_PATH "/org/gtk/Test/Finished" 974+#define FINISHED_INTERFACE "org.gtk.Test.Finished" 975+#define FINISHED_SIGNAL "Finished" 976+ 977+/* A signal emitted during testing */ 978+#define EXAMPLE_PATH "/org/gtk/GDBus/ExampleInterface" 979+#define EXAMPLE_INTERFACE "org.gtk.GDBus.ExampleInterface" 980+#define FOO_SIGNAL "Foo" 981+ 982+#define ALREADY_OWNED_NAME "org.gtk.Test.AlreadyOwned" 983+#define OWNED_LATER_NAME "org.gtk.Test.OwnedLater" 984+ 985+/* Log @s in a debug message. */ 986+static inline const char * 987+nonnull (const char *s, 988+ const char *if_null) 989+{ 990+ return (s == NULL) ? if_null : s; 991+} 992+ 993+typedef enum 994+{ 995+ TEST_CONN_NONE, 996+ TEST_CONN_FIRST, 997+ /* A connection that subscribes to signals */ 998+ TEST_CONN_SUBSCRIBER = TEST_CONN_FIRST, 999+ /* A mockup of a legitimate service */ 1000+ TEST_CONN_SERVICE, 1001+ /* A mockup of a second legitimate service */ 1002+ TEST_CONN_SERVICE2, 1003+ /* A connection that tries to trick @subscriber into processing its signals 1004+ * as if they came from @service */ 1005+ TEST_CONN_ATTACKER, 1006+ NUM_TEST_CONNS 1007+} TestConn; 1008+ 1009+static const char * const test_conn_descriptions[NUM_TEST_CONNS] = 1010+{ 1011+ "(unused)", 1012+ "subscriber", 1013+ "service", 1014+ "service 2", 1015+ "attacker" 1016+}; 1017+ 1018+typedef enum 1019+{ 1020+ SUBSCRIPTION_MODE_CONN, 1021+ SUBSCRIPTION_MODE_PROXY, 1022+ SUBSCRIPTION_MODE_PARALLEL 1023+} SubscriptionMode; 1024+ 1025+typedef struct 1026+{ 1027+ GDBusProxy *received_by_proxy; 1028+ TestConn sender; 1029+ char *path; 1030+ char *iface; 1031+ char *member; 1032+ GVariant *parameters; 1033+ char *arg0; 1034+ guint32 step; 1035+} ReceivedMessage; 1036+ 1037+static void 1038+received_message_free (ReceivedMessage *self) 1039+{ 1040+ 1041+ g_clear_object (&self->received_by_proxy); 1042+ g_free (self->path); 1043+ g_free (self->iface); 1044+ g_free (self->member); 1045+ g_clear_pointer (&self->parameters, g_variant_unref); 1046+ g_free (self->arg0); 1047+ g_free (self); 1048+} 1049+ 1050+typedef struct 1051+{ 1052+ TestConn sender; 1053+ TestConn unicast_to; 1054+ const char *path; 1055+ const char *iface; 1056+ const char *member; 1057+ const char *arg0; 1058+ const char *args; 1059+ guint received_by_conn; 1060+ guint received_by_proxy; 1061+} TestEmitSignal; 1062+ 1063+typedef struct 1064+{ 1065+ const char *string_sender; 1066+ TestConn unique_sender; 1067+ const char *path; 1068+ const char *iface; 1069+ const char *member; 1070+ const char *arg0; 1071+ GDBusSignalFlags flags; 1072+ gboolean unsubscribe_immediately; 1073+} TestSubscribe; 1074+ 1075+typedef struct 1076+{ 1077+ const char *name; 1078+ TestConn owner; 1079+ guint received_by_conn; 1080+ guint received_by_proxy; 1081+} TestOwnName; 1082+ 1083+typedef enum 1084+{ 1085+ TEST_ACTION_NONE = 0, 1086+ TEST_ACTION_SUBSCRIBE, 1087+ TEST_ACTION_EMIT_SIGNAL, 1088+ TEST_ACTION_OWN_NAME, 1089+} TestAction; 1090+ 1091+typedef struct 1092+{ 1093+ TestAction action; 1094+ union { 1095+ TestEmitSignal signal; 1096+ TestSubscribe subscribe; 1097+ TestOwnName own_name; 1098+ guint unsubscribe_undo_step; 1099+ } u; 1100+} TestStep; 1101+ 1102+/* Arbitrary, extend as necessary to accommodate the longest test */ 1103+#define MAX_TEST_STEPS 10 1104+ 1105+typedef struct 1106+{ 1107+ const char *description; 1108+ TestStep steps[MAX_TEST_STEPS]; 1109+} TestPlan; 1110+ 1111+static const TestPlan plan_simple = 1112+{ 1113+ .description = "A broadcast is only received after subscribing to it", 1114+ .steps = { 1115+ { 1116+ /* We don't receive a signal if we haven't subscribed yet */ 1117+ .action = TEST_ACTION_EMIT_SIGNAL, 1118+ .u.signal = { 1119+ .sender = TEST_CONN_SERVICE, 1120+ .path = EXAMPLE_PATH, 1121+ .iface = EXAMPLE_INTERFACE, 1122+ .member = FOO_SIGNAL, 1123+ .received_by_conn = 0, 1124+ .received_by_proxy = 0 1125+ }, 1126+ }, 1127+ { 1128+ .action = TEST_ACTION_SUBSCRIBE, 1129+ .u.subscribe = { 1130+ .path = EXAMPLE_PATH, 1131+ .iface = EXAMPLE_INTERFACE, 1132+ }, 1133+ }, 1134+ { 1135+ /* Now it works */ 1136+ .action = TEST_ACTION_EMIT_SIGNAL, 1137+ .u.signal = { 1138+ .sender = TEST_CONN_SERVICE, 1139+ .path = EXAMPLE_PATH, 1140+ .iface = EXAMPLE_INTERFACE, 1141+ .member = FOO_SIGNAL, 1142+ .received_by_conn = 1, 1143+ /* The proxy can't be used in this case, because it needs 1144+ * a bus name to subscribe to */ 1145+ .received_by_proxy = 0 1146+ }, 1147+ }, 1148+ }, 1149+}; 1150+ 1151+static const TestPlan plan_broadcast_from_anyone = 1152+{ 1153+ .description = "A subscription with NULL sender accepts broadcast and unicast", 1154+ .steps = { 1155+ { 1156+ /* Subscriber wants to receive signals from anyone */ 1157+ .action = TEST_ACTION_SUBSCRIBE, 1158+ .u.subscribe = { 1159+ .path = EXAMPLE_PATH, 1160+ .iface = EXAMPLE_INTERFACE, 1161+ }, 1162+ }, 1163+ { 1164+ /* First service sends a broadcast */ 1165+ .action = TEST_ACTION_EMIT_SIGNAL, 1166+ .u.signal = { 1167+ .sender = TEST_CONN_SERVICE, 1168+ .path = EXAMPLE_PATH, 1169+ .iface = EXAMPLE_INTERFACE, 1170+ .member = FOO_SIGNAL, 1171+ .received_by_conn = 1, 1172+ .received_by_proxy = 0 1173+ }, 1174+ }, 1175+ { 1176+ /* Second service also sends a broadcast */ 1177+ .action = TEST_ACTION_EMIT_SIGNAL, 1178+ .u.signal = { 1179+ .sender = TEST_CONN_SERVICE2, 1180+ .path = EXAMPLE_PATH, 1181+ .iface = EXAMPLE_INTERFACE, 1182+ .member = FOO_SIGNAL, 1183+ .received_by_conn = 1, 1184+ .received_by_proxy = 0 1185+ }, 1186+ }, 1187+ { 1188+ /* First service sends a unicast signal */ 1189+ .action = TEST_ACTION_EMIT_SIGNAL, 1190+ .u.signal = { 1191+ .sender = TEST_CONN_SERVICE, 1192+ .unicast_to = TEST_CONN_SUBSCRIBER, 1193+ .path = EXAMPLE_PATH, 1194+ .iface = EXAMPLE_INTERFACE, 1195+ .member = FOO_SIGNAL, 1196+ .received_by_conn = 1, 1197+ .received_by_proxy = 0 1198+ }, 1199+ }, 1200+ { 1201+ /* Second service also sends a unicast signal */ 1202+ .action = TEST_ACTION_EMIT_SIGNAL, 1203+ .u.signal = { 1204+ .sender = TEST_CONN_SERVICE2, 1205+ .unicast_to = TEST_CONN_SUBSCRIBER, 1206+ .path = EXAMPLE_PATH, 1207+ .iface = EXAMPLE_INTERFACE, 1208+ .member = FOO_SIGNAL, 1209+ .received_by_conn = 1, 1210+ .received_by_proxy = 0 1211+ }, 1212+ }, 1213+ }, 1214+}; 1215+ 1216+static const TestPlan plan_match_twice = 1217+{ 1218+ .description = "A message matching more than one subscription is received " 1219+ "once per subscription", 1220+ .steps = { 1221+ { 1222+ .action = TEST_ACTION_SUBSCRIBE, 1223+ .u.subscribe = { 1224+ .unique_sender = TEST_CONN_SERVICE, 1225+ .path = EXAMPLE_PATH, 1226+ .iface = EXAMPLE_INTERFACE, 1227+ }, 1228+ }, 1229+ { 1230+ .action = TEST_ACTION_SUBSCRIBE, 1231+ .u.subscribe = { 1232+ .path = EXAMPLE_PATH, 1233+ }, 1234+ }, 1235+ { 1236+ .action = TEST_ACTION_SUBSCRIBE, 1237+ .u.subscribe = { 1238+ .iface = EXAMPLE_INTERFACE, 1239+ }, 1240+ }, 1241+ { 1242+ .action = TEST_ACTION_SUBSCRIBE, 1243+ .u.subscribe = { 1244+ .unique_sender = TEST_CONN_SERVICE, 1245+ .path = EXAMPLE_PATH, 1246+ .iface = EXAMPLE_INTERFACE, 1247+ }, 1248+ }, 1249+ { 1250+ .action = TEST_ACTION_EMIT_SIGNAL, 1251+ .u.signal = { 1252+ .sender = TEST_CONN_SERVICE, 1253+ .path = EXAMPLE_PATH, 1254+ .iface = EXAMPLE_INTERFACE, 1255+ .member = FOO_SIGNAL, 1256+ .received_by_conn = 4, 1257+ /* Only the first and last work with GDBusProxy */ 1258+ .received_by_proxy = 2 1259+ }, 1260+ }, 1261+ }, 1262+}; 1263+ 1264+static const TestPlan plan_limit_by_unique_name = 1265+{ 1266+ .description = "A subscription via a unique name only accepts messages " 1267+ "sent by that same unique name", 1268+ .steps = { 1269+ { 1270+ /* Subscriber wants to receive signals from service */ 1271+ .action = TEST_ACTION_SUBSCRIBE, 1272+ .u.subscribe = { 1273+ .unique_sender = TEST_CONN_SERVICE, 1274+ .path = EXAMPLE_PATH, 1275+ .iface = EXAMPLE_INTERFACE, 1276+ }, 1277+ }, 1278+ { 1279+ /* Attacker wants to trick subscriber into thinking that service 1280+ * sent a signal */ 1281+ .action = TEST_ACTION_EMIT_SIGNAL, 1282+ .u.signal = { 1283+ .sender = TEST_CONN_ATTACKER, 1284+ .path = EXAMPLE_PATH, 1285+ .iface = EXAMPLE_INTERFACE, 1286+ .member = FOO_SIGNAL, 1287+ .received_by_conn = 0, 1288+ .received_by_proxy = 0 1289+ }, 1290+ }, 1291+ { 1292+ /* Attacker tries harder, by sending a signal unicast directly to 1293+ * the subscriber */ 1294+ .action = TEST_ACTION_EMIT_SIGNAL, 1295+ .u.signal = { 1296+ .sender = TEST_CONN_ATTACKER, 1297+ .unicast_to = TEST_CONN_SUBSCRIBER, 1298+ .path = EXAMPLE_PATH, 1299+ .iface = EXAMPLE_INTERFACE, 1300+ .member = FOO_SIGNAL, 1301+ .received_by_conn = 0, 1302+ .received_by_proxy = 0 1303+ }, 1304+ }, 1305+ { 1306+ /* When the real service sends a signal, it should still get through */ 1307+ .action = TEST_ACTION_EMIT_SIGNAL, 1308+ .u.signal = { 1309+ .sender = TEST_CONN_SERVICE, 1310+ .path = EXAMPLE_PATH, 1311+ .iface = EXAMPLE_INTERFACE, 1312+ .member = FOO_SIGNAL, 1313+ .received_by_conn = 1, 1314+ .received_by_proxy = 1 1315+ }, 1316+ }, 1317+ }, 1318+}; 1319+ 1320+static const TestPlan plan_nonexistent_unique_name = 1321+{ 1322+ .description = "A subscription via a unique name that doesn't exist " 1323+ "accepts no messages", 1324+ .steps = { 1325+ { 1326+ /* Subscriber wants to receive signals from service */ 1327+ .action = TEST_ACTION_SUBSCRIBE, 1328+ .u.subscribe = { 1329+ /* This relies on the implementation detail that the dbus-daemon 1330+ * (and presumably other bus implementations) never actually generates 1331+ * a unique name in this format */ 1332+ .string_sender = ":0.this.had.better.not.exist", 1333+ .path = EXAMPLE_PATH, 1334+ .iface = EXAMPLE_INTERFACE, 1335+ }, 1336+ }, 1337+ { 1338+ /* Attacker wants to trick subscriber into thinking that service 1339+ * sent a signal */ 1340+ .action = TEST_ACTION_EMIT_SIGNAL, 1341+ .u.signal = { 1342+ .sender = TEST_CONN_ATTACKER, 1343+ .path = EXAMPLE_PATH, 1344+ .iface = EXAMPLE_INTERFACE, 1345+ .member = FOO_SIGNAL, 1346+ .received_by_conn = 0, 1347+ .received_by_proxy = 0 1348+ }, 1349+ }, 1350+ { 1351+ /* Attacker tries harder, by sending a signal unicast directly to 1352+ * the subscriber */ 1353+ .action = TEST_ACTION_EMIT_SIGNAL, 1354+ .u.signal = { 1355+ .sender = TEST_CONN_ATTACKER, 1356+ .unicast_to = TEST_CONN_SUBSCRIBER, 1357+ .path = EXAMPLE_PATH, 1358+ .iface = EXAMPLE_INTERFACE, 1359+ .member = FOO_SIGNAL, 1360+ .received_by_conn = 0, 1361+ .received_by_proxy = 0 1362+ }, 1363+ }, 1364+ }, 1365+}; 1366+ 1367+static const TestPlan plan_limit_by_well_known_name = 1368+{ 1369+ .description = "A subscription via a well-known name only accepts messages " 1370+ "sent by the owner of that well-known name", 1371+ .steps = { 1372+ { 1373+ /* Service already owns one name */ 1374+ .action = TEST_ACTION_OWN_NAME, 1375+ .u.own_name = { 1376+ .name = ALREADY_OWNED_NAME, 1377+ .owner = TEST_CONN_SERVICE 1378+ }, 1379+ }, 1380+ { 1381+ /* Subscriber wants to receive signals from service */ 1382+ .action = TEST_ACTION_SUBSCRIBE, 1383+ .u.subscribe = { 1384+ .string_sender = ALREADY_OWNED_NAME, 1385+ .path = EXAMPLE_PATH, 1386+ .iface = EXAMPLE_INTERFACE, 1387+ }, 1388+ }, 1389+ { 1390+ /* Subscriber wants to receive signals from service by another name */ 1391+ .action = TEST_ACTION_SUBSCRIBE, 1392+ .u.subscribe = { 1393+ .string_sender = OWNED_LATER_NAME, 1394+ .path = EXAMPLE_PATH, 1395+ .iface = EXAMPLE_INTERFACE, 1396+ }, 1397+ }, 1398+ { 1399+ /* Attacker wants to trick subscriber into thinking that service 1400+ * sent a signal */ 1401+ .action = TEST_ACTION_EMIT_SIGNAL, 1402+ .u.signal = { 1403+ .sender = TEST_CONN_ATTACKER, 1404+ .path = EXAMPLE_PATH, 1405+ .iface = EXAMPLE_INTERFACE, 1406+ .member = FOO_SIGNAL, 1407+ .received_by_conn = 0, 1408+ .received_by_proxy = 0 1409+ }, 1410+ }, 1411+ { 1412+ /* Attacker tries harder, by sending a signal unicast directly to 1413+ * the subscriber */ 1414+ .action = TEST_ACTION_EMIT_SIGNAL, 1415+ .u.signal = { 1416+ .sender = TEST_CONN_ATTACKER, 1417+ .unicast_to = TEST_CONN_SUBSCRIBER, 1418+ .path = EXAMPLE_PATH, 1419+ .iface = EXAMPLE_INTERFACE, 1420+ .member = FOO_SIGNAL, 1421+ .received_by_conn = 0, 1422+ .received_by_proxy = 0 1423+ }, 1424+ }, 1425+ { 1426+ /* When the service sends a signal with the name it already owns, 1427+ * it should get through */ 1428+ .action = TEST_ACTION_EMIT_SIGNAL, 1429+ .u.signal = { 1430+ .sender = TEST_CONN_SERVICE, 1431+ .path = EXAMPLE_PATH, 1432+ .iface = EXAMPLE_INTERFACE, 1433+ .member = FOO_SIGNAL, 1434+ .received_by_conn = 1, 1435+ .received_by_proxy = 1 1436+ }, 1437+ }, 1438+ { 1439+ /* Service claims another name */ 1440+ .action = TEST_ACTION_OWN_NAME, 1441+ .u.own_name = { 1442+ .name = OWNED_LATER_NAME, 1443+ .owner = TEST_CONN_SERVICE 1444+ }, 1445+ }, 1446+ { 1447+ /* Now the subscriber gets this signal twice, once for each 1448+ * subscription; and similarly each of the two proxies gets this 1449+ * signal twice */ 1450+ .action = TEST_ACTION_EMIT_SIGNAL, 1451+ .u.signal = { 1452+ .sender = TEST_CONN_SERVICE, 1453+ .path = EXAMPLE_PATH, 1454+ .iface = EXAMPLE_INTERFACE, 1455+ .member = FOO_SIGNAL, 1456+ .received_by_conn = 2, 1457+ .received_by_proxy = 2 1458+ }, 1459+ }, 1460+ }, 1461+}; 1462+ 1463+static const TestPlan plan_unsubscribe_immediately = 1464+{ 1465+ .description = "Unsubscribing before GetNameOwner can return doesn't result in a crash", 1466+ .steps = { 1467+ { 1468+ /* Service already owns one name */ 1469+ .action = TEST_ACTION_OWN_NAME, 1470+ .u.own_name = { 1471+ .name = ALREADY_OWNED_NAME, 1472+ .owner = TEST_CONN_SERVICE 1473+ }, 1474+ }, 1475+ { 1476+ .action = TEST_ACTION_SUBSCRIBE, 1477+ .u.subscribe = { 1478+ .string_sender = ALREADY_OWNED_NAME, 1479+ .path = EXAMPLE_PATH, 1480+ .iface = EXAMPLE_INTERFACE, 1481+ .unsubscribe_immediately = TRUE 1482+ }, 1483+ }, 1484+ { 1485+ .action = TEST_ACTION_EMIT_SIGNAL, 1486+ .u.signal = { 1487+ .sender = TEST_CONN_SERVICE, 1488+ .path = EXAMPLE_PATH, 1489+ .iface = EXAMPLE_INTERFACE, 1490+ .member = FOO_SIGNAL, 1491+ .received_by_conn = 0, 1492+ /* The proxy can't unsubscribe, except by destroying the proxy 1493+ * completely, which we don't currently implement in this test */ 1494+ .received_by_proxy = 1 1495+ }, 1496+ }, 1497+ }, 1498+}; 1499+ 1500+static const TestPlan plan_limit_to_message_bus = 1501+{ 1502+ .description = "A subscription to the message bus only accepts messages " 1503+ "from the message bus", 1504+ .steps = { 1505+ { 1506+ /* Subscriber wants to receive signals from the message bus itself */ 1507+ .action = TEST_ACTION_SUBSCRIBE, 1508+ .u.subscribe = { 1509+ .string_sender = DBUS_SERVICE_DBUS, 1510+ .path = DBUS_PATH_DBUS, 1511+ .iface = DBUS_INTERFACE_DBUS, 1512+ }, 1513+ }, 1514+ { 1515+ /* Attacker wants to trick subscriber into thinking that the message 1516+ * bus sent a signal */ 1517+ .action = TEST_ACTION_EMIT_SIGNAL, 1518+ .u.signal = { 1519+ .sender = TEST_CONN_ATTACKER, 1520+ .path = DBUS_PATH_DBUS, 1521+ .iface = DBUS_INTERFACE_DBUS, 1522+ .member = NAME_OWNER_CHANGED, 1523+ .arg0 = "would I lie to you?", 1524+ .received_by_conn = 0, 1525+ .received_by_proxy = 0 1526+ }, 1527+ }, 1528+ { 1529+ /* Attacker tries harder, by sending a signal unicast directly to 1530+ * the subscriber, and using more realistic arguments */ 1531+ .action = TEST_ACTION_EMIT_SIGNAL, 1532+ .u.signal = { 1533+ .unicast_to = TEST_CONN_SUBSCRIBER, 1534+ .sender = TEST_CONN_ATTACKER, 1535+ .path = DBUS_PATH_DBUS, 1536+ .iface = DBUS_INTERFACE_DBUS, 1537+ .member = NAME_OWNER_CHANGED, 1538+ .args = "('com.example.Name', '', ':1.12')", 1539+ .received_by_conn = 0, 1540+ .received_by_proxy = 0 1541+ }, 1542+ }, 1543+ { 1544+ /* When the message bus sends a signal (in this case triggered by 1545+ * owning a name), it should still get through */ 1546+ .action = TEST_ACTION_OWN_NAME, 1547+ .u.own_name = { 1548+ .name = OWNED_LATER_NAME, 1549+ .owner = TEST_CONN_SERVICE, 1550+ .received_by_conn = 1, 1551+ .received_by_proxy = 1 1552+ }, 1553+ }, 1554+ }, 1555+}; 1556+ 1557+typedef struct 1558+{ 1559+ const TestPlan *plan; 1560+ SubscriptionMode mode; 1561+ GError *error; 1562+ /* (element-type ReceivedMessage) */ 1563+ GPtrArray *received; 1564+ /* conns[TEST_CONN_NONE] is unused and remains NULL */ 1565+ GDBusConnection *conns[NUM_TEST_CONNS]; 1566+ /* Proxies on conns[TEST_CONN_SUBSCRIBER] */ 1567+ GPtrArray *proxies; 1568+ /* unique_names[TEST_CONN_NONE] is unused and remains NULL */ 1569+ const char *unique_names[NUM_TEST_CONNS]; 1570+ /* finished[TEST_CONN_NONE] is unused and remains FALSE */ 1571+ gboolean finished[NUM_TEST_CONNS]; 1572+ /* Remains 0 for any step that is not a subscription */ 1573+ guint subscriptions[MAX_TEST_STEPS]; 1574+ /* Number of times the signal from step n was received */ 1575+ guint received_by_conn[MAX_TEST_STEPS]; 1576+ /* Number of times the signal from step n was received */ 1577+ guint received_by_proxy[MAX_TEST_STEPS]; 1578+ guint finished_subscription; 1579+} Fixture; 1580+ 1581+/* Wait for asynchronous messages from @conn to have been processed 1582+ * by the message bus, as a sequence point so that we can make 1583+ * "happens before" and "happens after" assertions relative to this. 1584+ * The easiest way to achieve this is to call a message bus method that has 1585+ * no arguments and wait for it to return: because the message bus processes 1586+ * messages in-order, anything we sent before this must have been processed 1587+ * by the time this call arrives. */ 1588+static void 1589+connection_wait_for_bus (GDBusConnection *conn) 1590+{ 1591+ GError *error = NULL; 1592+ GVariant *call_result; 1593+ 1594+ call_result = g_dbus_connection_call_sync (conn, 1595+ DBUS_SERVICE_DBUS, 1596+ DBUS_PATH_DBUS, 1597+ DBUS_INTERFACE_DBUS, 1598+ "GetId", 1599+ NULL, /* arguments */ 1600+ NULL, /* result type */ 1601+ G_DBUS_CALL_FLAGS_NONE, 1602+ -1, 1603+ NULL, 1604+ &error); 1605+ g_assert_no_error (error); 1606+ g_assert_nonnull (call_result); 1607+ g_variant_unref (call_result); 1608+} 1609+ 1610+/* 1611+ * Called when the subscriber receives a message from any connection 1612+ * announcing that it has emitted all the signals that it plans to emit. 1613+ */ 1614+static void 1615+subscriber_finished_cb (GDBusConnection *conn, 1616+ const char *sender_name, 1617+ const char *path, 1618+ const char *iface, 1619+ const char *member, 1620+ GVariant *parameters, 1621+ void *user_data) 1622+{ 1623+ Fixture *f = user_data; 1624+ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER]; 1625+ guint i; 1626+ 1627+ g_assert_true (conn == subscriber); 1628+ 1629+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) 1630+ { 1631+ if (g_str_equal (sender_name, f->unique_names[i])) 1632+ { 1633+ g_assert_false (f->finished[i]); 1634+ f->finished[i] = TRUE; 1635+ 1636+ g_test_message ("Received Finished signal from %s %s", 1637+ test_conn_descriptions[i], sender_name); 1638+ return; 1639+ } 1640+ } 1641+ 1642+ g_error ("Received Finished signal from unknown sender %s", sender_name); 1643+} 1644+ 1645+/* 1646+ * Called when we receive a signal, either via the GDBusProxy (proxy != NULL) 1647+ * or via the GDBusConnection (proxy == NULL). 1648+ */ 1649+static void 1650+fixture_received_signal (Fixture *f, 1651+ GDBusProxy *proxy, 1652+ const char *sender_name, 1653+ const char *path, 1654+ const char *iface, 1655+ const char *member, 1656+ GVariant *parameters) 1657+{ 1658+ guint i; 1659+ ReceivedMessage *received; 1660+ 1661+ /* Ignore the Finished signal if it matches a wildcard subscription */ 1662+ if (g_str_equal (member, FINISHED_SIGNAL)) 1663+ return; 1664+ 1665+ received = g_new0 (ReceivedMessage, 1); 1666+ 1667+ if (proxy != NULL) 1668+ received->received_by_proxy = g_object_ref (proxy); 1669+ else 1670+ received->received_by_proxy = NULL; 1671+ 1672+ received->path = g_strdup (path); 1673+ received->iface = g_strdup (iface); 1674+ received->member = g_strdup (member); 1675+ received->parameters = g_variant_ref (parameters); 1676+ 1677+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) 1678+ { 1679+ if (g_str_equal (sender_name, f->unique_names[i])) 1680+ { 1681+ received->sender = i; 1682+ g_assert_false (f->finished[i]); 1683+ break; 1684+ } 1685+ } 1686+ 1687+ if (g_str_equal (sender_name, DBUS_SERVICE_DBUS)) 1688+ { 1689+ g_test_message ("Signal received from message bus %s", 1690+ sender_name); 1691+ } 1692+ else 1693+ { 1694+ g_test_message ("Signal received from %s %s", 1695+ test_conn_descriptions[received->sender], 1696+ sender_name); 1697+ g_assert_cmpint (received->sender, !=, TEST_CONN_NONE); 1698+ } 1699+ 1700+ g_test_message ("Signal received from %s %s via %s", 1701+ test_conn_descriptions[received->sender], 1702+ sender_name, 1703+ proxy != NULL ? "proxy" : "connection"); 1704+ g_test_message ("\tPath: %s", path); 1705+ g_test_message ("\tInterface: %s", iface); 1706+ g_test_message ("\tMember: %s", member); 1707+ 1708+ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(su)"))) 1709+ { 1710+ g_variant_get (parameters, "(su)", &received->arg0, &received->step); 1711+ g_test_message ("\tString argument 0: %s", received->arg0); 1712+ g_test_message ("\tSent in step: %u", received->step); 1713+ } 1714+ else if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)"))) 1715+ { 1716+ g_variant_get (parameters, "(uu)", NULL, &received->step); 1717+ g_test_message ("\tArgument 0: (not a string)"); 1718+ g_test_message ("\tSent in step: %u", received->step); 1719+ } 1720+ else if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) 1721+ { 1722+ const char *name; 1723+ const char *old_owner; 1724+ const char *new_owner; 1725+ 1726+ /* The only signal of this signature that we legitimately receive 1727+ * during this test is NameOwnerChanged, so just assert that it 1728+ * is from the message bus and can be matched to a plausible step. 1729+ * (This is less thorough than the above, and will not work if we 1730+ * add a test scenario where a name's ownership is repeatedly 1731+ * changed while watching NameOwnerChanged - so don't do that.) */ 1732+ g_assert_cmpstr (sender_name, ==, DBUS_SERVICE_DBUS); 1733+ g_assert_cmpstr (path, ==, DBUS_PATH_DBUS); 1734+ g_assert_cmpstr (iface, ==, DBUS_INTERFACE_DBUS); 1735+ g_assert_cmpstr (member, ==, NAME_OWNER_CHANGED); 1736+ 1737+ g_variant_get (parameters, "(&s&s&s)", &name, &old_owner, &new_owner); 1738+ 1739+ for (i = 0; i < G_N_ELEMENTS (f->plan->steps); i++) 1740+ { 1741+ const TestStep *step = &f->plan->steps[i]; 1742+ 1743+ if (step->action == TEST_ACTION_OWN_NAME) 1744+ { 1745+ const TestOwnName *own_name = &step->u.own_name; 1746+ 1747+ if (g_str_equal (name, own_name->name) 1748+ && g_str_equal (new_owner, f->unique_names[own_name->owner]) 1749+ && own_name->received_by_conn > 0) 1750+ { 1751+ received->step = i; 1752+ break; 1753+ } 1754+ } 1755+ 1756+ if (i >= G_N_ELEMENTS (f->plan->steps)) 1757+ g_error ("Could not match message to a test step"); 1758+ } 1759+ } 1760+ else 1761+ { 1762+ g_error ("Unexpected message received"); 1763+ } 1764+ 1765+ g_ptr_array_add (f->received, g_steal_pointer (&received)); 1766+} 1767+ 1768+static void 1769+proxy_signal_cb (GDBusProxy *proxy, 1770+ const char *sender_name, 1771+ const char *member, 1772+ GVariant *parameters, 1773+ void *user_data) 1774+{ 1775+ Fixture *f = user_data; 1776+ 1777+ fixture_received_signal (f, proxy, sender_name, 1778+ g_dbus_proxy_get_object_path (proxy), 1779+ g_dbus_proxy_get_interface_name (proxy), 1780+ member, parameters); 1781+} 1782+ 1783+static void 1784+subscribed_signal_cb (GDBusConnection *conn, 1785+ const char *sender_name, 1786+ const char *path, 1787+ const char *iface, 1788+ const char *member, 1789+ GVariant *parameters, 1790+ void *user_data) 1791+{ 1792+ Fixture *f = user_data; 1793+ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER]; 1794+ 1795+ g_assert_true (conn == subscriber); 1796+ 1797+ fixture_received_signal (f, NULL, sender_name, path, iface, member, parameters); 1798+} 1799+ 1800+static void 1801+fixture_subscribe (Fixture *f, 1802+ const TestSubscribe *subscribe, 1803+ guint step_number) 1804+{ 1805+ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER]; 1806+ const char *sender; 1807+ 1808+ if (subscribe->string_sender != NULL) 1809+ { 1810+ sender = subscribe->string_sender; 1811+ g_test_message ("\tSender: %s", sender); 1812+ } 1813+ else if (subscribe->unique_sender != TEST_CONN_NONE) 1814+ { 1815+ sender = f->unique_names[subscribe->unique_sender]; 1816+ g_test_message ("\tSender: %s %s", 1817+ test_conn_descriptions[subscribe->unique_sender], 1818+ sender); 1819+ } 1820+ else 1821+ { 1822+ sender = NULL; 1823+ g_test_message ("\tSender: (any)"); 1824+ } 1825+ 1826+ g_test_message ("\tPath: %s", nonnull (subscribe->path, "(any)")); 1827+ g_test_message ("\tInterface: %s", 1828+ nonnull (subscribe->iface, "(any)")); 1829+ g_test_message ("\tMember: %s", 1830+ nonnull (subscribe->member, "(any)")); 1831+ g_test_message ("\tString argument 0: %s", 1832+ nonnull (subscribe->arg0, "(any)")); 1833+ g_test_message ("\tFlags: %x", subscribe->flags); 1834+ 1835+ if (f->mode != SUBSCRIPTION_MODE_PROXY) 1836+ { 1837+ /* CONN or PARALLEL */ 1838+ guint id; 1839+ 1840+ g_test_message ("\tSubscribing via connection"); 1841+ id = g_dbus_connection_signal_subscribe (subscriber, 1842+ sender, 1843+ subscribe->iface, 1844+ subscribe->member, 1845+ subscribe->path, 1846+ subscribe->arg0, 1847+ subscribe->flags, 1848+ subscribed_signal_cb, 1849+ f, NULL); 1850+ 1851+ g_assert_cmpuint (id, !=, 0); 1852+ 1853+ if (subscribe->unsubscribe_immediately) 1854+ { 1855+ g_test_message ("\tImmediately unsubscribing"); 1856+ g_dbus_connection_signal_unsubscribe (subscriber, id); 1857+ } 1858+ else 1859+ { 1860+ f->subscriptions[step_number] = id; 1861+ } 1862+ } 1863+ 1864+ if (f->mode != SUBSCRIPTION_MODE_CONN) 1865+ { 1866+ /* PROXY or PARALLEL */ 1867+ 1868+ if (sender == NULL) 1869+ { 1870+ g_test_message ("\tCannot subscribe via proxy: no bus name"); 1871+ } 1872+ else if (subscribe->path == NULL) 1873+ { 1874+ g_test_message ("\tCannot subscribe via proxy: no path"); 1875+ } 1876+ else if (subscribe->iface == NULL) 1877+ { 1878+ g_test_message ("\tCannot subscribe via proxy: no interface"); 1879+ } 1880+ else 1881+ { 1882+ GDBusProxy *proxy; 1883+ 1884+ g_test_message ("\tSubscribing via proxy"); 1885+ proxy = g_dbus_proxy_new_sync (subscriber, 1886+ (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES 1887+ | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START), 1888+ NULL, /* GDBusInterfaceInfo */ 1889+ sender, 1890+ subscribe->path, 1891+ subscribe->iface, 1892+ NULL, /* GCancellable */ 1893+ &f->error); 1894+ g_assert_no_error (f->error); 1895+ g_assert_nonnull (proxy); 1896+ g_signal_connect (proxy, "g-signal", G_CALLBACK (proxy_signal_cb), f); 1897+ g_ptr_array_add (f->proxies, g_steal_pointer (&proxy)); 1898+ } 1899+ } 1900+ 1901+ /* As in setup(), we need to wait for AddMatch to happen. */ 1902+ g_test_message ("Waiting for AddMatch to be processed"); 1903+ connection_wait_for_bus (subscriber); 1904+} 1905+ 1906+static void 1907+fixture_emit_signal (Fixture *f, 1908+ const TestEmitSignal *signal, 1909+ guint step_number) 1910+{ 1911+ GVariant *body; 1912+ const char *destination; 1913+ gboolean ok; 1914+ 1915+ g_test_message ("\tSender: %s", 1916+ test_conn_descriptions[signal->sender]); 1917+ 1918+ if (signal->unicast_to != TEST_CONN_NONE) 1919+ { 1920+ destination = f->unique_names[signal->unicast_to]; 1921+ g_test_message ("\tDestination: %s %s", 1922+ test_conn_descriptions[signal->unicast_to], 1923+ destination); 1924+ } 1925+ else 1926+ { 1927+ destination = NULL; 1928+ g_test_message ("\tDestination: (broadcast)"); 1929+ } 1930+ 1931+ g_assert_nonnull (signal->path); 1932+ g_test_message ("\tPath: %s", signal->path); 1933+ g_assert_nonnull (signal->iface); 1934+ g_test_message ("\tInterface: %s", signal->iface); 1935+ g_assert_nonnull (signal->member); 1936+ g_test_message ("\tMember: %s", signal->member); 1937+ 1938+ /* If arg0 is non-NULL, put it in the message's argument 0. 1939+ * Otherwise put something that will not match any arg0. 1940+ * Either way, put the sequence number in argument 1 so we can 1941+ * correlate sent messages with received messages later. */ 1942+ if (signal->args != NULL) 1943+ { 1944+ /* floating */ 1945+ body = g_variant_new_parsed (signal->args); 1946+ g_assert_nonnull (body); 1947+ } 1948+ else if (signal->arg0 != NULL) 1949+ { 1950+ g_test_message ("\tString argument 0: %s", signal->arg0); 1951+ body = g_variant_new ("(su)", signal->arg0, (guint32) step_number); 1952+ } 1953+ else 1954+ { 1955+ g_test_message ("\tArgument 0: (not a string)"); 1956+ body = g_variant_new ("(uu)", (guint32) 0, (guint32) step_number); 1957+ } 1958+ 1959+ ok = g_dbus_connection_emit_signal (f->conns[signal->sender], 1960+ destination, 1961+ signal->path, 1962+ signal->iface, 1963+ signal->member, 1964+ /* steals floating reference */ 1965+ g_steal_pointer (&body), 1966+ &f->error); 1967+ g_assert_no_error (f->error); 1968+ g_assert_true (ok); 1969+ 1970+ /* Emitting the signal is asynchronous, so if we want subsequent steps 1971+ * to be guaranteed to happen after the signal from the message bus's 1972+ * perspective, we have to do a round-trip to the message bus to sync up. */ 1973+ g_test_message ("Waiting for signal to reach message bus"); 1974+ connection_wait_for_bus (f->conns[signal->sender]); 1975+} 1976+ 1977+static void 1978+fixture_own_name (Fixture *f, 1979+ const TestOwnName *own_name) 1980+{ 1981+ GVariant *call_result; 1982+ guint32 flags; 1983+ guint32 result_code; 1984+ 1985+ g_test_message ("\tName: %s", own_name->name); 1986+ g_test_message ("\tOwner: %s", 1987+ test_conn_descriptions[own_name->owner]); 1988+ 1989+ /* For simplicity, we do this via a direct bus call rather than 1990+ * using g_bus_own_name_on_connection(). The flags in 1991+ * GBusNameOwnerFlags are numerically equal to those in the 1992+ * D-Bus wire protocol. */ 1993+ flags = G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE; 1994+ call_result = g_dbus_connection_call_sync (f->conns[own_name->owner], 1995+ DBUS_SERVICE_DBUS, 1996+ DBUS_PATH_DBUS, 1997+ DBUS_INTERFACE_DBUS, 1998+ "RequestName", 1999+ g_variant_new ("(su)", 2000+ own_name->name, 2001+ flags), 2002+ G_VARIANT_TYPE ("(u)"), 2003+ G_DBUS_CALL_FLAGS_NONE, 2004+ -1, 2005+ NULL, 2006+ &f->error); 2007+ g_assert_no_error (f->error); 2008+ g_assert_nonnull (call_result); 2009+ g_variant_get (call_result, "(u)", &result_code); 2010+ g_assert_cmpuint (result_code, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); 2011+ g_variant_unref (call_result); 2012+} 2013+ 2014+static void 2015+fixture_run_plan (Fixture *f, 2016+ const TestPlan *plan, 2017+ SubscriptionMode mode) 2018+{ 2019+ guint i; 2020+ 2021+ G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->subscriptions)); 2022+ G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->received_by_conn)); 2023+ G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->received_by_proxy)); 2024+ 2025+ f->mode = mode; 2026+ f->plan = plan; 2027+ 2028+ g_test_summary (plan->description); 2029+ 2030+ for (i = 0; i < G_N_ELEMENTS (plan->steps); i++) 2031+ { 2032+ const TestStep *step = &plan->steps[i]; 2033+ 2034+ switch (step->action) 2035+ { 2036+ case TEST_ACTION_SUBSCRIBE: 2037+ g_test_message ("Step %u: adding subscription", i); 2038+ fixture_subscribe (f, &step->u.subscribe, i); 2039+ break; 2040+ 2041+ case TEST_ACTION_EMIT_SIGNAL: 2042+ g_test_message ("Step %u: emitting signal", i); 2043+ fixture_emit_signal (f, &step->u.signal, i); 2044+ break; 2045+ 2046+ case TEST_ACTION_OWN_NAME: 2047+ g_test_message ("Step %u: claiming bus name", i); 2048+ fixture_own_name (f, &step->u.own_name); 2049+ break; 2050+ 2051+ case TEST_ACTION_NONE: 2052+ /* Padding to fill the rest of the array, do nothing */ 2053+ break; 2054+ 2055+ default: 2056+ g_return_if_reached (); 2057+ } 2058+ } 2059+ 2060+ /* Now that we have done everything we wanted to do, emit Finished 2061+ * from each connection. */ 2062+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) 2063+ { 2064+ gboolean ok; 2065+ 2066+ ok = g_dbus_connection_emit_signal (f->conns[i], 2067+ NULL, 2068+ FINISHED_PATH, 2069+ FINISHED_INTERFACE, 2070+ FINISHED_SIGNAL, 2071+ NULL, 2072+ &f->error); 2073+ g_assert_no_error (f->error); 2074+ g_assert_true (ok); 2075+ } 2076+ 2077+ /* Wait until we have seen the Finished signal from each sender */ 2078+ while (TRUE) 2079+ { 2080+ gboolean all_finished = TRUE; 2081+ 2082+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) 2083+ all_finished = all_finished && f->finished[i]; 2084+ 2085+ if (all_finished) 2086+ break; 2087+ 2088+ g_main_context_iteration (NULL, TRUE); 2089+ } 2090+ 2091+ /* Assert that the correct things happened before each Finished signal */ 2092+ for (i = 0; i < f->received->len; i++) 2093+ { 2094+ const ReceivedMessage *received = g_ptr_array_index (f->received, i); 2095+ 2096+ g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_conn)); 2097+ g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_proxy)); 2098+ 2099+ if (received->received_by_proxy != NULL) 2100+ f->received_by_proxy[received->step] += 1; 2101+ else 2102+ f->received_by_conn[received->step] += 1; 2103+ } 2104+ 2105+ for (i = 0; i < G_N_ELEMENTS (plan->steps); i++) 2106+ { 2107+ const TestStep *step = &plan->steps[i]; 2108+ 2109+ if (step->action == TEST_ACTION_EMIT_SIGNAL) 2110+ { 2111+ const TestEmitSignal *signal = &plan->steps[i].u.signal; 2112+ 2113+ if (mode != SUBSCRIPTION_MODE_PROXY) 2114+ { 2115+ g_test_message ("Signal from step %u was received %u times by " 2116+ "GDBusConnection, expected %u", 2117+ i, f->received_by_conn[i], signal->received_by_conn); 2118+ g_assert_cmpuint (f->received_by_conn[i], ==, signal->received_by_conn); 2119+ } 2120+ else 2121+ { 2122+ g_assert_cmpuint (f->received_by_conn[i], ==, 0); 2123+ } 2124+ 2125+ if (mode != SUBSCRIPTION_MODE_CONN) 2126+ { 2127+ g_test_message ("Signal from step %u was received %u times by " 2128+ "GDBusProxy, expected %u", 2129+ i, f->received_by_proxy[i], signal->received_by_proxy); 2130+ g_assert_cmpuint (f->received_by_proxy[i], ==, signal->received_by_proxy); 2131+ } 2132+ else 2133+ { 2134+ g_assert_cmpuint (f->received_by_proxy[i], ==, 0); 2135+ } 2136+ } 2137+ else if (step->action == TEST_ACTION_OWN_NAME) 2138+ { 2139+ const TestOwnName *own_name = &plan->steps[i].u.own_name; 2140+ 2141+ if (mode != SUBSCRIPTION_MODE_PROXY) 2142+ { 2143+ g_test_message ("NameOwnerChanged from step %u was received %u " 2144+ "times by GDBusConnection, expected %u", 2145+ i, f->received_by_conn[i], own_name->received_by_conn); 2146+ g_assert_cmpuint (f->received_by_conn[i], ==, own_name->received_by_conn); 2147+ } 2148+ else 2149+ { 2150+ g_assert_cmpuint (f->received_by_conn[i], ==, 0); 2151+ } 2152+ 2153+ if (mode != SUBSCRIPTION_MODE_CONN) 2154+ { 2155+ g_test_message ("NameOwnerChanged from step %u was received %u " 2156+ "times by GDBusProxy, expected %u", 2157+ i, f->received_by_proxy[i], own_name->received_by_proxy); 2158+ g_assert_cmpuint (f->received_by_proxy[i], ==, own_name->received_by_proxy); 2159+ } 2160+ else 2161+ { 2162+ g_assert_cmpuint (f->received_by_proxy[i], ==, 0); 2163+ } 2164+ } 2165+ } 2166+} 2167+ 2168+static void 2169+setup (Fixture *f, 2170+ G_GNUC_UNUSED const void *context) 2171+{ 2172+ GDBusConnection *subscriber; 2173+ guint i; 2174+ 2175+ session_bus_up (); 2176+ 2177+ f->proxies = g_ptr_array_new_full (MAX_TEST_STEPS, g_object_unref); 2178+ f->received = g_ptr_array_new_full (MAX_TEST_STEPS, 2179+ (GDestroyNotify) received_message_free); 2180+ 2181+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) 2182+ { 2183+ f->conns[i] = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &f->error); 2184+ g_assert_no_error (f->error); 2185+ g_assert_nonnull (f->conns[i]); 2186+ 2187+ f->unique_names[i] = g_dbus_connection_get_unique_name (f->conns[i]); 2188+ g_assert_nonnull (f->unique_names[i]); 2189+ g_test_message ("%s is %s", 2190+ test_conn_descriptions[i], 2191+ f->unique_names[i]); 2192+ } 2193+ 2194+ subscriber = f->conns[TEST_CONN_SUBSCRIBER]; 2195+ 2196+ /* Used to wait for all connections to finish sending whatever they 2197+ * wanted to send */ 2198+ f->finished_subscription = g_dbus_connection_signal_subscribe (subscriber, 2199+ NULL, 2200+ FINISHED_INTERFACE, 2201+ FINISHED_SIGNAL, 2202+ FINISHED_PATH, 2203+ NULL, 2204+ G_DBUS_SIGNAL_FLAGS_NONE, 2205+ subscriber_finished_cb, 2206+ f, NULL); 2207+ /* AddMatch is sent asynchronously, so we don't know how 2208+ * soon it will be processed. Before emitting signals, we 2209+ * need to wait for the message bus to get as far as processing 2210+ * AddMatch. */ 2211+ g_test_message ("Waiting for AddMatch to be processed"); 2212+ connection_wait_for_bus (subscriber); 2213+} 2214+ 2215+static void 2216+test_conn_subscribe (Fixture *f, 2217+ const void *context) 2218+{ 2219+ fixture_run_plan (f, context, SUBSCRIPTION_MODE_CONN); 2220+} 2221+ 2222+static void 2223+test_proxy_subscribe (Fixture *f, 2224+ const void *context) 2225+{ 2226+ fixture_run_plan (f, context, SUBSCRIPTION_MODE_PROXY); 2227+} 2228+ 2229+static void 2230+test_parallel_subscribe (Fixture *f, 2231+ const void *context) 2232+{ 2233+ fixture_run_plan (f, context, SUBSCRIPTION_MODE_PARALLEL); 2234+} 2235+ 2236+static void 2237+teardown (Fixture *f, 2238+ G_GNUC_UNUSED const void *context) 2239+{ 2240+ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER]; 2241+ guint i; 2242+ 2243+ g_ptr_array_unref (f->proxies); 2244+ 2245+ if (f->finished_subscription != 0) 2246+ g_dbus_connection_signal_unsubscribe (subscriber, f->finished_subscription); 2247+ 2248+ for (i = 0; i < G_N_ELEMENTS (f->subscriptions); i++) 2249+ { 2250+ if (f->subscriptions[i] != 0) 2251+ g_dbus_connection_signal_unsubscribe (subscriber, f->subscriptions[i]); 2252+ } 2253+ 2254+ g_ptr_array_unref (f->received); 2255+ 2256+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) 2257+ g_clear_object (&f->conns[i]); 2258+ 2259+ g_clear_error (&f->error); 2260+ 2261+ session_bus_down (); 2262+} 2263+ 2264+int 2265+main (int argc, 2266+ char *argv[]) 2267+{ 2268+ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); 2269+ 2270+ g_test_dbus_unset (); 2271+ 2272+#define ADD_SUBSCRIBE_TEST(name) \ 2273+ do { \ 2274+ g_test_add ("/gdbus/subscribe/conn/" #name, \ 2275+ Fixture, &plan_ ## name, \ 2276+ setup, test_conn_subscribe, teardown); \ 2277+ g_test_add ("/gdbus/subscribe/proxy/" #name, \ 2278+ Fixture, &plan_ ## name, \ 2279+ setup, test_proxy_subscribe, teardown); \ 2280+ g_test_add ("/gdbus/subscribe/parallel/" #name, \ 2281+ Fixture, &plan_ ## name, \ 2282+ setup, test_parallel_subscribe, teardown); \ 2283+ } while (0) 2284+ 2285+ ADD_SUBSCRIBE_TEST (simple); 2286+ ADD_SUBSCRIBE_TEST (broadcast_from_anyone); 2287+ ADD_SUBSCRIBE_TEST (match_twice); 2288+ ADD_SUBSCRIBE_TEST (limit_by_unique_name); 2289+ ADD_SUBSCRIBE_TEST (nonexistent_unique_name); 2290+ ADD_SUBSCRIBE_TEST (limit_by_well_known_name); 2291+ ADD_SUBSCRIBE_TEST (limit_to_message_bus); 2292+ ADD_SUBSCRIBE_TEST (unsubscribe_immediately); 2293+ 2294+ return g_test_run(); 2295+} 2296+ 2297diff --git a/gio/tests/meson.build b/gio/tests/meson.build 2298index 3ed23a5..fd826cf 100644 2299--- a/gio/tests/meson.build 2300+++ b/gio/tests/meson.build 2301@@ -347,6 +347,7 @@ if host_machine.system() != 'windows' 2302 }, 2303 'gdbus-proxy-unique-name' : {'extra_sources' : extra_sources}, 2304 'gdbus-proxy-well-known-name' : {'extra_sources' : extra_sources}, 2305+ 'gdbus-subscribe' : {'extra_sources' : extra_sources}, 2306 'gdbus-test-codegen' : { 2307 'extra_sources' : [extra_sources, gdbus_test_codegen_generated, gdbus_test_codegen_generated_interface_info], 2308 'c_args' : ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32'], 2309diff --git a/glib/glib-private.h b/glib/glib-private.h 2310index 943252f..f02828f 100644 2311--- a/glib/glib-private.h 2312+++ b/glib/glib-private.h 2313@@ -201,4 +201,22 @@ GLibPrivateVTable *glib__private__ (void); 2314 # define GLIB_DEFAULT_LOCALE "" 2315 #endif 2316 2317+/* Backported from GLib 2.78.x, where it is public API in gstrfuncs.h */ 2318+static inline gboolean 2319+g_set_str (char **str_pointer, 2320+ const char *new_str) 2321+{ 2322+ char *copy; 2323+ 2324+ if (*str_pointer == new_str || 2325+ (*str_pointer && new_str && strcmp (*str_pointer, new_str) == 0)) 2326+ return FALSE; 2327+ 2328+ copy = g_strdup (new_str); 2329+ g_free (*str_pointer); 2330+ *str_pointer = copy; 2331+ 2332+ return TRUE; 2333+} 2334+ 2335 #endif /* __GLIB_PRIVATE_H__ */ 2336 2337