From a81ac433c43c49b4304fcc92ed101b3ef63b2b0a Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 2 Feb 2015 20:02:56 +0000 Subject: [PATCH 3/7] Add a regression test for becoming a 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. --- test/Makefile.am | 11 + test/data/valid-config-files/forbidding.conf.in | 18 + test/monitor.c | 1469 +++++++++++++++++++++++ 3 files changed, 1498 insertions(+) create mode 100644 test/data/valid-config-files/forbidding.conf.in create mode 100644 test/monitor.c diff --git a/test/Makefile.am b/test/Makefile.am index 8b84c7d..6d72185 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -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 \ diff --git a/test/data/valid-config-files/forbidding.conf.in b/test/data/valid-config-files/forbidding.conf.in new file mode 100644 index 0000000..6a674f8 --- /dev/null +++ b/test/data/valid-config-files/forbidding.conf.in @@ -0,0 +1,18 @@ + + + + session + @TEST_LISTEN@ + + + + + + + + + + + + diff --git a/test/monitor.c b/test/monitor.c new file mode 100644 index 0000000..74ff452 --- /dev/null +++ b/test/monitor.c @@ -0,0 +1,1469 @@ +/* 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 + +#include + +#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; + + become_monitor (f); + + m = dbus_message_new_signal ("/foo", "com.example.bar", "BroadcastSignal1"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + m = dbus_message_new_signal ("/foo", "com.example.bar", "BroadcastSignal2"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + m = dbus_message_new_signal ("/foo", "com.example.bar", "BroadcastSignal3"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + while (g_queue_get_length (&f->monitored) < 3) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", "com.example.bar", + "BroadcastSignal1", "", NULL); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", "com.example.bar", + "BroadcastSignal2", "", NULL); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", "com.example.bar", + "BroadcastSignal3", "", NULL); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + g_assert (m == NULL); +} + +static void +test_forbidden_broadcast (Fixture *f, + gconstpointer context) +{ + DBusMessage *m; + + if (f->address == NULL) + return; + + become_monitor (f); + + m = dbus_message_new_signal ("/foo", "com.example.CannotSend", + "BroadcastSignal1"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + m = dbus_message_new_signal ("/foo", "com.example.CannotReceive", + "BroadcastSignal2"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + m = dbus_message_new_signal ("/foo", "com.example.CannotSend", + "BroadcastSignal3"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + while (g_queue_get_length (&f->monitored) < 3) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", "com.example.CannotSend", + "BroadcastSignal1", "", NULL); + dbus_message_unref (m); + + /* FIXME: dbus-daemon does not fake a denial message for the monitor's + * benefit; should it? */ + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", "com.example.CannotReceive", + "BroadcastSignal2", "", NULL); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", "com.example.CannotSend", + "BroadcastSignal3", "", NULL); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + g_assert (m == NULL); +} + +static void +test_unicast_signal (Fixture *f, + gconstpointer context) +{ + DBusMessage *m; + + if (f->address == NULL) + return; + + become_monitor (f); + + m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal1"); + if (!dbus_message_set_destination (m, f->recipient_name)) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal2"); + if (!dbus_message_set_destination (m, f->recipient_name)) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal3"); + if (!dbus_message_set_destination (m, f->recipient_name)) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + while (g_queue_get_length (&f->monitored) < 3) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.bar", "UnicastSignal1", "", f->recipient_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.bar", "UnicastSignal2", "", f->recipient_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.bar", "UnicastSignal3", "", f->recipient_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + g_assert (m == NULL); +} + +static void +test_forbidden (Fixture *f, + gconstpointer context) +{ + DBusMessage *m; + + if (f->address == NULL) + return; + + become_monitor (f); + + m = dbus_message_new_signal ("/foo", "com.example.CannotSend", + "UnicastSignal1"); + if (!dbus_message_set_destination (m, f->recipient_name)) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + m = dbus_message_new_signal ("/foo", "com.example.CannotReceive", + "UnicastSignal2"); + if (!dbus_message_set_destination (m, f->recipient_name)) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + m = dbus_message_new_signal ("/foo", "com.example.CannotSend", + "UnicastSignal3"); + if (!dbus_message_set_destination (m, f->recipient_name)) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + while (g_queue_get_length (&f->monitored) < 6) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.CannotSend", "UnicastSignal1", "", f->recipient_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name, + DBUS_ERROR_ACCESS_DENIED); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.CannotReceive", "UnicastSignal2", "", f->recipient_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name, + DBUS_ERROR_ACCESS_DENIED); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.CannotSend", "UnicastSignal3", "", f->recipient_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name, + DBUS_ERROR_ACCESS_DENIED); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + g_assert (m == NULL); +} + +static void +test_method_call (Fixture *f, + gconstpointer context) +{ + DBusMessage *m; + + if (f->address == NULL) + return; + + become_monitor (f); + + m = dbus_message_new_method_call (f->recipient_name, "/foo", "com.example.bar", + "Call1"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + while (g_queue_get_length (&f->monitored) < 2) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_method_call (m, f->sender_name, f->recipient_name, "/foo", + "com.example.bar", "Call1", ""); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_error_reply (m, f->recipient_name, f->sender_name, + DBUS_ERROR_UNKNOWN_METHOD); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + g_assert (m == NULL); +} + +static void +test_forbidden_method_call (Fixture *f, + gconstpointer context) +{ + DBusMessage *m; + + if (f->address == NULL) + return; + + become_monitor (f); + + m = dbus_message_new_method_call (f->recipient_name, "/foo", + "com.example.CannotSend", "Call1"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + while (g_queue_get_length (&f->monitored) < 2) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_method_call (m, f->sender_name, f->recipient_name, "/foo", + "com.example.CannotSend", "Call1", ""); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name, + DBUS_ERROR_ACCESS_DENIED); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + g_assert (m == NULL); + + m = dbus_message_new_method_call (f->recipient_name, "/foo", + "com.example.CannotReceive", "Call2"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + while (g_queue_get_length (&f->monitored) < 2) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_method_call (m, f->sender_name, f->recipient_name, "/foo", + "com.example.CannotReceive", "Call2", ""); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name, + DBUS_ERROR_ACCESS_DENIED); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + g_assert (m == NULL); +} + +static void +test_dbus_daemon (Fixture *f, + gconstpointer context) +{ + DBusMessage *m; + int res; + + if (f->address == NULL) + return; + + become_monitor (f); + + res = dbus_bus_request_name (f->sender, "com.example.Sender", + DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e); + test_assert_no_error (&f->e); + g_assert_cmpint (res, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); + + res = dbus_bus_release_name (f->sender, "com.example.Sender", &f->e); + test_assert_no_error (&f->e); + g_assert_cmpint (res, ==, DBUS_RELEASE_NAME_REPLY_RELEASED); + + while (g_queue_get_length (&f->monitored) < 8) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_method_call (m, f->sender_name, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, "RequestName", "su"); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, + "NameOwnerChanged", "sss", NULL); + dbus_message_unref (m); + + /* FIXME: should we get this? */ + m = g_queue_pop_head (&f->monitored); + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, + "NameAcquired", "s", f->sender_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_method_reply (m, DBUS_SERVICE_DBUS, f->sender_name, "u"); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_method_call (m, f->sender_name, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, "ReleaseName", "s"); + dbus_message_unref (m); + + /* FIXME: should we get this? */ + m = g_queue_pop_head (&f->monitored); + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, + "NameLost", "s", f->sender_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, + "NameOwnerChanged", "sss", NULL); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_method_reply (m, DBUS_SERVICE_DBUS, f->sender_name, "u"); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + g_assert (m == NULL); +} + +static void +test_selective (Fixture *f, + gconstpointer context) +{ + DBusMessage *m; + + if (f->address == NULL) + return; + + become_monitor (f); + + m = dbus_message_new_signal ("/foo", "com.example.Interesting", + "UnicastSignal1"); + if (!dbus_message_set_destination (m, f->recipient_name)) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + m = dbus_message_new_signal ("/foo", "com.example.Tedious", + "UnicastSignal2"); + if (!dbus_message_set_destination (m, f->recipient_name)) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + m = dbus_message_new_signal ("/foo", "com.example.Fun", + "UnicastSignal3"); + if (!dbus_message_set_destination (m, f->recipient_name)) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + while (g_queue_get_length (&f->monitored) < 2) + test_main_context_iterate (f->ctx, TRUE); + + /* We get the interesting signal and the fun signal, but not the tedious + * signal. */ + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.Interesting", "UnicastSignal1", "", f->recipient_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.Fun", "UnicastSignal3", "", f->recipient_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + g_assert (m == NULL); +} + +static void +expect_new_connection (Fixture *f) +{ + DBusMessage *m; + + while (g_queue_get_length (&f->monitored) < 4) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_hello (m); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_hello_reply (m); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, + "NameOwnerChanged", "sss", NULL); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_name_acquired (m); + dbus_message_unref (m); +} + +static void +take_well_known_name (Fixture *f, + DBusConnection *connection, + const char *name) +{ + int ret; + + ret = dbus_bus_request_name (connection, name, + DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e); + test_assert_no_error (&f->e); + g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); +} + +static void +expect_take_well_known_name (Fixture *f, + DBusConnection *connection, + const char *name) +{ + DBusMessage *m; + const char *connection_name = dbus_bus_get_unique_name (connection); + + while (g_queue_get_length (&f->monitored) < 4) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_method_call (m, connection_name, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, "RequestName", "su"); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, + "NameOwnerChanged", "sss", NULL); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, + "NameAcquired", "s", connection_name); + dbus_message_unref (m); + + m = g_queue_pop_head (&f->monitored); + assert_method_reply (m, DBUS_SERVICE_DBUS, connection_name, "u"); + dbus_message_unref (m); +} + +static void +test_activation (Fixture *f, + gconstpointer context) +{ + DBusMessage *m; + + if (f->address == NULL) + return; + + become_monitor (f); + + /* The sender sends a message to an activatable service. */ + m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal1"); + if (!dbus_message_set_destination (m, "com.example.SystemdActivatable1")) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + /* We observe the activation request, and the message that caused it, + * before systemd has even joined the bus. */ + while (g_queue_get_length (&f->monitored) < 2) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + "org.freedesktop.systemd1.Activator", "ActivationRequest", "s", + "org.freedesktop.systemd1"); + dbus_message_unref (m); + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.bar", "UnicastSignal1", "", + "com.example.SystemdActivatable1"); + dbus_message_unref (m); + + /* The fake systemd connects to the bus. */ + f->systemd = test_connect_to_bus (f->ctx, f->address); + if (!dbus_connection_add_filter (f->systemd, systemd_filter, f, NULL)) + g_error ("OOM"); + f->systemd_name = dbus_bus_get_unique_name (f->systemd); + + expect_new_connection (f); + take_well_known_name (f, f->systemd, "org.freedesktop.systemd1"); + expect_take_well_known_name (f, f->systemd, "org.freedesktop.systemd1"); + + /* It gets its activation request. */ + while (f->systemd_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->systemd_message; + f->systemd_message = NULL; + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + "org.freedesktop.systemd1.Activator", "ActivationRequest", "s", + "org.freedesktop.systemd1"); + dbus_message_unref (m); + + /* systemd starts the activatable service. */ + f->activated = test_connect_to_bus (f->ctx, f->address); + if (!dbus_connection_add_filter (f->activated, activated_filter, + f, NULL)) + g_error ("OOM"); + f->activated_name = dbus_bus_get_unique_name (f->activated); + + expect_new_connection (f); + take_well_known_name (f, f->activated, "com.example.SystemdActivatable1"); + expect_take_well_known_name (f, f->activated, + "com.example.SystemdActivatable1"); + + /* The message is delivered to the activatable service. */ + while (f->activated_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->activated_message; + f->activated_message = NULL; + assert_signal (m, f->sender_name, "/foo", + "com.example.bar", "UnicastSignal1", "", + "com.example.SystemdActivatable1"); + dbus_message_unref (m); + + /* The sender sends a message to a different activatable service. */ + m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal2"); + if (!dbus_message_set_destination (m, "com.example.SystemdActivatable2")) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + /* This time systemd is already ready for it. */ + while (g_queue_get_length (&f->monitored) < 2 || + f->systemd_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->systemd_message; + f->systemd_message = NULL; + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + "org.freedesktop.systemd1.Activator", "ActivationRequest", "s", + "org.freedesktop.systemd1"); + dbus_message_unref (m); + + /* The monitor sees the activation request and the signal that + * prompted it.*/ + m = g_queue_pop_head (&f->monitored); + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + "org.freedesktop.systemd1.Activator", "ActivationRequest", "s", + "org.freedesktop.systemd1"); + dbus_message_unref (m); + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.bar", "UnicastSignal2", "", + "com.example.SystemdActivatable2"); + dbus_message_unref (m); + + /* The activatable service takes its name. Here I'm faking it by using + * an existing connection. */ + take_well_known_name (f, f->activated, "com.example.SystemdActivatable2"); + + /* The message is delivered to the activatable service. + * Implementation detail: the monitor sees this happen before it even + * sees that the name request happened, which is pretty odd. */ + while (f->activated_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->activated_message; + f->activated_message = NULL; + assert_signal (m, f->sender_name, "/foo", + "com.example.bar", "UnicastSignal2", "", + "com.example.SystemdActivatable2"); + dbus_message_unref (m); + + expect_take_well_known_name (f, f->activated, + "com.example.SystemdActivatable2"); + + /* A third activation. */ + m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal3"); + if (!dbus_message_set_destination (m, "com.example.SystemdActivatable3")) + g_error ("OOM"); + dbus_connection_send (f->sender, m, NULL); + dbus_message_unref (m); + + /* Once again, we see the activation request and the reason. */ + while (g_queue_get_length (&f->monitored) < 2) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + "org.freedesktop.systemd1.Activator", "ActivationRequest", "s", + "org.freedesktop.systemd1"); + dbus_message_unref (m); + m = g_queue_pop_head (&f->monitored); + assert_signal (m, f->sender_name, "/foo", + "com.example.bar", "UnicastSignal3", "", + "com.example.SystemdActivatable3"); + dbus_message_unref (m); + + /* systemd gets the request too. */ + while (f->systemd_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->systemd_message; + f->systemd_message = NULL; + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + "org.freedesktop.systemd1.Activator", "ActivationRequest", "s", + "org.freedesktop.systemd1"); + dbus_message_unref (m); + + /* This time activation fails */ + m = dbus_message_new_signal ("/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Activator", "ActivationFailure"); + + do + { + const char *unit = "dbus-com.example.SystemdActivatable3.service"; + const char *error_name = "com.example.Nope"; + const char *error_message = "Computer says no"; + + if (!dbus_message_append_args (m, + DBUS_TYPE_STRING, &unit, + DBUS_TYPE_STRING, &error_name, + DBUS_TYPE_STRING, &error_message, + DBUS_TYPE_INVALID)) + g_error ("OOM"); + } + while (0); + + if (!dbus_message_set_destination (m, "org.freedesktop.DBus")) + g_error ("OOM"); + dbus_connection_send (f->systemd, m, NULL); + dbus_message_unref (m); + + /* The monitor sees activation fail */ + + /* Once again, we see the activation request and the reason. */ + while (g_queue_get_length (&f->monitored) < 1) + test_main_context_iterate (f->ctx, TRUE); + + m = g_queue_pop_head (&f->monitored); + assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name, + "com.example.Nope"); + dbus_message_unref (m); +} + +static void +teardown (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + dbus_error_free (&f->e); + g_clear_error (&f->ge); + + if (f->monitor != NULL) + { + dbus_connection_remove_filter (f->monitor, monitor_filter, f); + dbus_connection_close (f->monitor); + dbus_connection_unref (f->monitor); + f->monitor = NULL; + } + + if (f->sender != NULL) + { + dbus_connection_close (f->sender); + dbus_connection_unref (f->sender); + f->sender = NULL; + } + + if (f->recipient != NULL) + { + dbus_connection_remove_filter (f->recipient, recipient_filter, f); + dbus_connection_close (f->recipient); + dbus_connection_unref (f->recipient); + f->recipient = NULL; + } + + if (f->systemd != NULL) + { + dbus_connection_remove_filter (f->systemd, systemd_filter, f); + dbus_connection_close (f->systemd); + dbus_connection_unref (f->systemd); + f->systemd = NULL; + } + + if (f->activated != NULL) + { + dbus_connection_remove_filter (f->activated, activated_filter, f); + dbus_connection_close (f->activated); + dbus_connection_unref (f->activated); + f->activated = NULL; + } + + test_kill_pid (f->daemon_pid); + g_spawn_close_pid (f->daemon_pid); + + test_main_context_unref (f->ctx); + + g_queue_foreach (&f->monitored, (GFunc) dbus_message_unref, NULL); + g_queue_clear (&f->monitored); + + g_free (f->address); +} + +int +main (int argc, + char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id="); + + g_test_add ("/monitor/become", Fixture, &side_effects_config, + setup, test_become_monitor, teardown); + g_test_add ("/monitor/broadcast", Fixture, NULL, + setup, test_broadcast, teardown); + g_test_add ("/monitor/forbidden-broadcast", Fixture, &forbidding_config, + setup, test_forbidden_broadcast, teardown); + g_test_add ("/monitor/unicast-signal", Fixture, NULL, + setup, test_unicast_signal, teardown); + g_test_add ("/monitor/forbidden", Fixture, &forbidding_config, + setup, test_forbidden, teardown); + g_test_add ("/monitor/method-call", Fixture, NULL, + setup, test_method_call, teardown); + g_test_add ("/monitor/forbidden-method", Fixture, &forbidding_config, + setup, test_forbidden_method_call, teardown); + g_test_add ("/monitor/dbus-daemon", Fixture, NULL, + setup, test_dbus_daemon, teardown); + g_test_add ("/monitor/selective", Fixture, &selective_config, + setup, test_selective, teardown); + g_test_add ("/monitor/wildcard", Fixture, &wildcard_config, + setup, test_unicast_signal, teardown); + g_test_add ("/monitor/no-rule", Fixture, &no_rules_config, + setup, test_unicast_signal, teardown); + g_test_add ("/monitor/eavesdrop", Fixture, &eavesdrop_config, + setup, test_unicast_signal, teardown); + g_test_add ("/monitor/no-eavesdrop", Fixture, &no_eavesdrop_config, + setup, test_unicast_signal, teardown); + g_test_add ("/monitor/activation", Fixture, &fake_systemd_config, + setup, test_activation, teardown); + + return g_test_run (); +} -- 2.1.4