Commit 00af6389 authored by Simon McVittie's avatar Simon McVittie

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=46787Reviewed-by: Philip Withnall's avatarPhilip Withnall <philip.withnall@collabora.co.uk>
parent 4a0f1849
......@@ -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;
}
......@@ -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);
......
......@@ -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
......
......@@ -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
......
......@@ -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 */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment