diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2015-01-23 19:11:31 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2015-02-04 17:15:08 +0000 |
commit | 00af6389be46d65afcce8cdfd060f278aaaa9466 (patch) | |
tree | f5f32aadd98ef4e46b97453a01fa582600acba4b | |
parent | 4a0f1849be319b1b2b7a6d415b57e5544ec191d6 (diff) |
Add support for morphing a D-Bus connection into a "monitor"
This is a special connection that is not allowed to send anything,
and loses all its well-known names.
In future commits, it will get a new set of match rules and the
ability to eavesdrop on messages before the rest of the bus daemon
has had a chance to process them.
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46787
Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
-rw-r--r-- | bus/connection.c | 102 | ||||
-rw-r--r-- | bus/connection.h | 5 | ||||
-rw-r--r-- | bus/dispatch.c | 55 | ||||
-rw-r--r-- | bus/driver.c | 35 | ||||
-rw-r--r-- | dbus/dbus-shared.h | 3 |
5 files changed, 199 insertions, 1 deletions
diff --git a/bus/connection.c b/bus/connection.c index f278e619c..1d9bfb2b4 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -64,6 +64,10 @@ struct BusConnections int stamp; /**< Incrementing number */ BusExpireList *pending_replies; /**< List of pending replies */ + /** List of all monitoring connections, a subset of completed. + * Each member is a #DBusConnection. */ + DBusList *monitors; + #ifdef DBUS_ENABLE_STATS int total_match_rules; int peak_match_rules; @@ -105,6 +109,9 @@ typedef struct #endif int n_pending_unix_fds; DBusTimeout *pending_unix_fds_timeout; + + /** non-NULL if and only if this is a monitor */ + DBusList *link_in_monitors; } BusConnectionData; static dbus_bool_t bus_pending_reply_expired (BusExpireList *list, @@ -283,6 +290,12 @@ bus_connection_disconnected (DBusConnection *connection) bus_connection_remove_transactions (connection); + if (d->link_in_monitors != NULL) + { + _dbus_list_remove_link (&d->connections->monitors, d->link_in_monitors); + d->link_in_monitors = NULL; + } + if (d->link_in_connection_list != NULL) { if (d->name != NULL) @@ -513,7 +526,10 @@ bus_connections_unref (BusConnections *connections) } _dbus_assert (connections->n_incomplete == 0); - + + /* drop all monitors */ + _dbus_list_clear (&connections->monitors); + /* drop all real connections */ while (connections->completed != NULL) { @@ -2493,3 +2509,87 @@ bus_connection_get_peak_bus_names (DBusConnection *connection) return d->peak_bus_names; } #endif /* DBUS_ENABLE_STATS */ + +dbus_bool_t +bus_connection_is_monitor (DBusConnection *connection) +{ + BusConnectionData *d; + + d = BUS_CONNECTION_DATA (connection); + + return d != NULL && d->link_in_monitors != NULL; +} + +dbus_bool_t +bus_connection_be_monitor (DBusConnection *connection, + BusTransaction *transaction, + DBusError *error) +{ + BusConnectionData *d; + DBusList *link; + DBusList *tmp; + DBusList *iter; + + d = BUS_CONNECTION_DATA (connection); + _dbus_assert (d != NULL); + + link = _dbus_list_alloc_link (connection); + + if (link == NULL) + { + BUS_SET_OOM (error); + return FALSE; + } + + /* release all its names */ + if (!_dbus_list_copy (&d->services_owned, &tmp)) + { + _dbus_list_free_link (link); + BUS_SET_OOM (error); + return FALSE; + } + + for (iter = _dbus_list_get_first_link (&tmp); + iter != NULL; + iter = _dbus_list_get_next_link (&tmp, iter)) + { + BusService *service = iter->data; + + /* This call is transactional: if there isn't enough memory to + * do everything, then the service gets all its names back when + * the transaction is cancelled due to OOM. */ + if (!bus_service_remove_owner (service, connection, transaction, error)) + { + _dbus_list_free_link (link); + _dbus_list_clear (&tmp); + return FALSE; + } + } + + /* We have now done everything that can fail, so there is no problem + * with doing the irrevocable stuff. */ + + _dbus_list_clear (&tmp); + + bus_context_log (transaction->context, DBUS_SYSTEM_LOG_INFO, + "Connection %s (%s) became a monitor.", d->name, + d->cached_loginfo_string); + + if (d->n_match_rules > 0) + { + BusMatchmaker *mm; + + mm = bus_context_get_matchmaker (d->connections->context); + bus_matchmaker_disconnected (mm, connection); + } + + /* flag it as a monitor */ + d->link_in_monitors = link; + _dbus_list_append_link (&d->connections->monitors, link); + + /* it isn't allowed to reply, and it is no longer relevant whether it + * receives replies */ + bus_connection_drop_pending_replies (d->connections, connection); + + return TRUE; +} diff --git a/bus/connection.h b/bus/connection.h index 6fbcd38dc..f8d616517 100644 --- a/bus/connection.h +++ b/bus/connection.h @@ -116,6 +116,11 @@ dbus_bool_t bus_connection_get_unix_groups (DBusConnection *connecti DBusError *error); BusClientPolicy* bus_connection_get_policy (DBusConnection *connection); +dbus_bool_t bus_connection_is_monitor (DBusConnection *connection); +dbus_bool_t bus_connection_be_monitor (DBusConnection *connection, + BusTransaction *transaction, + DBusError *error); + /* transaction API so we can send or not send a block of messages as a whole */ typedef void (* BusTransactionCancelFunction) (void *data); diff --git a/bus/dispatch.c b/bus/dispatch.c index 8f322f8c1..97fe37129 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -47,6 +47,13 @@ * dbus_connection_open_private() does not block. */ #define TEST_DEBUG_PIPE "debug-pipe:name=test-server" +static inline const char * +nonnull (const char *maybe_null, + const char *if_null) +{ + return (maybe_null ? maybe_null : if_null); +} + static dbus_bool_t send_one_message (DBusConnection *connection, BusContext *context, @@ -200,6 +207,54 @@ bus_dispatch (DBusConnection *connection, /* Ref connection in case we disconnect it at some point in here */ dbus_connection_ref (connection); + /* Monitors aren't meant to send messages to us. */ + if (bus_connection_is_monitor (connection)) + { + sender = bus_connection_get_name (connection); + + /* should never happen */ + if (sender == NULL) + sender = "(unknown)"; + + if (dbus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, + "Disconnected")) + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "Monitoring connection %s closed.", sender); + bus_connection_disconnected (connection); + goto out; + } + else + { + /* Monitors are not allowed to send messages, because that + * probably indicates that the monitor is incorrectly replying + * to its eavesdropped messages, and we want the authors of + * such monitors to fix them. + */ + bus_context_log (context, DBUS_SYSTEM_LOG_WARNING, + "Monitoring connection %s (%s) is not allowed " + "to send messages; closing it. Please fix the " + "monitor to not do that. " + "(message type=\"%s\" interface=\"%s\" " + "member=\"%s\" error name=\"%s\" " + "destination=\"%s\")", + sender, bus_connection_get_loginfo (connection), + dbus_message_type_to_string ( + dbus_message_get_type (message)), + nonnull (dbus_message_get_interface (message), + "(unset)"), + nonnull (dbus_message_get_member (message), + "(unset)"), + nonnull (dbus_message_get_error_name (message), + "(unset)"), + nonnull (dbus_message_get_destination (message), + DBUS_SERVICE_DBUS)); + dbus_connection_close (connection); + goto out; + } + } + service_name = dbus_message_get_destination (message); #ifdef DBUS_ENABLE_VERBOSE_MODE diff --git a/bus/driver.c b/bus/driver.c index 6e8a6dac4..ac29b9f13 100644 --- a/bus/driver.c +++ b/bus/driver.c @@ -1788,6 +1788,35 @@ bus_driver_handle_get_id (DBusConnection *connection, return FALSE; } +static dbus_bool_t +bus_driver_handle_become_monitor (DBusConnection *connection, + BusTransaction *transaction, + DBusMessage *message, + DBusError *error) +{ + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!bus_driver_check_message_is_for_us (message, error)) + return FALSE; + + if (!bus_driver_check_caller_is_privileged (connection, transaction, + message, error)) + return FALSE; + + /* Send the ack before we remove the rule, since the ack is undone + * on transaction cancel, but becoming a monitor isn't. + */ + if (!send_ack_reply (connection, transaction, message, error)) + return FALSE; + + /* FIXME: use the array of filters from the message */ + + if (!bus_connection_be_monitor (connection, transaction, error)) + return FALSE; + + return TRUE; +} + typedef struct { const char *name; @@ -1889,6 +1918,11 @@ static const MessageHandler introspectable_message_handlers[] = { { NULL, NULL, NULL, NULL } }; +static const MessageHandler monitoring_message_handlers[] = { + { "BecomeMonitor", "asu", "", bus_driver_handle_become_monitor }, + { NULL, NULL, NULL, NULL } +}; + #ifdef DBUS_ENABLE_STATS static const MessageHandler stats_message_handlers[] = { { "GetStats", "", "a{sv}", bus_stats_handle_get_stats }, @@ -1920,6 +1954,7 @@ static InterfaceHandler interface_handlers[] = { " <arg type=\"s\"/>\n" " </signal>\n" }, { DBUS_INTERFACE_INTROSPECTABLE, introspectable_message_handlers, NULL }, + { DBUS_INTERFACE_MONITORING, monitoring_message_handlers, NULL }, #ifdef DBUS_ENABLE_STATS { BUS_INTERFACE_STATS, stats_message_handlers, NULL }, #endif diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h index 6a5767041..51c3da8fa 100644 --- a/dbus/dbus-shared.h +++ b/dbus/dbus-shared.h @@ -86,6 +86,9 @@ typedef enum */ /** The interface exported by the object with #DBUS_SERVICE_DBUS and #DBUS_PATH_DBUS */ #define DBUS_INTERFACE_DBUS "org.freedesktop.DBus" +/** The monitoring interface exported by the dbus-daemon */ +#define DBUS_INTERFACE_MONITORING "org.freedesktop.DBus.Monitoring" + /** The interface supported by introspectable objects */ #define DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable" /** The interface supported by objects with properties */ |