dbus/test/monitor.c
Simon McVittie 093ec67b8f bus/driver: Make non-core interfaces unavailable on most object paths
The o.fd.DBus interface needs to remain available on arbitrary object
paths for backwards compatibility, and the Introspectable interface
is genuinely useful, but everything else can be skipped.

This is arguably an incompatible change for the undocumented Verbose
interface, and for the GetAllMatchRules method on the undocumented
Stats interface: previously those were available at all object paths.

Reviewed-by: Philip Withnall <withnall@endlessm.com>
[smcv: Adjust comments, enum order, variable naming as per Philip's review]
Signed-off-by: Simon McVittie <smcv@collabora.com>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=101256
2017-06-02 10:43:29 +01:00

1878 lines
56 KiB
C

/* Integration tests for monitor-mode D-Bus connections
*
* Copyright © 2010-2011 Nokia Corporation
* Copyright © 2015 Collabora Ltd.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <config.h>
#include <string.h>
#include "test-utils-glib.h"
typedef struct {
const char *config_file;
const char * const *match_rules;
gboolean care_about_our_names;
} Config;
typedef struct {
const Config *config;
TestMainContext *ctx;
DBusError e;
GError *ge;
gchar *address;
GPid daemon_pid;
DBusConnection *monitor;
DBusConnection *sender;
DBusConnection *recipient;
GQueue monitored;
const char *monitor_name;
const char *sender_name;
const char *recipient_name;
DBusConnection *systemd;
const char *systemd_name;
DBusMessage *systemd_message;
DBusConnection *activated;
const char *activated_name;
DBusMessage *activated_message;
} Fixture;
static const char * const no_match_rules[] = {
NULL
};
static const char * const wildcard_match_rules[] = {
"",
NULL,
FALSE
};
static const char * const eavesdrop_match_rules[] = {
"eavesdrop=true",
NULL,
FALSE
};
static const char * const no_eavesdrop_match_rules[] = {
"eavesdrop=false",
NULL,
FALSE
};
static const char * const selective_match_rules[] = {
"interface='com.example.Interesting'",
"interface='com.example.Fun'",
NULL,
FALSE
};
static 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_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
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_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);
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);
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",
"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);
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;
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);
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, 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_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_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 ();
}