Commit a650bd05 authored by Simon McVittie's avatar Simon McVittie

Add a regression test for being a new-style monitor

This includes most of the situations I could think of:

* method call on dbus-daemon and response
* NameOwnerChanged
* NameAcquired, NameLost (although I'm not 100% sure these should
  get captured, since they're redundant with NameOwnerChanged)
* unicast message is allowed through
* unicast message is rejected by no-sending or no-receiving policy
* broadcast is allowed through
* broadcast is rejected by no-sending policy (the error reply
  is also captured)
* broadcast is rejected by no-receiving policy (there is no error
  reply)
* message causing service activation, and the message telling systemd
  to do the actual activation
* systemd reporting that activation failed

It does not cover:

* sending a message to dbus-daemon, then provoking a reply, then
  dbus-daemon does not allow itself to send the reply due to its
  own security policy

This is such an obscure corner case that I'm not even convinced it's
testable without dropping down into lower-level socket manipulation:
dbus-daemon's replies are always assumed to be requested replies,
and replies contain so little other metadata that I think we can
only forbid them by forbidding all method replies. If we do that,
the reply to Hello() won't arrive and the client-side connection will
not become active.

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=46787Reviewed-by: Philip Withnall's avatarPhilip Withnall <philip.withnall@collabora.co.uk>
parent 1acedfdd
......@@ -156,6 +156,7 @@ installable_tests += \
test-corrupt \
test-dbus-daemon \
test-dbus-daemon-eavesdrop \
test-monitor \
test-loopback \
test-marshal \
test-refs \
......@@ -245,6 +246,15 @@ test_marshal_LDADD = \
$(GLIB_LIBS) \
$(NULL)
test_monitor_SOURCES = \
monitor.c \
$(NULL)
test_monitor_CPPFLAGS = $(testutils_shared_if_possible_cppflags)
test_monitor_LDADD = \
$(testutils_shared_if_possible_libs) \
$(GLIB_LIBS) \
$(NULL)
test_syntax_SOURCES = syntax.c
test_syntax_LDADD = \
$(top_builddir)/dbus/libdbus-1.la \
......@@ -295,6 +305,7 @@ in_data = \
data/valid-config-files/debug-allow-all-sha1.conf.in \
data/valid-config-files/debug-allow-all.conf.in \
data/valid-config-files/finite-timeout.conf.in \
data/valid-config-files/forbidding.conf.in \
data/valid-config-files/incoming-limit.conf.in \
data/valid-config-files/multi-user.conf.in \
data/valid-config-files/systemd-activation.conf.in \
......
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Our well-known bus type, don't change this -->
<type>session</type>
<listen>@TEST_LISTEN@</listen>
<policy context="default">
<!-- Allow everything -->
<allow send_destination="*"/>
<allow receive_sender="*"/>
<allow own="*"/>
<!-- Exception: some messages are forbidden -->
<deny send_interface="com.example.CannotSend"/>
<deny receive_interface="com.example.CannotReceive"/>
</policy>
</busconfig>
/* Integration tests for monitor-mode D-Bus connections
*
* Copyright © 2010-2011 Nokia Corporation
* Copyright © 2015 Collabora Ltd.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <config.h>
#include <string.h>
#include "test-utils-glib.h"
typedef struct {
const char *config_file;
const char * const *match_rules;
gboolean care_about_our_names;
} Config;
typedef struct {
const Config *config;
TestMainContext *ctx;
DBusError e;
GError *ge;
gchar *address;
GPid daemon_pid;
DBusConnection *monitor;
DBusConnection *sender;
DBusConnection *recipient;
GQueue monitored;
const char *monitor_name;
const char *sender_name;
const char *recipient_name;
DBusConnection *systemd;
const char *systemd_name;
DBusMessage *systemd_message;
DBusConnection *activated;
const char *activated_name;
DBusMessage *activated_message;
} Fixture;
static const char * const no_match_rules[] = {
NULL
};
static const char * const wildcard_match_rules[] = {
"",
NULL,
FALSE
};
static const char * const eavesdrop_match_rules[] = {
"eavesdrop=true",
NULL,
FALSE
};
static const char * const no_eavesdrop_match_rules[] = {
"eavesdrop=false",
NULL,
FALSE
};
static const char * const selective_match_rules[] = {
"interface='com.example.Interesting'",
"interface='com.example.Fun'",
NULL,
FALSE
};
static Config forbidding_config = {
"valid-config-files/forbidding.conf",
NULL,
FALSE
};
static Config wildcard_config = {
NULL,
wildcard_match_rules,
FALSE
};
static Config selective_config = {
NULL,
selective_match_rules,
FALSE
};
static Config no_rules_config = {
NULL,
no_match_rules,
FALSE
};
static Config eavesdrop_config = {
NULL,
eavesdrop_match_rules,
FALSE
};
static Config no_eavesdrop_config = {
NULL,
no_eavesdrop_match_rules,
FALSE
};
static Config fake_systemd_config = {
"valid-config-files/systemd-activation.conf",
NULL,
FALSE
};
static Config side_effects_config = {
NULL,
NULL,
TRUE
};
static inline const char *
not_null2 (const char *x,
const char *fallback)
{
if (x == NULL)
return fallback;
return x;
}
static inline const char *
not_null (const char *x)
{
return not_null2 (x, "(null)");
}
#define log_message(m) _log_message (m, __FILE__, __LINE__)
G_GNUC_UNUSED
static void
_log_message (DBusMessage *m,
const char *file,
int line)
{
g_message ("%s:%d: message type %d (%s)", file, line,
dbus_message_get_type (m),
dbus_message_type_to_string (dbus_message_get_type (m)));
g_message ("\tfrom: %s",
not_null2 (dbus_message_get_sender (m), "(dbus-daemon)"));
g_message ("\tto: %s",
not_null2 (dbus_message_get_destination (m), "(broadcast)"));
g_message ("\tpath: %s",
not_null (dbus_message_get_path (m)));
g_message ("\tinterface: %s",
not_null (dbus_message_get_interface (m)));
g_message ("\tmember: %s",
not_null (dbus_message_get_member (m)));
g_message ("\tsignature: %s",
not_null (dbus_message_get_signature (m)));
g_message ("\terror name: %s",
not_null (dbus_message_get_error_name (m)));
if (strcmp ("s", dbus_message_get_signature (m)) == 0)
{
DBusError e = DBUS_ERROR_INIT;
const char *s;
dbus_message_get_args (m, &e,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID);
test_assert_no_error (&e);
g_message ("\tstring payload: %s", s);
}
}
/* these are macros so they get the right line number */
#define assert_hello(m) \
do { \
g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_METHOD_CALL)); \
g_assert_cmpstr (dbus_message_get_destination (m), ==, DBUS_SERVICE_DBUS); \
g_assert_cmpstr (dbus_message_get_path (m), ==, DBUS_PATH_DBUS); \
g_assert_cmpstr (dbus_message_get_interface (m), ==, DBUS_INTERFACE_DBUS); \
g_assert_cmpstr (dbus_message_get_member (m), ==, "Hello"); \
g_assert_cmpstr (dbus_message_get_signature (m), ==, ""); \
g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
g_assert_cmpint (dbus_message_get_reply_serial (m), ==, 0); \
} while (0)
#define assert_hello_reply(m) \
do { \
DBusError _e = DBUS_ERROR_INIT; \
const char *_s; \
\
g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_METHOD_RETURN)); \
g_assert_cmpstr (dbus_message_get_sender (m), ==, DBUS_SERVICE_DBUS); \
g_assert_cmpstr (dbus_message_get_path (m), ==, NULL); \
g_assert_cmpstr (dbus_message_get_interface (m), ==, NULL); \
g_assert_cmpstr (dbus_message_get_member (m), ==, NULL); \
g_assert_cmpstr (dbus_message_get_signature (m), ==, "s"); \
g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
g_assert_cmpint (dbus_message_get_reply_serial (m), !=, 0); \
\
dbus_message_get_args (m, &_e, \
DBUS_TYPE_STRING, &_s, \
DBUS_TYPE_INVALID); \
test_assert_no_error (&_e); \
g_assert_cmpstr (dbus_message_get_destination (m), ==, _s); \
} while (0)
#define assert_name_acquired(m) \
do { \
DBusError _e = DBUS_ERROR_INIT; \
const char *_s; \
\
g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_SIGNAL)); \
g_assert_cmpstr (dbus_message_get_sender (m), ==, DBUS_SERVICE_DBUS); \
g_assert_cmpstr (dbus_message_get_path (m), ==, DBUS_PATH_DBUS); \
g_assert_cmpstr (dbus_message_get_interface (m), ==, DBUS_INTERFACE_DBUS); \
g_assert_cmpstr (dbus_message_get_member (m), ==, "NameAcquired"); \
g_assert_cmpstr (dbus_message_get_signature (m), ==, "s"); \
g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
g_assert_cmpint (dbus_message_get_reply_serial (m), ==, 0); \
\
dbus_message_get_args (m, &_e, \
DBUS_TYPE_STRING, &_s, \
DBUS_TYPE_INVALID); \
test_assert_no_error (&_e); \
g_assert_cmpstr (dbus_message_get_destination (m), ==, _s); \
} while (0)
#define assert_method_call(m, sender, \
destination, path, iface, method, signature) \
do { \
g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_METHOD_CALL)); \
g_assert_cmpstr (dbus_message_get_sender (m), ==, sender); \
g_assert_cmpstr (dbus_message_get_destination (m), ==, destination); \
g_assert_cmpstr (dbus_message_get_path (m), ==, path); \
g_assert_cmpstr (dbus_message_get_interface (m), ==, iface); \
g_assert_cmpstr (dbus_message_get_member (m), ==, method); \
g_assert_cmpstr (dbus_message_get_signature (m), ==, signature); \
g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
g_assert_cmpint (dbus_message_get_reply_serial (m), ==, 0); \
} while (0)
#define assert_signal(m, \
sender, path, iface, member, signature, \
destination) \
do { \
g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_SIGNAL)); \
g_assert_cmpstr (dbus_message_get_sender (m), ==, sender); \
g_assert_cmpstr (dbus_message_get_destination (m), ==, destination); \
g_assert_cmpstr (dbus_message_get_path (m), ==, path); \
g_assert_cmpstr (dbus_message_get_interface (m), ==, iface); \
g_assert_cmpstr (dbus_message_get_member (m), ==, member); \
g_assert_cmpstr (dbus_message_get_signature (m), ==, signature); \
g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
g_assert_cmpint (dbus_message_get_reply_serial (m), ==, 0); \
} while (0)
#define assert_method_reply(m, sender, destination, signature) \
do { \
g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_METHOD_RETURN)); \
g_assert_cmpstr (dbus_message_get_sender (m), ==, sender); \
g_assert_cmpstr (dbus_message_get_destination (m), ==, destination); \
g_assert_cmpstr (dbus_message_get_path (m), ==, NULL); \
g_assert_cmpstr (dbus_message_get_interface (m), ==, NULL); \
g_assert_cmpstr (dbus_message_get_member (m), ==, NULL); \
g_assert_cmpstr (dbus_message_get_signature (m), ==, signature); \
g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
g_assert_cmpint (dbus_message_get_reply_serial (m), !=, 0); \
} while (0)
#define assert_error_reply(m, sender, destination, error_name) \
do { \
g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_ERROR)); \
g_assert_cmpstr (dbus_message_get_sender (m), ==, sender); \
g_assert_cmpstr (dbus_message_get_destination (m), ==, destination); \
g_assert_cmpstr (dbus_message_get_error_name (m), ==, error_name); \
g_assert_cmpstr (dbus_message_get_path (m), ==, NULL); \
g_assert_cmpstr (dbus_message_get_interface (m), ==, NULL); \
g_assert_cmpstr (dbus_message_get_member (m), ==, NULL); \
g_assert_cmpstr (dbus_message_get_signature (m), ==, "s"); \
g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
g_assert_cmpint (dbus_message_get_reply_serial (m), !=, 0); \
} while (0)
/* This is called after processing pending replies to our own method
* calls, but before anything else.
*/
static DBusHandlerResult
monitor_filter (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
Fixture *f = user_data;
g_assert_cmpstr (dbus_message_get_interface (message), !=,
"com.example.Tedious");
/* we are not interested in the monitor getting NameAcquired or NameLost
* for most tests */
if (f->config == NULL || !f->config->care_about_our_names)
{
if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
"NameAcquired") ||
dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
"NameLost"))
{
DBusError e = DBUS_ERROR_INIT;
const char *s;
dbus_message_get_args (message, &e,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID);
test_assert_no_error (&e);
if (strcmp (s, f->monitor_name) == 0)
{
/* ignore */
return DBUS_HANDLER_RESULT_HANDLED;
}
}
}
g_queue_push_tail (&f->monitored, dbus_message_ref (message));
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
recipient_filter (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
g_assert_cmpstr (dbus_message_get_interface (message), !=,
"com.example.CannotSend");
g_assert_cmpstr (dbus_message_get_interface (message), !=,
"com.example.CannotReceive");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static DBusHandlerResult
systemd_filter (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
Fixture *f = user_data;
if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
"NameAcquired") ||
dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
"NameLost"))
{
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
g_assert (f->systemd_message == NULL);
f->systemd_message = dbus_message_ref (message);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static DBusHandlerResult
activated_filter (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
Fixture *f = user_data;
if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
"NameAcquired") ||
dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
"NameLost"))
{
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
g_assert (f->activated_message == NULL);
f->activated_message = dbus_message_ref (message);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static void
setup (Fixture *f,
gconstpointer context)
{
f->config = context;
f->ctx = test_main_context_get ();
f->ge = NULL;
dbus_error_init (&f->e);
f->address = test_get_dbus_daemon (f->config ? f->config->config_file : NULL,
TEST_USER_ME, &f->daemon_pid);
if (f->address == NULL)
return;
f->monitor = test_connect_to_bus (f->ctx, f->address);
f->monitor_name = dbus_bus_get_unique_name (f->monitor);
f->sender = test_connect_to_bus (f->ctx, f->address);
f->sender_name = dbus_bus_get_unique_name (f->sender);
f->recipient = test_connect_to_bus (f->ctx, f->address);
f->recipient_name = dbus_bus_get_unique_name (f->recipient);
if (!dbus_connection_add_filter (f->monitor, monitor_filter, f, NULL))
g_error ("OOM");
if (!dbus_connection_add_filter (f->recipient, recipient_filter, f, NULL))
g_error ("OOM");
}
static void
become_monitor (Fixture *f)
{
DBusMessage *m;
DBusPendingCall *pc;
dbus_bool_t ok;
DBusMessageIter appender, array_appender;
const char * const *match_rules;
int i;
dbus_uint32_t zero = 0;
if (f->config != NULL && f->config->match_rules != NULL)
match_rules = f->config->match_rules;
else
match_rules = wildcard_match_rules;
m = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS, DBUS_INTERFACE_MONITORING, "BecomeMonitor");
if (m == NULL)
g_error ("OOM");
dbus_message_iter_init_append (m, &appender);
if (!dbus_message_iter_open_container (&appender, DBUS_TYPE_ARRAY, "s",
&array_appender))
g_error ("OOM");
for (i = 0; match_rules[i] != NULL; i++)
{
if (!dbus_message_iter_append_basic (&array_appender, DBUS_TYPE_STRING,
&match_rules[i]))
g_error ("OOM");
}
if (!dbus_message_iter_close_container (&appender, &array_appender) ||
!dbus_message_iter_append_basic (&appender, DBUS_TYPE_UINT32, &zero))
g_error ("OOM");
if (!dbus_connection_send_with_reply (f->monitor, m, &pc,
DBUS_TIMEOUT_USE_DEFAULT) ||
pc == NULL)
g_error ("OOM");
dbus_message_unref (m);
m = NULL;
if (dbus_pending_call_get_completed (pc))
test_pending_call_store_reply (pc, &m);
else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply,
&m, NULL))
g_error ("OOM");
while (m == NULL)
test_main_context_iterate (f->ctx, TRUE);
ok = dbus_message_get_args (m, &f->e,
DBUS_TYPE_INVALID);
test_assert_no_error (&f->e);
g_assert (ok);
dbus_pending_call_unref (pc);
dbus_message_unref (m);
m = NULL;
}
/*
* Test the side-effects of becoming a monitor.
*/
static void
test_become_monitor (Fixture *f,
gconstpointer context)
{
DBusMessage *m;
int ret;
dbus_bool_t got_unique = FALSE, got_a = FALSE, got_b = FALSE, got_c = FALSE;
dbus_bool_t lost_unique = FALSE, lost_a = FALSE, lost_b = FALSE, lost_c = FALSE;
if (f->address == NULL)
return;
ret = dbus_bus_request_name (f->monitor, "com.example.A",
DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e);
test_assert_no_error (&f->e);
g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
ret = dbus_bus_request_name (f->monitor, "com.example.B",
DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e);
test_assert_no_error (&f->e);
g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
ret = dbus_bus_request_name (f->monitor, "com.example.C",
DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e);
test_assert_no_error (&f->e);
g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
while (!got_unique || !got_a || !got_b || !got_c)
{
test_main_context_iterate (f->ctx, TRUE);
while ((m = g_queue_pop_head (&f->monitored)) != NULL)
{
if (dbus_message_is_signal (m, DBUS_INTERFACE_DBUS,
"NameAcquired"))
{
const char *name;
dbus_bool_t ok = dbus_message_get_args (m, &f->e,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID);
g_assert_cmpstr (dbus_message_get_path (m), ==,
DBUS_PATH_DBUS);
test_assert_no_error (&f->e);
g_assert (ok);
if (g_str_equal (name, f->monitor_name))
{
g_assert (!got_unique);
got_unique = TRUE;
}
else if (g_str_equal (name, "com.example.A"))
{
g_assert (!got_a);
got_a = TRUE;
}
else if (g_str_equal (name, "com.example.B"))
{
g_assert (!got_b);
got_b = TRUE;
}
else
{
g_assert_cmpstr (name, ==, "com.example.C");
g_assert (!got_c);
got_c = TRUE;
}
}
else
{
g_error ("unexpected message %s.%s",
dbus_message_get_interface (m),
dbus_message_get_member (m));
}
dbus_message_unref (m);
}
}
become_monitor (f);
while (!lost_unique || !lost_a || !lost_b || !lost_c)
{
test_main_context_iterate (f->ctx, TRUE);
while ((m = g_queue_pop_head (&f->monitored)) != NULL)
{
if (dbus_message_is_signal (m, DBUS_INTERFACE_DBUS,
"NameLost"))
{
const char *name;
dbus_bool_t ok = dbus_message_get_args (m, &f->e,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID);
test_assert_no_error (&f->e);
g_assert (ok);
if (g_str_equal (name, f->monitor_name))
{
g_assert (!lost_unique);
lost_unique = TRUE;
}
else if (g_str_equal (name, "com.example.A"))
{
g_assert (!lost_a);
lost_a = TRUE;
}
else if (g_str_equal (name, "com.example.B"))
{
g_assert (!lost_b);
lost_b = TRUE;
}
else
{
g_assert_cmpstr (name, ==, "com.example.C");
g_assert (!lost_c);
lost_c = TRUE;
}
}
else
{
g_error ("unexpected message %s.%s",
dbus_message_get_interface (m),
dbus_message_get_member (m));
}
dbus_message_unref (m);
}
}
/* Calling methods is forbidden; we get disconnected. */
dbus_bus_add_match (f->monitor, "", &f->e);
g_assert_cmpstr (f->e.name, ==, DBUS_ERROR_NO_REPLY);
g_assert (!dbus_connection_get_is_connected (f->monitor));
while (TRUE)
{
test_main_context_iterate (f->ctx, TRUE);
/* When we iterate all the connection's messages, we see ourselves
* losing all our names, then we're disconnected. */
while ((m = g_queue_pop_head (&f->monitored)) != NULL)
{
if (dbus_message_is_signal (m, DBUS_INTERFACE_LOCAL, "Disconnected"))
{
dbus_message_unref (m);
goto disconnected;
}
else
{
g_error ("unexpected message %s.%s",
dbus_message_get_interface (m),
dbus_message_get_member (m));
}
dbus_message_unref (m);
}
}
disconnected:
g_assert (lost_a);
g_assert (lost_b);
g_assert (lost_c);
}
static void
test_broadcast (Fixture *f,
gconstpointer context)
{
DBusMessage *m;
if (f->address == NULL)
return;
dbus_bus_add_match (f->recipient, "type='signal'", &f->e);
test_assert_no_error (&f->e);
become_monitor (f);