#include <config.h>
#include "dispatch.h"
+#include "check.h"
#include "connection.h"
#include "driver.h"
#include "services.h"
#include "utils.h"
#include "bus.h"
#include "signals.h"
+#include "dispatch.h"
#include "test.h"
#include <dbus/dbus-internals.h>
+#include <dbus/dbus-connection-internal.h>
#include <dbus/dbus-misc.h>
#include <string.h>
* dbus_connection_open_private() does not block. */
#define TEST_DEBUG_PIPE "debug-pipe:name=test-server"
+static inline const char *
+nonnull (const char *maybe_null,
+ const char *if_null)
+{
+ return (maybe_null ? maybe_null : if_null);
+}
+
static dbus_bool_t
send_one_message (DBusConnection *connection,
BusContext *context,
BusTransaction *transaction,
DBusError *error)
{
- if (!bus_context_check_security_policy (context, transaction,
- sender,
- addressed_recipient,
- connection,
- message,
- NULL))
- return TRUE; /* silently don't send it */
+ DBusError stack_error = DBUS_ERROR_INIT;
+ BusDeferredMessage *deferred_message = NULL;
+ BusResult result;
+
+ result = bus_context_check_security_policy (context, transaction, sender, addressed_recipient,
+ connection, message, NULL, &stack_error, &deferred_message);
+
+ if (result == BUS_RESULT_FALSE)
+ {
+ if (!bus_transaction_capture_error_reply (transaction, &stack_error,
+ message))
+ {
+ bus_context_log (context, DBUS_SYSTEM_LOG_WARNING,
+ "broadcast rejected, but not enough "
+ "memory to tell monitors");
+ }
+
+ dbus_error_free (&stack_error);
+ return TRUE; /* don't send it but don't return an error either */
+ }
if (dbus_message_contains_unix_fds(message) &&
!dbus_connection_can_send_type(connection, DBUS_TYPE_UNIX_FD))
- return TRUE; /* silently don't send it */
+ {
+ dbus_set_error (&stack_error, DBUS_ERROR_NOT_SUPPORTED,
+ "broadcast cannot be delivered to %s (%s) because "
+ "it does not support receiving Unix fds",
+ bus_connection_get_name (connection),
+ bus_connection_get_loginfo (connection));
+
+ if (!bus_transaction_capture_error_reply (transaction, &stack_error,
+ message))
+ {
+ bus_context_log (context, DBUS_SYSTEM_LOG_WARNING,
+ "broadcast with Unix fd not delivered, but not "
+ "enough memory to tell monitors");
+ }
+
+ dbus_error_free (&stack_error);
+ return TRUE; /* don't send it but don't return an error either */
+ }
+
+ if (result == BUS_RESULT_LATER)
+ {
+ if (!bus_deferred_message_queue_at_recipient(deferred_message, transaction, FALSE, FALSE))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ return TRUE; /* pretend to have sent it */
+ }
if (!bus_transaction_send (transaction,
connection,
- message))
+ message, FALSE))
{
BUS_SET_OOM (error);
return FALSE;
return TRUE;
}
-dbus_bool_t
-bus_dispatch_matches (BusTransaction *transaction,
- DBusConnection *sender,
- DBusConnection *addressed_recipient,
- DBusMessage *message,
- DBusError *error)
+BusResult
+bus_dispatch_matches (BusTransaction *transaction,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusMessage *message,
+ BusDeferredMessage *dispatched_deferred_message,
+ DBusError *error)
{
DBusError tmp_error;
BusConnections *connections;
BusMatchmaker *matchmaker;
DBusList *link;
BusContext *context;
+ BusDeferredMessage *deferred_message = NULL;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
* or for signals with no particular recipient
*/
- _dbus_assert (sender == NULL || bus_connection_is_active (sender));
+ /*
+ * With deferred messages, dispatch may happen when there is no sender anymore.
+ * Policy rules involving the sender should be checked before, anyway.
+ */
+/* _dbus_assert (sender == NULL || bus_connection_is_active (sender)); */
_dbus_assert (dbus_message_get_sender (message) != NULL);
context = bus_transaction_get_context (transaction);
/* First, send the message to the addressed_recipient, if there is one. */
if (addressed_recipient != NULL)
{
- if (!bus_context_check_security_policy (context, transaction,
- sender, addressed_recipient,
- addressed_recipient,
- message, error))
- return FALSE;
+ BusResult result;
+ /* To maintain message order message needs to be appended at the recipient if there are already
+ * deferred messages and we are not doing deferred dispatch
+ */
+ if (dispatched_deferred_message == NULL && bus_connection_has_deferred_messages(addressed_recipient))
+ {
+ result = bus_context_check_security_policy(context, transaction,
+ sender, addressed_recipient, addressed_recipient, message, NULL, error,
+ &deferred_message);
+
+ if (deferred_message == NULL)
+ deferred_message = bus_deferred_message_new(message, sender,
+ addressed_recipient, addressed_recipient, result);
+ else
+ bus_deferred_message_ref(deferred_message);
+
+ if (deferred_message == NULL)
+ {
+ BUS_SET_OOM(error);
+ return BUS_RESULT_FALSE;
+ }
+
+ if (!bus_deferred_message_queue_at_recipient(deferred_message, transaction, TRUE, FALSE))
+ {
+ bus_deferred_message_unref(deferred_message);
+ BUS_SET_OOM(error);
+ return BUS_RESULT_FALSE;
+ }
+
+ bus_deferred_message_unref(deferred_message);
+ return BUS_RESULT_TRUE; /* pretend to have sent it */
+ }
+
+ if (dispatched_deferred_message != NULL)
+ {
+ result = bus_deferred_message_get_response(dispatched_deferred_message);
+ if (result == BUS_RESULT_TRUE)
+ {
+ /* if we know the result of policy check we still need to check if message limits
+ * are not exceeded. It is also required to add entry in expected replies list if
+ * this is a method call
+ */
+ if (!bus_deferred_message_check_message_limits(dispatched_deferred_message, error))
+ return BUS_RESULT_FALSE;
+
+ if (!bus_deferred_message_expect_method_reply(dispatched_deferred_message, transaction, error))
+ return BUS_RESULT_FALSE;
+ }
+ else if (result == BUS_RESULT_FALSE)
+ {
+ bus_deferred_message_create_error(dispatched_deferred_message, "Rejected message", error);
+ return BUS_RESULT_FALSE;
+ }
+ }
+ else
+ result = BUS_RESULT_LATER;
+
+ if (result == BUS_RESULT_LATER)
+ result = bus_context_check_security_policy(context, transaction,
+ sender, addressed_recipient, addressed_recipient, message, NULL, error,
+ &deferred_message);
+
+ switch (result)
+ {
+ case BUS_RESULT_TRUE:
+ break;
+ case BUS_RESULT_FALSE:
+ return BUS_RESULT_FALSE;
+ case BUS_RESULT_LATER:
+ {
+ BusDeferredMessageStatus status;
+
+ if (dispatched_deferred_message != NULL)
+ {
+ /* for deferred dispatch prepend message at the recipient */
+ if (!bus_deferred_message_queue_at_recipient(deferred_message, transaction, TRUE, TRUE))
+ {
+ BUS_SET_OOM(error);
+ return BUS_RESULT_FALSE;
+ }
+ return BUS_RESULT_TRUE; /* pretend to have sent it */
+ }
+
+ status = bus_deferred_message_get_status(deferred_message);
+
+ if (status & BUS_DEFERRED_MESSAGE_CHECK_SEND)
+ {
+ /* send rule result not available - disable dispatching messages from the sender */
+ bus_deferred_message_disable_sender(deferred_message);
+ return BUS_RESULT_LATER;
+ }
+ else if (status & BUS_DEFERRED_MESSAGE_CHECK_RECEIVE)
+ {
+ /* receive rule result not available - queue message at the recipient */
+ if (!bus_deferred_message_queue_at_recipient(deferred_message, transaction, TRUE, FALSE))
+ {
+ BUS_SET_OOM(error);
+ return BUS_RESULT_FALSE;
+ }
+
+ return BUS_RESULT_TRUE; /* pretend to have sent it */
+ }
+ else
+ {
+ _dbus_verbose("deferred message has no status field set unexpectedly\n");
+ return BUS_RESULT_FALSE;
+ }
+ }
+ }
if (dbus_message_contains_unix_fds (message) &&
!dbus_connection_can_send_type (addressed_recipient,
DBUS_ERROR_NOT_SUPPORTED,
"Tried to send message with Unix file descriptors"
"to a client that doesn't support that.");
- return FALSE;
- }
+ return BUS_RESULT_FALSE;
+ }
/* Dispatch the message */
- if (!bus_transaction_send (transaction, addressed_recipient, message))
+ if (!bus_transaction_send(transaction, addressed_recipient, message,
+ dispatched_deferred_message != NULL ? TRUE : FALSE))
{
BUS_SET_OOM (error);
- return FALSE;
+ return BUS_RESULT_FALSE;
}
}
/* Now dispatch to others who look interested in this message */
- connections = bus_transaction_get_connections (transaction);
+ connections = bus_context_get_connections (context);
dbus_error_init (&tmp_error);
matchmaker = bus_context_get_matchmaker (context);
&recipients))
{
BUS_SET_OOM (error);
- return FALSE;
+ return BUS_RESULT_FALSE;
}
link = _dbus_list_get_first_link (&recipients);
if (dbus_error_is_set (&tmp_error))
{
dbus_move_error (&tmp_error, error);
- return FALSE;
+ return BUS_RESULT_FALSE;
}
else
- return TRUE;
+ return BUS_RESULT_TRUE;
}
static DBusHandlerResult
/* Ref connection in case we disconnect it at some point in here */
dbus_connection_ref (connection);
+ /* Monitors aren't meant to send messages to us. */
+ if (bus_connection_is_monitor (connection))
+ {
+ sender = bus_connection_get_name (connection);
+
+ /* should never happen */
+ if (sender == NULL)
+ sender = "(unknown)";
+
+ if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_LOCAL,
+ "Disconnected"))
+ {
+ bus_context_log (context, DBUS_SYSTEM_LOG_INFO,
+ "Monitoring connection %s closed.", sender);
+ bus_connection_disconnected (connection);
+ goto out;
+ }
+ else
+ {
+ /* Monitors are not allowed to send messages, because that
+ * probably indicates that the monitor is incorrectly replying
+ * to its eavesdropped messages, and we want the authors of
+ * such monitors to fix them.
+ */
+ bus_context_log (context, DBUS_SYSTEM_LOG_WARNING,
+ "Monitoring connection %s (%s) is not allowed "
+ "to send messages; closing it. Please fix the "
+ "monitor to not do that. "
+ "(message type=\"%s\" interface=\"%s\" "
+ "member=\"%s\" error name=\"%s\" "
+ "destination=\"%s\")",
+ sender, bus_connection_get_loginfo (connection),
+ dbus_message_type_to_string (
+ dbus_message_get_type (message)),
+ nonnull (dbus_message_get_interface (message),
+ "(unset)"),
+ nonnull (dbus_message_get_member (message),
+ "(unset)"),
+ nonnull (dbus_message_get_error_name (message),
+ "(unset)"),
+ nonnull (dbus_message_get_destination (message),
+ DBUS_SERVICE_DBUS));
+ dbus_connection_close (connection);
+ goto out;
+ }
+ }
+
service_name = dbus_message_get_destination (message);
#ifdef DBUS_ENABLE_VERBOSE_MODE
{
/* DBusConnection also handles some of these automatically, we leave
* it to do so.
+ *
+ * FIXME: this means monitors won't get the opportunity to see
+ * non-signals with NULL destination, or their replies (which in
+ * practice are UnknownMethod errors)
*/
result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
goto out;
BUS_SET_OOM (&error);
goto out;
}
+ }
+ else
+ {
+ /* For monitors' benefit: we don't want the sender to be able to
+ * trick the monitor by supplying a forged sender, and we also
+ * don't want the message to have no sender at all. */
+ if (!dbus_message_set_sender (message, ":not.active.yet"))
+ {
+ BUS_SET_OOM (&error);
+ goto out;
+ }
+ }
- /* We need to refetch the service name here, because
- * dbus_message_set_sender can cause the header to be
- * reallocated, and thus the service_name pointer will become
- * invalid.
- */
- service_name = dbus_message_get_destination (message);
+ /* We need to refetch the service name here, because
+ * dbus_message_set_sender can cause the header to be
+ * reallocated, and thus the service_name pointer will become
+ * invalid.
+ */
+ service_name = dbus_message_get_destination (message);
+
+ if (!bus_transaction_capture (transaction, connection, message))
+ {
+ BUS_SET_OOM (&error);
+ goto out;
}
if (service_name &&
strcmp (service_name, DBUS_SERVICE_DBUS) == 0) /* to bus driver */
{
- if (!bus_context_check_security_policy (context, transaction,
- connection, NULL, NULL, message, &error))
+ BusDeferredMessage *deferred_message = NULL;
+
+ switch (bus_context_check_security_policy (context, transaction,
+ connection, NULL, NULL, message,
+ NULL, &error,
+ &deferred_message))
{
+ case BUS_RESULT_TRUE:
+ break;
+ case BUS_RESULT_FALSE:
_dbus_verbose ("Security policy rejected message\n");
goto out;
+ case BUS_RESULT_LATER:
+ /* Disable dispatching messages from the sender,
+ * roll back and dispatch the message once the policy result is available */
+ bus_deferred_message_disable_sender(deferred_message);
+ bus_transaction_cancel_and_free (transaction);
+ transaction = NULL;
+ result = DBUS_HANDLER_RESULT_LATER;
+ goto out;
}
_dbus_verbose ("Giving message to %s\n", DBUS_SERVICE_DBUS);
- if (!bus_driver_handle_message (connection, transaction, message, &error))
- goto out;
+ switch (bus_driver_handle_message (connection, transaction, message, &error))
+ {
+ case BUS_RESULT_TRUE:
+ break;
+ case BUS_RESULT_FALSE:
+ goto out;
+ case BUS_RESULT_LATER:
+ bus_transaction_cancel_and_free (transaction);
+ transaction = NULL;
+ result = DBUS_HANDLER_RESULT_LATER;
+ goto out;
+ }
}
else if (!bus_connection_is_active (connection)) /* clients must talk to bus driver first */
{
if (service == NULL && dbus_message_get_auto_start (message))
{
BusActivation *activation;
- /* We can't do the security policy check here, since the addressed
- * recipient service doesn't exist yet. We do it before sending the
- * message after the service has been created.
- */
+ BusDeferredMessage *deferred_message = NULL;
+
activation = bus_connection_get_activation (connection);
- if (!bus_activation_activate_service (activation, connection, transaction, TRUE,
- message, service_name, &error))
+ /* This will do as much of a security policy check as it can.
+ * We can't do the full security policy check here, since the
+ * addressed recipient service doesn't exist yet. We do it before
+ * sending the message after the service has been created.
+ */
+ switch (bus_activation_activate_service (activation, connection, transaction, TRUE,
+ message, service_name, &error,
+ &deferred_message))
{
+ case BUS_RESULT_FALSE:
_DBUS_ASSERT_ERROR_IS_SET (&error);
_dbus_verbose ("bus_activation_activate_service() failed: %s\n", error.name);
- goto out;
+ break;
+ case BUS_RESULT_LATER:
+ bus_deferred_message_disable_sender(deferred_message);
+ bus_transaction_cancel_and_free (transaction);
+ transaction = NULL;
+ result = DBUS_HANDLER_RESULT_LATER;
+ break;
+ case BUS_RESULT_TRUE:
+ break;
}
goto out;
* addressed_recipient == NULL), and match it against other connections'
* match rules.
*/
- if (!bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
- goto out;
+ switch (bus_dispatch_matches (transaction, connection, addressed_recipient, message, NULL, &error))
+ {
+ case BUS_RESULT_TRUE:
+ case BUS_RESULT_FALSE:
+ break;
+ case BUS_RESULT_LATER:
+ /* Roll back and dispatch the message once the policy result is available */
+ bus_transaction_cancel_and_free (transaction);
+ transaction = NULL;
+ result = DBUS_HANDLER_RESULT_LATER;
+ break;
+ }
out:
if (dbus_error_is_set (&error))
{
- if (!dbus_connection_get_is_connected (connection))
- {
- /* If we disconnected it, we won't bother to send it any error
- * messages.
- */
- _dbus_verbose ("Not sending error to connection we disconnected\n");
- }
- else if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ /* Even if we disconnected it, pretend to send it any pending error
+ * messages so that monitors can observe them.
+ */
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
bus_connection_send_oom_error (connection, message);
#include <stdio.h>
+#include "stats.h"
+
/* This is used to know whether we need to block in order to finish
* sending a message, or whether the initial dbus_connection_send()
* already flushed the queue.
const char *expected_service_name;
dbus_bool_t failed;
DBusConnection *skip_connection;
+ BusContext *context;
} CheckServiceOwnerChangedData;
static dbus_bool_t
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
- _dbus_warn ("Did not receive a message on %p, expecting %s\n",
- connection, "NameOwnerChanged");
- goto out;
+ block_connection_until_message_from_bus (d->context, connection, "NameOwnerChanged");
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a message on %p, expecting %s\n",
+ connection, "NameOwnerChanged");
+ goto out;
+ }
}
else if (!dbus_message_is_signal (message,
DBUS_INTERFACE_DBUS,
socd.expected_service_name = base_service;
socd.failed = FALSE;
socd.skip_connection = NULL;
+ socd.context = context;
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
return TRUE;
}
- dbus_connection_unref (connection);
-
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
socd.expected_service_name = name;
socd.failed = FALSE;
socd.skip_connection = connection; /* we haven't done AddMatch so won't get it ourselves */
+ socd.context = context;
+
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
- _dbus_warn ("Expecting %s, got nothing\n",
+ block_connection_until_message_from_bus (context, connection, "signal NameAcquired");
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Expecting %s, got nothing\n",
"NameAcquired");
- goto out;
+ goto out;
+ }
}
if (! dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
"NameAcquired"))
if (name_message)
dbus_message_unref (name_message);
+ dbus_connection_unref (connection);
+
return retval;
}
{
; /* good, this is a valid response */
}
+#ifdef DBUS_WIN
+ else if (dbus_message_is_error (message, DBUS_ERROR_FAILED))
+ {
+ /* this is OK, Unix uids aren't meaningful on Windows */
+ }
+#endif
else
{
warn_unexpected (connection, message, "not this error");
dbus_bool_t retval;
DBusError error;
const char *base_service_name;
+#ifdef DBUS_UNIX
dbus_uint32_t pid;
+#endif
retval = FALSE;
dbus_error_init (&error);
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__linux__) || \
defined(__OpenBSD__)
+ /* In principle NetBSD should also be in that list, but
+ * its implementation of PID-passing doesn't work
+ * over a socketpair() as used in the debug-pipe transport.
+ * We test this functionality in a more realistic situation
+ * in test/dbus-daemon.c. */
warn_unexpected (connection, message, "not this error");
goto out;
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
-check_add_match_all (BusContext *context,
- DBusConnection *connection)
+check_add_match (BusContext *context,
+ DBusConnection *connection,
+ const char *rule)
{
DBusMessage *message;
dbus_bool_t retval;
dbus_uint32_t serial;
DBusError error;
- const char *empty = "";
retval = FALSE;
dbus_error_init (&error);
message = NULL;
- _dbus_verbose ("check_add_match_all for %p\n", connection);
+ _dbus_verbose ("check_add_match for connection %p, rule %s\n",
+ connection, rule);
message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
if (message == NULL)
return TRUE;
- /* empty string match rule matches everything */
- if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &empty,
+ if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &rule,
DBUS_TYPE_INVALID))
{
dbus_message_unref (message);
return retval;
}
+#ifdef DBUS_ENABLE_STATS
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_get_all_match_rules (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_bool_t retval;
+ dbus_uint32_t serial;
+ DBusError error;
+
+ retval = FALSE;
+ dbus_error_init (&error);
+ message = NULL;
+
+ _dbus_verbose ("check_get_all_match_rules for connection %p\n",
+ connection);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ BUS_INTERFACE_STATS,
+ "GetAllMatchRules");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ dbus_connection_ref (connection); /* because we may get disconnected */
+
+ /* send our message */
+ bus_test_run_clients_loop (SEND_PENDING (connection));
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected\n");
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ block_connection_until_message_from_bus (context, connection, "reply to AddMatch");
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected\n");
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ dbus_connection_unref (connection);
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "AddMatch", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
+ {
+ ; /* good, expected */
+ _dbus_assert (dbus_message_get_reply_serial (message) == serial);
+ }
+ else
+ {
+ warn_unexpected (connection, message, "method return for AddMatch");
+
+ goto out;
+ }
+ }
+
+ if (!check_no_leftovers (context))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ dbus_error_free (&error);
+
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+#endif
+
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
}
else
{
- if (!check_add_match_all (context, connection))
+ if (!check_add_match (context, connection, ""))
return FALSE;
kill_client_connection (context, connection);
socd.expected_service_name = base_service;
socd.failed = FALSE;
socd.skip_connection = connection;
+ socd.context = context;
+
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
socd.skip_connection = connection;
socd.failed = FALSE;
socd.expected_service_name = service_name;
+ socd.context = context;
+
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
socd.expected_service_name = service_name;
socd.failed = FALSE;
socd.skip_connection = connection;
+ socd.context = context;
+
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
socd.expected_service_name = activated_name;
socd.failed = FALSE;
socd.skip_connection = NULL;
+ socd.context = context;
+
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
socd.expected_service_name = base_service;
socd.failed = FALSE;
socd.skip_connection = NULL;
+ socd.context = context;
+
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
socd.expected_service_name = base_service;
socd.failed = FALSE;
socd.skip_connection = NULL;
+ socd.context = context;
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
return retval;
}
-#ifndef DBUS_WIN_FIXME
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
/* make sure this only happens with the launch helper */
_dbus_assert (servicehelper != NULL);
}
+#ifdef DBUS_WIN
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_CHILD_EXITED))
+ {
+ /* unhandled exceptions are normal exit codes */
+ }
+#else
else if (dbus_message_is_error (message,
DBUS_ERROR_SPAWN_CHILD_SIGNALED))
{
; /* good, this is expected also */
}
+#endif
else
{
warn_unexpected (connection, message, "not this error");
{
; /* good, this is a valid response */
}
+#ifdef DBUS_WIN
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_CHILD_EXITED))
+ {
+ /* unhandled exceptions are normal exit codes */
+ }
+#else
else if (dbus_message_is_error (message,
DBUS_ERROR_SPAWN_CHILD_SIGNALED))
{
; /* good, this is expected also */
}
+#endif
else
{
warn_unexpected (connection, message, "not this error");
return retval;
}
-#endif
#define TEST_ECHO_MESSAGE "Test echo message"
#define TEST_RUN_HELLO_FROM_SELF_MESSAGE "Test sending message to self"
socd.expected_service_name = base_service;
socd.failed = FALSE;
socd.skip_connection = NULL;
+ socd.context = context;
+
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
socd.expected_service_name = base_service;
socd.failed = FALSE;
socd.skip_connection = NULL;
+ socd.context = context;
+
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
if (!check_double_hello_message (context, foo))
_dbus_assert_not_reached ("double hello message failed");
- if (!check_add_match_all (context, foo))
+ if (!check_add_match (context, foo, ""))
_dbus_assert_not_reached ("AddMatch message failed");
bar = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
if (!check_hello_message (context, bar))
_dbus_assert_not_reached ("hello message failed");
- if (!check_add_match_all (context, bar))
+ if (!check_add_match (context, bar, ""))
_dbus_assert_not_reached ("AddMatch message failed");
baz = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
if (!check_hello_message (context, baz))
_dbus_assert_not_reached ("hello message failed");
- if (!check_add_match_all (context, baz))
+ if (!check_add_match (context, baz, ""))
_dbus_assert_not_reached ("AddMatch message failed");
-#ifdef DBUS_WIN_FIXME
- _dbus_warn("TODO: testing of GetConnectionUnixUser message skipped for now\n");
- _dbus_warn("TODO: testing of GetConnectionUnixProcessID message skipped for now\n");
-#else
+ if (!check_add_match (context, baz, "interface='com.example'"))
+ _dbus_assert_not_reached ("AddMatch message failed");
+
+#ifdef DBUS_ENABLE_STATS
+ if (!check_get_all_match_rules (context, baz))
+ _dbus_assert_not_reached ("GetAllMatchRules message failed");
+#endif
+
if (!check_get_connection_unix_user (context, baz))
_dbus_assert_not_reached ("GetConnectionUnixUser message failed");
+#ifdef DBUS_WIN_FIXME
+ _dbus_verbose("TODO: testing of GetConnectionUnixProcessID message skipped for now\n");
+#else
if (!check_get_connection_unix_process_id (context, baz))
_dbus_assert_not_reached ("GetConnectionUnixProcessID message failed");
#endif
check2_try_iterations (context, foo, "nonexistent_service_no_auto_start",
check_nonexistent_service_no_auto_start);
-#ifdef DBUS_WIN_FIXME
- _dbus_warn("TODO: dispatch.c segfault_service_no_auto_start test\n");
-#else
check2_try_iterations (context, foo, "segfault_service_no_auto_start",
check_segfault_service_no_auto_start);
-#endif
check2_try_iterations (context, foo, "existent_service_no_auto_start",
check_existent_service_no_auto_start);
check2_try_iterations (context, foo, "nonexistent_service_auto_start",
check_nonexistent_service_auto_start);
-
-#ifdef DBUS_WIN_FIXME
- _dbus_warn("TODO: dispatch.c segfault_service_auto_start test\n");
-#else
/* only do the segfault test if we are not using the launcher */
check2_try_iterations (context, foo, "segfault_service_auto_start",
check_segfault_service_auto_start);
-#endif
/* only do the shell fail test if we are not using the launcher */
check2_try_iterations (context, foo, "shell_fail_service_auto_start",
if (!check_double_hello_message (context, foo))
_dbus_assert_not_reached ("double hello message failed");
- if (!check_add_match_all (context, foo))
+ if (!check_add_match (context, foo, ""))
_dbus_assert_not_reached ("AddMatch message failed");
/* this only tests the activation.c user check */
return TRUE;
}
+typedef struct {
+ DBusTimeout *timeout;
+ DBusConnection *connection;
+ dbus_bool_t timedout;
+ int check_counter;
+} BusTestCheckData;
+
+static BusTestCheckData *cdata;
+
+static dbus_bool_t
+bus_dispatch_test_check_timeout (void *data)
+{
+ _dbus_verbose ("timeout triggered - pretend that privilege check result is available\n");
+
+ /* should only happen once during the test */
+ _dbus_assert (!cdata->timedout);
+ cdata->timedout = TRUE;
+ _dbus_connection_enable_dispatch (cdata->connection);
+
+ /* don't call this again */
+ _dbus_loop_remove_timeout (bus_connection_get_loop (cdata->connection),
+ cdata->timeout);
+ dbus_connection_unref (cdata->connection);
+ cdata->connection = NULL;
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_dispatch_test_check_override (DBusConnection *connection,
+ const char *privilege)
+{
+ _dbus_verbose ("overriding privilege check %s #%d\n", privilege, cdata->check_counter);
+ cdata->check_counter++;
+ if (!cdata->timedout)
+ {
+ dbus_bool_t added;
+
+ /* Should be the first privilege check for the "Echo" method. */
+ _dbus_assert (cdata->check_counter == 1);
+ cdata->timeout = _dbus_timeout_new (1, bus_dispatch_test_check_timeout,
+ NULL, NULL);
+ _dbus_assert (cdata->timeout);
+ added = _dbus_loop_add_timeout (bus_connection_get_loop (connection),
+ cdata->timeout);
+ _dbus_assert (added);
+ cdata->connection = connection;
+ dbus_connection_ref (connection);
+ _dbus_connection_disable_dispatch (connection);
+ return BUS_RESULT_LATER;
+ }
+ else
+ {
+ /* Should only be checked one more time, and this time succeeds. */
+ _dbus_assert (cdata->check_counter == 2);
+ return BUS_RESULT_TRUE;
+ }
+}
+
+static dbus_bool_t
+bus_dispatch_test_check (const DBusString *test_data_dir)
+{
+ const char *filename = "valid-config-files/debug-check-some.conf";
+ BusContext *context;
+ DBusConnection *foo;
+ DBusError error;
+ dbus_bool_t result = TRUE;
+ BusTestCheckData data;
+
+ /* save the config name for the activation helper */
+ if (!setenv_TEST_LAUNCH_HELPER_CONFIG (test_data_dir, filename))
+ _dbus_assert_not_reached ("no memory setting TEST_LAUNCH_HELPER_CONFIG");
+
+ dbus_error_init (&error);
+
+ context = bus_context_new_test (test_data_dir, filename);
+ if (context == NULL)
+ return FALSE;
+
+ foo = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
+ if (foo == NULL)
+ _dbus_assert_not_reached ("could not alloc connection");
+
+ if (!bus_setup_debug_client (foo))
+ _dbus_assert_not_reached ("could not set up connection");
+
+ spin_connection_until_authenticated (context, foo);
+
+ if (!check_hello_message (context, foo))
+ _dbus_assert_not_reached ("hello message failed");
+
+ if (!check_double_hello_message (context, foo))
+ _dbus_assert_not_reached ("double hello message failed");
+
+ if (!check_add_match_all (context, foo))
+ _dbus_assert_not_reached ("AddMatch message failed");
+
+ /*
+ * Cause bus_check_send_privilege() to return BUS_RESULT_LATER in the
+ * first call, then BUS_RESULT_TRUE.
+ */
+ cdata = &data;
+ memset (cdata, 0, sizeof(*cdata));
+ bus_check_test_override = bus_dispatch_test_check_override;
+
+ result = check_existent_service_auto_start (context, foo);
+
+ _dbus_assert (cdata->check_counter == 2);
+ _dbus_assert (cdata->timedout);
+ _dbus_assert (cdata->timeout);
+ _dbus_assert (!cdata->connection);
+ _dbus_timeout_unref (cdata->timeout);
+
+ kill_client_connection_unchecked (foo);
+
+ bus_context_unref (context);
+
+ return result;
+}
+
dbus_bool_t
bus_dispatch_test (const DBusString *test_data_dir)
{
+ _dbus_verbose ("<check> tests\n");
+ if (!bus_dispatch_test_check (test_data_dir))
+ return FALSE;
+
/* run normal activation tests */
_dbus_verbose ("Normal activation tests\n");
if (!bus_dispatch_test_conf (test_data_dir,
"valid-config-files/debug-allow-all.conf", FALSE))
return FALSE;
-#ifdef DBUS_WIN
- _dbus_warn("Info: Launch helper activation tests skipped because launch-helper is not supported yet\n");
-#else
+#ifndef DBUS_WIN
/* run launch-helper activation tests */
_dbus_verbose ("Launch helper activation tests\n");
if (!bus_dispatch_test_conf (test_data_dir,
if (!check_hello_message (context, foo))
_dbus_assert_not_reached ("hello message failed");
- if (!check_add_match_all (context, foo))
+ if (!check_add_match (context, foo, ""))
_dbus_assert_not_reached ("addmatch message failed");
if (!check_no_leftovers (context))
DBusConnection *foo, *bar;
DBusError error;
DBusMessage *m;
- int one[2], two[2], x, y, z;
+ DBusSocket one[2], two[2];
+ int x, y, z;
char r;
dbus_error_init (&error);
if (!check_hello_message (context, foo))
_dbus_assert_not_reached ("hello message failed");
- if (!check_add_match_all (context, foo))
+ if (!check_add_match (context, foo, ""))
_dbus_assert_not_reached ("AddMatch message failed");
bar = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
if (!check_hello_message (context, bar))
_dbus_assert_not_reached ("hello message failed");
- if (!check_add_match_all (context, bar))
+ if (!check_add_match (context, bar, ""))
_dbus_assert_not_reached ("AddMatch message failed");
if (!(m = dbus_message_new_signal("/", "a.b.c", "d")))
_dbus_assert_not_reached ("could not alloc message");
- if (!(_dbus_full_duplex_pipe(one, one+1, TRUE, &error)))
+ if (!(_dbus_socketpair (one, one+1, TRUE, &error)))
_dbus_assert_not_reached("Failed to allocate pipe #1");
- if (!(_dbus_full_duplex_pipe(two, two+1, TRUE, &error)))
+ if (!(_dbus_socketpair (two, two+1, TRUE, &error)))
_dbus_assert_not_reached("Failed to allocate pipe #2");
if (!dbus_message_append_args(m,
DBUS_TYPE_INVALID))
_dbus_assert_not_reached("Failed to attach fds.");
- if (!_dbus_close(one[0], &error))
+ if (!_dbus_close_socket (one[0], &error))
_dbus_assert_not_reached("Failed to close pipe #1 ");
- if (!_dbus_close(two[0], &error))
+ if (!_dbus_close_socket (two[0], &error))
_dbus_assert_not_reached("Failed to close pipe #2 ");
if (!(dbus_connection_can_send_type(foo, DBUS_TYPE_UNIX_FD)))
if (!_dbus_close(z, &error))
_dbus_assert_not_reached("Failed to close pipe #2/other size 2nd fd ");
- if (read(one[1], &r, 1) != 1 || r != 'X')
+ if (read(one[1].fd, &r, 1) != 1 || r != 'X')
_dbus_assert_not_reached("Failed to read value from pipe.");
- if (read(two[1], &r, 1) != 1 || r != 'Y')
+ if (read(two[1].fd, &r, 1) != 1 || r != 'Y')
_dbus_assert_not_reached("Failed to read value from pipe.");
- if (read(two[1], &r, 1) != 1 || r != 'Z')
+ if (read(two[1].fd, &r, 1) != 1 || r != 'Z')
_dbus_assert_not_reached("Failed to read value from pipe.");
- if (!_dbus_close(one[1], &error))
+ if (!_dbus_close_socket (one[1], &error))
_dbus_assert_not_reached("Failed to close pipe #1 ");
- if (!_dbus_close(two[1], &error))
+ if (!_dbus_close_socket (two[1], &error))
_dbus_assert_not_reached("Failed to close pipe #2 ");
_dbus_verbose ("Disconnecting foo\n");