summaryrefslogtreecommitdiff
path: root/bus/connection.c
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2015-02-02 19:45:17 +0000
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2015-02-04 17:15:17 +0000
commit9fce7380331d24e8dd5fb9203eb8275ebb49e1d8 (patch)
tree6f655086533c73df8b24166c0e7b39f38b7939f3 /bus/connection.c
parent00af6389be46d65afcce8cdfd060f278aaaa9466 (diff)
downloaddbus-9fce7380331d24e8dd5fb9203eb8275ebb49e1d8.tar.gz
Capture all messages received or sent, and send them to monitors
Unlike eavesdropping, the point of capture is when the message is received, except for messages originating inside the dbus-daemon. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46787 Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
Diffstat (limited to 'bus/connection.c')
-rw-r--r--bus/connection.c187
1 files changed, 178 insertions, 9 deletions
diff --git a/bus/connection.c b/bus/connection.c
index 1d9bfb2b..410aaaca 100644
--- a/bus/connection.c
+++ b/bus/connection.c
@@ -67,6 +67,7 @@ struct BusConnections
/** List of all monitoring connections, a subset of completed.
* Each member is a #DBusConnection. */
DBusList *monitors;
+ BusMatchmaker *monitor_matchmaker;
#ifdef DBUS_ENABLE_STATS
int total_match_rules;
@@ -292,6 +293,11 @@ bus_connection_disconnected (DBusConnection *connection)
if (d->link_in_monitors != NULL)
{
+ BusMatchmaker *mm = d->connections->monitor_matchmaker;
+
+ if (mm != NULL)
+ bus_matchmaker_disconnected (mm, connection);
+
_dbus_list_remove_link (&d->connections->monitors, d->link_in_monitors);
d->link_in_monitors = NULL;
}
@@ -553,7 +559,10 @@ bus_connections_unref (BusConnections *connections)
_dbus_timeout_unref (connections->expire_timeout);
_dbus_hash_table_unref (connections->completed_by_user);
-
+
+ if (connections->monitor_matchmaker != NULL)
+ bus_matchmaker_unref (connections->monitor_matchmaker);
+
dbus_free (connections);
dbus_connection_free_data_slot (&connection_data_slot);
@@ -1272,6 +1281,10 @@ bus_connection_send_oom_error (DBusConnection *connection,
_dbus_assert (d != NULL);
_dbus_assert (d->oom_message != NULL);
+ bus_context_log (d->connections->context, DBUS_SYSTEM_LOG_WARNING,
+ "dbus-daemon transaction failed (OOM), sending error to "
+ "sender %s", bus_connection_get_loginfo (connection));
+
/* should always succeed since we set it to a placeholder earlier */
if (!dbus_message_set_reply_serial (d->oom_message,
dbus_message_get_serial (in_reply_to)))
@@ -2120,11 +2133,95 @@ bus_transaction_get_context (BusTransaction *transaction)
return transaction->context;
}
+/**
+ * Reserve enough memory to capture the given message if the
+ * transaction goes through.
+ */
+dbus_bool_t
+bus_transaction_capture (BusTransaction *transaction,
+ DBusConnection *sender,
+ DBusMessage *message)
+{
+ BusConnections *connections;
+ BusMatchmaker *mm;
+ DBusList *link;
+ DBusList *recipients = NULL;
+ dbus_bool_t ret = FALSE;
+
+ connections = bus_context_get_connections (transaction->context);
+
+ /* shortcut: don't compose the message unless someone wants it */
+ if (connections->monitors == NULL)
+ return TRUE;
+
+ mm = connections->monitor_matchmaker;
+ /* This is non-null if there has ever been a monitor - we don't GC it.
+ * There's little point, since there is up to 1 per process. */
+ _dbus_assert (mm != NULL);
+
+ if (!bus_matchmaker_get_recipients (mm, connections, sender, NULL, message,
+ &recipients))
+ goto out;
+
+ for (link = _dbus_list_get_first_link (&recipients);
+ link != NULL;
+ link = _dbus_list_get_next_link (&recipients, link))
+ {
+ DBusConnection *recipient = link->data;
+
+ if (!bus_transaction_send (transaction, recipient, message))
+ goto out;
+ }
+
+ ret = TRUE;
+
+out:
+ _dbus_list_clear (&recipients);
+ return ret;
+}
+
+static dbus_bool_t
+bus_transaction_capture_error_reply (BusTransaction *transaction,
+ const DBusError *error,
+ DBusMessage *in_reply_to)
+{
+ BusConnections *connections;
+ DBusMessage *reply;
+ dbus_bool_t ret = FALSE;
+
+ _dbus_assert (error != NULL);
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ connections = bus_context_get_connections (transaction->context);
+
+ /* shortcut: don't compose the message unless someone wants it */
+ if (connections->monitors == NULL)
+ return TRUE;
+
+ reply = dbus_message_new_error (in_reply_to,
+ error->name,
+ error->message);
+
+ if (reply == NULL)
+ return FALSE;
+
+ if (!dbus_message_set_sender (reply, DBUS_SERVICE_DBUS))
+ goto out;
+
+ ret = bus_transaction_capture (transaction, NULL, reply);
+
+out:
+ dbus_message_unref (reply);
+ return ret;
+}
+
dbus_bool_t
bus_transaction_send_from_driver (BusTransaction *transaction,
DBusConnection *connection,
DBusMessage *message)
{
+ DBusError error = DBUS_ERROR_INIT;
+
/* We have to set the sender to the driver, and have
* to check security policy since it was not done in
* dispatch.c
@@ -2149,14 +2246,34 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
/* bus driver never wants a reply */
dbus_message_set_no_reply (message, TRUE);
-
- /* If security policy doesn't allow the message, we silently
- * eat it; the driver doesn't care about getting a reply.
+
+ /* Capture it for monitors, even if the real recipient's receive policy
+ * does not allow it to receive this message from us (which would be odd).
+ */
+ if (!bus_transaction_capture (transaction, NULL, message))
+ return FALSE;
+
+ /* If security policy doesn't allow the message, we would silently
+ * eat it; the driver doesn't care about getting a reply. However,
+ * if we're actively capturing messages, it's nice to log that we
+ * tried to send it and did not allow ourselves to do so.
*/
if (!bus_context_check_security_policy (bus_transaction_get_context (transaction),
transaction,
- NULL, connection, connection, message, NULL))
- return TRUE;
+ NULL, connection, connection, message, &error))
+ {
+ if (!bus_transaction_capture_error_reply (transaction, &error, message))
+ {
+ bus_context_log (transaction->context, DBUS_SYSTEM_LOG_WARNING,
+ "message from dbus-daemon rejected but not enough "
+ "memory to capture it");
+ }
+
+ /* This is not fatal to the transaction so silently eat the disallowed
+ * message (see reasoning above) */
+ dbus_error_free (&error);
+ return TRUE;
+ }
return bus_transaction_send (transaction, connection, message);
}
@@ -2520,10 +2637,53 @@ bus_connection_is_monitor (DBusConnection *connection)
return d != NULL && d->link_in_monitors != NULL;
}
+static dbus_bool_t
+bcd_add_monitor_rules (BusConnectionData *d,
+ DBusConnection *connection,
+ DBusList **rules)
+{
+ BusMatchmaker *mm = d->connections->monitor_matchmaker;
+ DBusList *iter;
+
+ if (mm == NULL)
+ {
+ mm = bus_matchmaker_new ();
+
+ if (mm == NULL)
+ return FALSE;
+
+ d->connections->monitor_matchmaker = mm;
+ }
+
+ for (iter = _dbus_list_get_first_link (rules);
+ iter != NULL;
+ iter = _dbus_list_get_next_link (rules, iter))
+ {
+ if (!bus_matchmaker_add_rule (mm, iter->data))
+ {
+ bus_matchmaker_disconnected (mm, connection);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+bcd_drop_monitor_rules (BusConnectionData *d,
+ DBusConnection *connection)
+{
+ BusMatchmaker *mm = d->connections->monitor_matchmaker;
+
+ if (mm != NULL)
+ bus_matchmaker_disconnected (mm, connection);
+}
+
dbus_bool_t
-bus_connection_be_monitor (DBusConnection *connection,
- BusTransaction *transaction,
- DBusError *error)
+bus_connection_be_monitor (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusList **rules,
+ DBusError *error)
{
BusConnectionData *d;
DBusList *link;
@@ -2541,9 +2701,17 @@ bus_connection_be_monitor (DBusConnection *connection,
return FALSE;
}
+ if (!bcd_add_monitor_rules (d, connection, rules))
+ {
+ _dbus_list_free_link (link);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
/* release all its names */
if (!_dbus_list_copy (&d->services_owned, &tmp))
{
+ bcd_drop_monitor_rules (d, connection);
_dbus_list_free_link (link);
BUS_SET_OOM (error);
return FALSE;
@@ -2560,6 +2728,7 @@ bus_connection_be_monitor (DBusConnection *connection,
* the transaction is cancelled due to OOM. */
if (!bus_service_remove_owner (service, connection, transaction, error))
{
+ bcd_drop_monitor_rules (d, connection);
_dbus_list_free_link (link);
_dbus_list_clear (&tmp);
return FALSE;