]> git.ipfire.org Git - thirdparty/dbus.git/commitdiff
bus: Separate RemoveMatch into prepare and commit stages
authorSimon McVittie <smcv@collabora.com>
Mon, 22 Nov 2021 16:01:58 +0000 (16:01 +0000)
committerSimon McVittie <smcv@collabora.com>
Mon, 22 Nov 2021 16:10:51 +0000 (16:10 +0000)
This means we don't send a spurious successful reply if a caller removes
a match rule that they never added.

Signed-off-by: Simon McVittie <smcv@collabora.com>
bus/driver.c
bus/signals.c
bus/signals.h

index 3898cfd6ba651382c2a3fb29fff007ed140756ce..6f5451a10fc853be7afcf7c1b169622c0be0e048 100644 (file)
@@ -1409,6 +1409,7 @@ bus_driver_handle_remove_match (DBusConnection *connection,
   const char *text;
   DBusString str;
   BusMatchmaker *matchmaker;
+  DBusList *link;
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
 
@@ -1429,17 +1430,21 @@ bus_driver_handle_remove_match (DBusConnection *connection,
   if (rule == NULL)
     goto failed;
 
-  /* Send the ack before we remove the rule, since the ack is undone
-   * on transaction cancel, but rule removal isn't.
-   */
-  if (!bus_driver_send_ack_reply (connection, transaction, message, error))
-    goto failed;
-
   matchmaker = bus_connection_get_matchmaker (connection);
 
-  if (!bus_matchmaker_remove_rule_by_value (matchmaker, rule, error))
+  /* Check whether the rule exists and prepare to remove it, but don't
+   * actually do it yet. */
+  link = bus_matchmaker_prepare_remove_rule_by_value (matchmaker, rule, error);
+  if (link == NULL)
+    goto failed;
+
+  /* We do this before actually removing the rule, because removing the
+   * rule cannot be undone if we run out of memory here. */
+  if (!bus_driver_send_ack_reply (connection, transaction, message, error))
     goto failed;
 
+  /* The rule exists, so now we can do things we can't undo. */
+  bus_matchmaker_commit_remove_rule_by_value (matchmaker, rule, link);
   bus_match_rule_unref (rule);
 
   return TRUE;
index 08304c5b381dee3ac9981f811970b4aab519f986..2af5039b14890d082f62dcb10b3242d83a32d93b 100644 (file)
@@ -1608,21 +1608,32 @@ bus_matchmaker_remove_rule (BusMatchmaker   *matchmaker,
   bus_match_rule_unref (rule);
 }
 
-/* Remove a single rule which is equal to the given rule by value */
-dbus_bool_t
-bus_matchmaker_remove_rule_by_value (BusMatchmaker   *matchmaker,
-                                     BusMatchRule    *value,
-                                     DBusError       *error)
+/*
+ * Prepare to remove the the most-recently-added rule which is equal to
+ * the given rule by value, but do not actually do it yet.
+ *
+ * Return a linked-list link which must be treated as opaque by the caller:
+ * the only valid thing to do with it is to pass it to
+ * bus_matchmaker_commit_remove_rule_by_value().
+ *
+ * The returned linked-list link becomes invalid when control returns to
+ * the main loop. If the caller decides not to remove the rule after all,
+ * there is currently no need to cancel explicitly.
+ */
+DBusList *
+bus_matchmaker_prepare_remove_rule_by_value (BusMatchmaker   *matchmaker,
+                                             BusMatchRule    *value,
+                                             DBusError       *error)
 {
   DBusList **rules;
   DBusList *link = NULL;
 
-  _dbus_verbose ("Removing rule by value with message_type %d, interface %s\n",
+  _dbus_verbose ("Finding rule by value with message_type %d, interface %s\n",
                  value->message_type,
                  value->interface != NULL ? value->interface : "<null>");
 
   rules = bus_matchmaker_get_rules (matchmaker, value->message_type,
-      value->interface, FALSE);
+                                    value->interface, FALSE);
 
   if (rules != NULL)
     {
@@ -1639,26 +1650,37 @@ bus_matchmaker_remove_rule_by_value (BusMatchmaker   *matchmaker,
           prev = _dbus_list_get_prev_link (rules, link);
 
           if (match_rule_equal (rule, value))
-            {
-              bus_matchmaker_remove_rule_link (rules, link);
-              break;
-            }
+            return link;
 
           link = prev;
         }
     }
 
-  if (link == NULL)
-    {
-      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
-                      "The given match rule wasn't found and can't be removed");
-      return FALSE;
-    }
+  dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
+                  "The given match rule wasn't found and can't be removed");
+  return NULL;
+}
 
+/*
+ * Commit a previous call to bus_matchmaker_prepare_remove_rule_by_value(),
+ * which must have been done during the same main-loop iteration.
+ */
+void
+bus_matchmaker_commit_remove_rule_by_value (BusMatchmaker *matchmaker,
+                                            BusMatchRule  *value,
+                                            DBusList      *link)
+{
+  DBusList **rules;
+
+  _dbus_assert (match_rule_equal (link->data, value));
+  rules = bus_matchmaker_get_rules (matchmaker, value->message_type,
+                                    value->interface, FALSE);
+  /* Should only be called if a rule matching value was successfully
+   * added, which means rules must contain at least link */
+  _dbus_assert (rules != NULL);
+  bus_matchmaker_remove_rule_link (rules, link);
   bus_matchmaker_gc_rules (matchmaker, value->message_type, value->interface,
       rules);
-
-  return TRUE;
 }
 
 static void
index 0edfb07efc744e92f5f529bb33837877755e6c6a..027e921c1fd385464cd38155b71d9a663c7fa34f 100644 (file)
@@ -91,9 +91,6 @@ void           bus_matchmaker_unref (BusMatchmaker *matchmaker);
 
 dbus_bool_t bus_matchmaker_add_rule             (BusMatchmaker   *matchmaker,
                                                  BusMatchRule    *rule);
-dbus_bool_t bus_matchmaker_remove_rule_by_value (BusMatchmaker   *matchmaker,
-                                                 BusMatchRule    *value,
-                                                 DBusError       *error);
 void        bus_matchmaker_remove_rule          (BusMatchmaker   *matchmaker,
                                                  BusMatchRule    *rule);
 void        bus_matchmaker_disconnected         (BusMatchmaker   *matchmaker,
@@ -105,4 +102,11 @@ dbus_bool_t bus_matchmaker_get_recipients       (BusMatchmaker   *matchmaker,
                                                  DBusMessage     *message,
                                                  DBusList       **recipients_p);
 
+DBusList *bus_matchmaker_prepare_remove_rule_by_value (BusMatchmaker   *matchmaker,
+                                                       BusMatchRule    *value,
+                                                       DBusError       *error);
+void      bus_matchmaker_commit_remove_rule_by_value  (BusMatchmaker   *matchmaker,
+                                                       BusMatchRule    *value,
+                                                       DBusList        *link);
+
 #endif /* BUS_SIGNALS_H */