/* 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; gboolean recipient_enqueue_filter_added; GQueue monitored; GQueue received; 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 const char * const well_known_destination_match_rules[] = { "destination='com.example.Recipient'", NULL }; 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 well_known_destination_config = { NULL, well_known_destination_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 }; #ifdef DBUS_UNIX static Config fake_systemd_config = { "valid-config-files/systemd-activation.conf", NULL, FALSE }; #endif 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_test_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_test_message ("\tfrom: %s", not_null2 (dbus_message_get_sender (m), "(dbus-daemon)")); g_test_message ("\tto: %s", not_null2 (dbus_message_get_destination (m), "(broadcast)")); g_test_message ("\tpath: %s", not_null (dbus_message_get_path (m))); g_test_message ("\tinterface: %s", not_null (dbus_message_get_interface (m))); g_test_message ("\tmember: %s", not_null (dbus_message_get_member (m))); g_test_message ("\tsignature: %s", not_null (dbus_message_get_signature (m))); g_test_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_test_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_check_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 recipient_enqueue_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") || dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } g_queue_push_tail (&f->received, dbus_message_ref (message)); 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 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 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, NULL, &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_check_filter, f, NULL)) g_error ("OOM"); } static void become_monitor (Fixture *f, const Config *config) { DBusMessage *m; DBusPendingCall *pc; dbus_bool_t ok; DBusMessageIter appender, array_appender; const char * const *match_rules; int i; dbus_uint32_t zero = 0; dbus_connection_set_route_peer_messages (f->monitor, TRUE); if (config == NULL) config = f->config; if (config != NULL && config->match_rules != NULL) match_rules = 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 what happens if the method call arguments are invalid. */ static void test_invalid (Fixture *f, gconstpointer context) { DBusMessage *m; DBusPendingCall *pc; dbus_bool_t ok; DBusMessageIter appender, array_appender; dbus_uint32_t zero = 0; dbus_uint32_t invalid_flags = G_MAXUINT32; const char *s; if (f->address == NULL) return; dbus_connection_set_route_peer_messages (f->monitor, TRUE); /* Try to become a monitor but specify nonzero flags - not allowed */ 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"); if (!dbus_message_iter_close_container (&appender, &array_appender) || !dbus_message_iter_append_basic (&appender, DBUS_TYPE_UINT32, &invalid_flags)) 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); g_assert_cmpint (dbus_message_get_type (m), ==, DBUS_MESSAGE_TYPE_ERROR); g_assert_cmpstr (dbus_message_get_error_name (m), ==, DBUS_ERROR_INVALID_ARGS); /* Try to become a monitor but use the wrong object path - not allowed * (security hardening against inappropriate XML policy rules) */ dbus_pending_call_unref (pc); dbus_message_unref (m); m = dbus_message_new_method_call (DBUS_SERVICE_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"); 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); g_assert_cmpint (dbus_message_get_type (m), ==, DBUS_MESSAGE_TYPE_ERROR); g_assert_cmpstr (dbus_message_get_error_name (m), ==, DBUS_ERROR_UNKNOWN_INTERFACE); /* Try to become a monitor but specify a bad match rule - * also not allowed */ dbus_pending_call_unref (pc); dbus_message_unref (m); 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"); /* Syntactically incorrect match rule taken from #92298 - was probably * intended to be path='/modules/...' */ s = "interface='org.kde.walletd',member='/modules/kwalletd/org.kde.KWallet/walletOpened'"; if (!dbus_message_iter_append_basic (&array_appender, DBUS_TYPE_STRING, &s) || !dbus_message_iter_close_container (&appender, &array_appender) || !dbus_message_iter_append_basic (&appender, DBUS_TYPE_UINT32, &zero) || !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); g_assert_cmpint (dbus_message_get_type (m), ==, DBUS_MESSAGE_TYPE_ERROR); g_assert_cmpstr (dbus_message_get_error_name (m), ==, DBUS_ERROR_MATCH_RULE_INVALID); dbus_pending_call_unref (pc); dbus_message_unref (m); /* We did not become a monitor, so we can still call methods. */ pc = NULL; m = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetId"); if (m == NULL) 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); if (dbus_set_error_from_message (&f->e, m)) g_error ("%s: %s", f->e.name, f->e.message); ok = dbus_message_get_args (m, &f->e, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID); test_assert_no_error (&f->e); g_assert (ok); g_assert_cmpstr (s, !=, NULL); g_assert_cmpstr (s, !=, ""); dbus_pending_call_unref (pc); dbus_message_unref (m); } /* * 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) { if (g_queue_is_empty (&f->monitored)) 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, NULL); while (!lost_unique || !lost_a || !lost_b || !lost_c) { if (g_queue_is_empty (&f->monitored)) 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) { if (g_queue_is_empty (&f->monitored)) 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, NULL); 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; dbus_bus_add_match (f->recipient, "type='signal'", &f->e); test_assert_no_error (&f->e); if (!dbus_connection_add_filter (f->recipient, recipient_enqueue_filter, f, NULL)) g_error ("OOM"); f->recipient_enqueue_filter_added = TRUE; become_monitor (f, NULL); 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); m = dbus_message_new_signal ("/foo", "com.example.CannotBroadcast", "CannotBroadcast"); dbus_connection_send (f->sender, m, NULL); dbus_message_unref (m); m = dbus_message_new_signal ("/foo", "com.example.CannotBroadcast2", "CannotBroadcast2"); dbus_connection_send (f->sender, m, NULL); dbus_message_unref (m); /* these two will go through: we use them as an indirect way to assert that * the recipient has not received anything earlier */ m = dbus_message_new_signal ("/foo", "com.example.CannotUnicast", "CannotUnicast"); dbus_connection_send (f->sender, m, NULL); dbus_message_unref (m); m = dbus_message_new_signal ("/foo", "com.example.CannotUnicast2", "CannotUnicast2"); dbus_connection_send (f->sender, m, NULL); dbus_message_unref (m); while (g_queue_get_length (&f->monitored) < 12) 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); 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", "BroadcastSignal2", "", NULL); 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", "BroadcastSignal3", "", NULL); 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.CannotBroadcast", "CannotBroadcast", "", NULL); 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.CannotBroadcast2", "CannotBroadcast2", "", NULL); 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.CannotUnicast", "CannotUnicast", "", NULL); dbus_message_unref (m); m = g_queue_pop_head (&f->monitored); assert_signal (m, f->sender_name, "/foo", "com.example.CannotUnicast2", "CannotUnicast2", "", NULL); dbus_message_unref (m); m = g_queue_pop_head (&f->monitored); g_assert (m == NULL); /* the intended recipient only received the ones that were on the interface * where broadcasts are allowed */ while (g_queue_get_length (&f->received) < 2) test_main_context_iterate (f->ctx, TRUE); m = g_queue_pop_head (&f->received); assert_signal (m, f->sender_name, "/foo", "com.example.CannotUnicast", "CannotUnicast", "", NULL); dbus_message_unref (m); m = g_queue_pop_head (&f->received); assert_signal (m, f->sender_name, "/foo", "com.example.CannotUnicast2", "CannotUnicast2", "", NULL); dbus_message_unref (m); m = g_queue_pop_head (&f->received); g_assert (m == NULL); } static void test_unicast_signal (Fixture *f, gconstpointer context) { DBusMessage *m; if (f->address == NULL) return; become_monitor (f, NULL); 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; if (!dbus_connection_add_filter (f->recipient, recipient_enqueue_filter, f, NULL)) g_error ("OOM"); f->recipient_enqueue_filter_added = TRUE; become_monitor (f, NULL); 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); m = dbus_message_new_signal ("/foo", "com.example.CannotUnicast", "CannotUnicast"); 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.CannotUnicast2", "CannotUnicast2"); if (!dbus_message_set_destination (m, f->recipient_name)) g_error ("OOM"); dbus_connection_send (f->sender, m, NULL); dbus_message_unref (m); /* these two will go through: we use them as an indirect way to assert that * the recipient has not received anything earlier */ m = dbus_message_new_signal ("/foo", "com.example.CannotBroadcast", "CannotBroadcast"); 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.CannotBroadcast2", "CannotBroadcast2"); 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) < 12) 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); assert_signal (m, f->sender_name, "/foo", "com.example.CannotUnicast", "CannotUnicast", "", 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.CannotUnicast2", "CannotUnicast2", "", 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.CannotBroadcast", "CannotBroadcast", "", f->recipient_name); dbus_message_unref (m); m = g_queue_pop_head (&f->monitored); assert_signal (m, f->sender_name, "/foo", "com.example.CannotBroadcast2", "CannotBroadcast2", "", f->recipient_name); dbus_message_unref (m); m = g_queue_pop_head (&f->monitored); g_assert (m == NULL); /* the intended recipient only received the ones that were on the interface * where unicasts are allowed */ while (g_queue_get_length (&f->received) < 2) test_main_context_iterate (f->ctx, TRUE); m = g_queue_pop_head (&f->received); assert_signal (m, f->sender_name, "/foo", "com.example.CannotBroadcast", "CannotBroadcast", "", f->recipient_name); dbus_message_unref (m); m = g_queue_pop_head (&f->received); assert_signal (m, f->sender_name, "/foo", "com.example.CannotBroadcast2", "CannotBroadcast2", "", f->recipient_name); dbus_message_unref (m); m = g_queue_pop_head (&f->received); g_assert (m == NULL); } static void test_method_call (Fixture *f, gconstpointer context) { DBusMessage *m; if (f->address == NULL) return; become_monitor (f, NULL); /* regression test for * https://bugs.freedesktop.org/show_bug.cgi?id=90952 */ m = dbus_message_new_method_call (f->recipient_name, "/foo", DBUS_INTERFACE_PEER, "Ping"); 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", DBUS_INTERFACE_PEER, "Ping", ""); dbus_message_unref (m); m = g_queue_pop_head (&f->monitored); assert_method_reply (m, f->recipient_name, f->sender_name, ""); 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.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, NULL); 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, NULL); 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; /* Match rules added before becoming a monitor should be cleared: * if they weren't, this test would get Interesting twice, then Tedious, * and only see Fun after that. */ dbus_bus_add_match (f->monitor, "eavesdrop='true',interface='com.example.Interesting'", &f->e); test_assert_no_error (&f->e); dbus_bus_add_match (f->monitor, "eavesdrop='true',interface='com.example.Tedious'", &f->e); test_assert_no_error (&f->e); become_monitor (f, NULL); 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 test_well_known_destination (Fixture *f, gconstpointer context) { DBusMessage *m; if (f->address == NULL) return; take_well_known_name (f, f->recipient, "com.example.Recipient"); /* we don't expect_take_well_known_name here because the * monitor isn't up yet */ become_monitor (f, NULL); /* The sender sends a message to itself. It will not be observed. */ m = dbus_message_new_signal ("/foo", "com.example.bar", "Unobserved"); if (!dbus_message_set_destination (m, f->sender_name)) g_error ("OOM"); dbus_connection_send (f->sender, m, NULL); dbus_message_unref (m); /* The sender sends a message to the recipient by well-known name. * It will be observed. */ m = dbus_message_new_signal ("/foo", "com.example.bar", "Observed1"); if (!dbus_message_set_destination (m, "com.example.Recipient")) g_error ("OOM"); dbus_connection_send (f->sender, m, NULL); dbus_message_unref (m); /* The sender sends a message to the recipient by unique name. * It will still be observed. */ m = dbus_message_new_signal ("/foo", "com.example.bar", "Observed2"); 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); m = g_queue_pop_head (&f->monitored); assert_signal (m, f->sender_name, "/foo", "com.example.bar", "Observed1", "", "com.example.Recipient"); dbus_message_unref (m); m = g_queue_pop_head (&f->monitored); assert_signal (m, f->sender_name, "/foo", "com.example.bar", "Observed2", "", f->recipient_name); dbus_message_unref (m); m = g_queue_pop_head (&f->monitored); g_assert (m == NULL); } static void test_unique_destination (Fixture *f, gconstpointer context) { DBusMessage *m; Config config = { NULL, NULL, /* match rules */ FALSE }; const gchar *match_rules[2] = { NULL, NULL }; gchar *rule; if (f->address == NULL) return; take_well_known_name (f, f->recipient, "com.example.Recipient"); /* we don't expect_take_well_known_name here because the * monitor isn't up yet */ rule = g_strdup_printf ("destination='%s'", f->recipient_name); /* free it later */ g_test_queue_free (rule); match_rules[0] = rule; config.match_rules = match_rules; become_monitor (f, &config); /* The sender sends a message to itself. It will not be observed. */ m = dbus_message_new_signal ("/foo", "com.example.bar", "Unobserved"); if (!dbus_message_set_destination (m, f->sender_name)) g_error ("OOM"); dbus_connection_send (f->sender, m, NULL); dbus_message_unref (m); /* The sender sends a message to the recipient by well-known name. * It will be observed. */ m = dbus_message_new_signal ("/foo", "com.example.bar", "Observed1"); if (!dbus_message_set_destination (m, "com.example.Recipient")) g_error ("OOM"); dbus_connection_send (f->sender, m, NULL); dbus_message_unref (m); /* The sender sends a message to the recipient by unique name. * It will still be observed. */ m = dbus_message_new_signal ("/foo", "com.example.bar", "Observed2"); 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); m = g_queue_pop_head (&f->monitored); assert_signal (m, f->sender_name, "/foo", "com.example.bar", "Observed1", "", "com.example.Recipient"); dbus_message_unref (m); m = g_queue_pop_head (&f->monitored); assert_signal (m, f->sender_name, "/foo", "com.example.bar", "Observed2", "", f->recipient_name); dbus_message_unref (m); m = g_queue_pop_head (&f->monitored); g_assert (m == NULL); } #ifdef DBUS_UNIX /* currently only used for the systemd activation test */ 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); } /* currently only used for the systemd activation test */ 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, NULL); /* 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); } #endif /* DBUS_UNIX */ 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_check_filter, f); if (f->recipient_enqueue_filter_added) dbus_connection_remove_filter (f->recipient, recipient_enqueue_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; } if (f->daemon_pid != 0) { test_kill_pid (f->daemon_pid); g_spawn_close_pid (f->daemon_pid); f->daemon_pid = 0; } test_main_context_unref (f->ctx); g_queue_foreach (&f->monitored, (GFunc) dbus_message_unref, NULL); g_queue_clear (&f->monitored); g_queue_foreach (&f->received, (GFunc) dbus_message_unref, NULL); g_queue_clear (&f->received); g_free (f->address); } int main (int argc, char **argv) { test_init (&argc, &argv); g_test_add ("/monitor/invalid", Fixture, NULL, setup, test_invalid, teardown); 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/well-known-destination", Fixture, &well_known_destination_config, setup, test_well_known_destination, teardown); g_test_add ("/monitor/unique-destination", Fixture, NULL, setup, test_unique_destination, 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); #ifdef DBUS_UNIX /* this relies on the systemd activation code path */ g_test_add ("/monitor/activation", Fixture, &fake_systemd_config, setup, test_activation, teardown); #endif return g_test_run (); }