blob: 7a96f9dc15977b2de8c700eaa68f4b2e1139bf62 [file] [log] [blame]
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dispatch.c Message dispatcher
*
* Copyright (C) 2003 CodeFactory AB
* Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
* Copyright (C) 2004 Imendio HB
*
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <config.h>
#include "dispatch.h"
#include "connection.h"
#include "driver.h"
#include "services.h"
#include "activation.h"
#include "utils.h"
#include "bus.h"
#include "signals.h"
#include "test.h"
#include <dbus/dbus-internals.h>
#include <string.h>
#ifdef HAVE_UNIX_FD_PASSING
#include <dbus/dbus-sysdeps-unix.h>
#include <unistd.h>
#endif
/* This is hard-coded in the files in valid-config-files-*. We have to use
* the debug-pipe transport because the tests in this file require that
* dbus_connection_open_private() does not block. */
#define TEST_DEBUG_PIPE "debug-pipe:name=test-server"
static dbus_bool_t
send_one_message (DBusConnection *connection,
BusContext *context,
DBusConnection *sender,
DBusConnection *addressed_recipient,
DBusMessage *message,
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 */
if (dbus_message_contains_unix_fds(message) &&
!dbus_connection_can_send_type(connection, DBUS_TYPE_UNIX_FD))
return TRUE; /* silently don't send it */
if (!bus_transaction_send (transaction,
connection,
message))
{
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)
{
DBusError tmp_error;
BusConnections *connections;
DBusList *recipients;
BusMatchmaker *matchmaker;
DBusList *link;
BusContext *context;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
/* sender and recipient can both be NULL for the bus driver,
* or for signals with no particular recipient
*/
_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;
if (dbus_message_contains_unix_fds (message) &&
!dbus_connection_can_send_type (addressed_recipient,
DBUS_TYPE_UNIX_FD))
{
dbus_set_error (error,
DBUS_ERROR_NOT_SUPPORTED,
"Tried to send message with Unix file descriptors"
"to a client that doesn't support that.");
return FALSE;
}
/* Dispatch the message */
if (!bus_transaction_send (transaction, addressed_recipient, message))
{
BUS_SET_OOM (error);
return FALSE;
}
}
/* Now dispatch to others who look interested in this message */
connections = bus_transaction_get_connections (transaction);
dbus_error_init (&tmp_error);
matchmaker = bus_context_get_matchmaker (context);
recipients = NULL;
if (!bus_matchmaker_get_recipients (matchmaker, connections,
sender, addressed_recipient, message,
&recipients))
{
BUS_SET_OOM (error);
return FALSE;
}
link = _dbus_list_get_first_link (&recipients);
while (link != NULL)
{
DBusConnection *dest;
dest = link->data;
if (!send_one_message (dest, context, sender, addressed_recipient,
message, transaction, &tmp_error))
break;
link = _dbus_list_get_next_link (&recipients, link);
}
_dbus_list_clear (&recipients);
if (dbus_error_is_set (&tmp_error))
{
dbus_move_error (&tmp_error, error);
return FALSE;
}
else
return TRUE;
}
static DBusHandlerResult
bus_dispatch (DBusConnection *connection,
DBusMessage *message)
{
const char *sender, *service_name;
DBusError error;
BusTransaction *transaction;
BusContext *context;
DBusHandlerResult result;
DBusConnection *addressed_recipient;
result = DBUS_HANDLER_RESULT_HANDLED;
transaction = NULL;
addressed_recipient = NULL;
dbus_error_init (&error);
context = bus_connection_get_context (connection);
_dbus_assert (context != NULL);
/* If we can't even allocate an OOM error, we just go to sleep
* until we can.
*/
while (!bus_connection_preallocate_oom_error (connection))
_dbus_wait_for_memory ();
/* Ref connection in case we disconnect it at some point in here */
dbus_connection_ref (connection);
service_name = dbus_message_get_destination (message);
#ifdef DBUS_ENABLE_VERBOSE_MODE
{
const char *interface_name, *member_name, *error_name;
interface_name = dbus_message_get_interface (message);
member_name = dbus_message_get_member (message);
error_name = dbus_message_get_error_name (message);
_dbus_verbose ("DISPATCH: %s %s %s to %s\n",
interface_name ? interface_name : "(no interface)",
member_name ? member_name : "(no member)",
error_name ? error_name : "(no error name)",
service_name ? service_name : "peer");
}
#endif /* DBUS_ENABLE_VERBOSE_MODE */
/* If service_name is NULL, if it's a signal we send it to all
* connections with a match rule. If it's not a signal, there
* are some special cases here but mostly we just bail out.
*/
if (service_name == NULL)
{
if (dbus_message_is_signal (message,
DBUS_INTERFACE_LOCAL,
"Disconnected"))
{
bus_connection_disconnected (connection);
goto out;
}
if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL)
{
/* DBusConnection also handles some of these automatically, we leave
* it to do so.
*/
result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
goto out;
}
}
/* Create our transaction */
transaction = bus_transaction_new (context);
if (transaction == NULL)
{
BUS_SET_OOM (&error);
goto out;
}
/* Assign a sender to the message */
if (bus_connection_is_active (connection))
{
sender = bus_connection_get_name (connection);
_dbus_assert (sender != NULL);
if (!dbus_message_set_sender (message, sender))
{
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);
}
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))
{
_dbus_verbose ("Security policy rejected message\n");
goto out;
}
_dbus_verbose ("Giving message to %s\n", DBUS_SERVICE_DBUS);
if (!bus_driver_handle_message (connection, transaction, message, &error))
goto out;
}
else if (!bus_connection_is_active (connection)) /* clients must talk to bus driver first */
{
_dbus_verbose ("Received message from non-registered client. Disconnecting.\n");
dbus_connection_close (connection);
goto out;
}
else if (service_name != NULL) /* route to named service */
{
DBusString service_string;
BusService *service;
BusRegistry *registry;
_dbus_assert (service_name != NULL);
registry = bus_connection_get_registry (connection);
_dbus_string_init_const (&service_string, service_name);
service = bus_registry_lookup (registry, &service_string);
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.
*/
activation = bus_connection_get_activation (connection);
if (!bus_activation_activate_service (activation, connection, transaction, TRUE,
message, service_name, &error))
{
_DBUS_ASSERT_ERROR_IS_SET (&error);
_dbus_verbose ("bus_activation_activate_service() failed: %s\n", error.name);
goto out;
}
goto out;
}
else if (service == NULL)
{
dbus_set_error (&error,
DBUS_ERROR_NAME_HAS_NO_OWNER,
"Name \"%s\" does not exist",
service_name);
goto out;
}
else
{
addressed_recipient = bus_service_get_primary_owners_connection (service);
_dbus_assert (addressed_recipient != NULL);
}
}
/* Now send the message to its destination (or not, if
* addressed_recipient == NULL), and match it against other connections'
* match rules.
*/
if (!bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
goto out;
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))
{
bus_connection_send_oom_error (connection, message);
/* cancel transaction due to OOM */
if (transaction != NULL)
{
bus_transaction_cancel_and_free (transaction);
transaction = NULL;
}
}
else
{
/* Try to send the real error, if no mem to do that, send
* the OOM error
*/
_dbus_assert (transaction != NULL);
if (!bus_transaction_send_error_reply (transaction, connection,
&error, message))
{
bus_connection_send_oom_error (connection, message);
/* cancel transaction due to OOM */
if (transaction != NULL)
{
bus_transaction_cancel_and_free (transaction);
transaction = NULL;
}
}
}
dbus_error_free (&error);
}
if (transaction != NULL)
{
bus_transaction_execute_and_free (transaction);
}
dbus_connection_unref (connection);
return result;
}
static DBusHandlerResult
bus_dispatch_message_filter (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
return bus_dispatch (connection, message);
}
dbus_bool_t
bus_dispatch_add_connection (DBusConnection *connection)
{
if (!dbus_connection_add_filter (connection,
bus_dispatch_message_filter,
NULL, NULL))
return FALSE;
return TRUE;
}
void
bus_dispatch_remove_connection (DBusConnection *connection)
{
/* Here we tell the bus driver that we want to get off. */
bus_driver_remove_connection (connection);
dbus_connection_remove_filter (connection,
bus_dispatch_message_filter,
NULL);
}
#ifdef DBUS_BUILD_TESTS
#include <stdio.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.
*/
#define SEND_PENDING(connection) (dbus_connection_has_messages_to_send (connection))
typedef dbus_bool_t (* Check1Func) (BusContext *context);
typedef dbus_bool_t (* Check2Func) (BusContext *context,
DBusConnection *connection);
static dbus_bool_t check_no_leftovers (BusContext *context);
static void
block_connection_until_message_from_bus (BusContext *context,
DBusConnection *connection,
const char *what_is_expected)
{
_dbus_verbose ("expecting: %s\n", what_is_expected);
while (dbus_connection_get_dispatch_status (connection) ==
DBUS_DISPATCH_COMPLETE &&
dbus_connection_get_is_connected (connection))
{
bus_test_run_bus_loop (context, TRUE);
bus_test_run_clients_loop (FALSE);
}
}
static void
spin_connection_until_authenticated (BusContext *context,
DBusConnection *connection)
{
_dbus_verbose ("Spinning to auth connection %p\n", connection);
while (!dbus_connection_get_is_authenticated (connection) &&
dbus_connection_get_is_connected (connection))
{
bus_test_run_bus_loop (context, FALSE);
bus_test_run_clients_loop (FALSE);
}
_dbus_verbose (" ... done spinning to auth connection %p\n", connection);
}
/* compensate for fact that pop_message() can return #NULL due to OOM */
static DBusMessage*
pop_message_waiting_for_memory (DBusConnection *connection)
{
while (dbus_connection_get_dispatch_status (connection) ==
DBUS_DISPATCH_NEED_MEMORY)
_dbus_wait_for_memory ();
return dbus_connection_pop_message (connection);
}
static DBusMessage*
borrow_message_waiting_for_memory (DBusConnection *connection)
{
while (dbus_connection_get_dispatch_status (connection) ==
DBUS_DISPATCH_NEED_MEMORY)
_dbus_wait_for_memory ();
return dbus_connection_borrow_message (connection);
}
static void
warn_unexpected_real (DBusConnection *connection,
DBusMessage *message,
const char *expected,
const char *function,
int line)
{
if (message)
_dbus_warn ("%s:%d received message interface \"%s\" member \"%s\" error name \"%s\" on %p, expecting %s\n",
function, line,
dbus_message_get_interface (message) ?
dbus_message_get_interface (message) : "(unset)",
dbus_message_get_member (message) ?
dbus_message_get_member (message) : "(unset)",
dbus_message_get_error_name (message) ?
dbus_message_get_error_name (message) : "(unset)",
connection,
expected);
else
_dbus_warn ("%s:%d received no message on %p, expecting %s\n",
function, line, connection, expected);
}
#define warn_unexpected(connection, message, expected) \
warn_unexpected_real (connection, message, expected, _DBUS_FUNCTION_NAME, __LINE__)
static void
verbose_message_received (DBusConnection *connection,
DBusMessage *message)
{
_dbus_verbose ("Received message interface \"%s\" member \"%s\" error name \"%s\" on %p\n",
dbus_message_get_interface (message) ?
dbus_message_get_interface (message) : "(unset)",
dbus_message_get_member (message) ?
dbus_message_get_member (message) : "(unset)",
dbus_message_get_error_name (message) ?
dbus_message_get_error_name (message) : "(unset)",
connection);
}
typedef enum
{
SERVICE_CREATED,
OWNER_CHANGED,
SERVICE_DELETED
} ServiceInfoKind;
typedef struct
{
ServiceInfoKind expected_kind;
const char *expected_service_name;
dbus_bool_t failed;
DBusConnection *skip_connection;
} CheckServiceOwnerChangedData;
static dbus_bool_t
check_service_owner_changed_foreach (DBusConnection *connection,
void *data)
{
CheckServiceOwnerChangedData *d = data;
DBusMessage *message;
DBusError error;
const char *service_name, *old_owner, *new_owner;
if (d->expected_kind == SERVICE_CREATED
&& connection == d->skip_connection)
return TRUE;
dbus_error_init (&error);
d->failed = TRUE;
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,
"NameOwnerChanged"))
{
warn_unexpected (connection, message, "NameOwnerChanged");
goto out;
}
else
{
reget_service_info_data:
service_name = NULL;
old_owner = NULL;
new_owner = NULL;
dbus_message_get_args (message, &error,
DBUS_TYPE_STRING, &service_name,
DBUS_TYPE_STRING, &old_owner,
DBUS_TYPE_STRING, &new_owner,
DBUS_TYPE_INVALID);
if (dbus_error_is_set (&error))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
dbus_error_free (&error);
_dbus_wait_for_memory ();
goto reget_service_info_data;
}
else
{
_dbus_warn ("Did not get the expected arguments\n");
goto out;
}
}
if ((d->expected_kind == SERVICE_CREATED && ( old_owner[0] || !new_owner[0]))
|| (d->expected_kind == OWNER_CHANGED && (!old_owner[0] || !new_owner[0]))
|| (d->expected_kind == SERVICE_DELETED && (!old_owner[0] || new_owner[0])))
{
_dbus_warn ("inconsistent NameOwnerChanged arguments\n");
goto out;
}
if (strcmp (service_name, d->expected_service_name) != 0)
{
_dbus_warn ("expected info on service %s, got info on %s\n",
d->expected_service_name,
service_name);
goto out;
}
if (*service_name == ':' && new_owner[0]
&& strcmp (service_name, new_owner) != 0)
{
_dbus_warn ("inconsistent ServiceOwnedChanged message (\"%s\" [ %s -> %s ])\n",
service_name, old_owner, new_owner);
goto out;
}
}
d->failed = FALSE;
out:
dbus_error_free (&error);
if (message)
dbus_message_unref (message);
return !d->failed;
}
static void
kill_client_connection (BusContext *context,
DBusConnection *connection)
{
char *base_service;
const char *s;
CheckServiceOwnerChangedData socd;
_dbus_verbose ("killing connection %p\n", connection);
s = dbus_bus_get_unique_name (connection);
_dbus_assert (s != NULL);
while ((base_service = _dbus_strdup (s)) == NULL)
_dbus_wait_for_memory ();
dbus_connection_ref (connection);
/* kick in the disconnect handler that unrefs the connection */
dbus_connection_close (connection);
bus_test_run_everything (context);
_dbus_assert (bus_test_client_listed (connection));
/* Run disconnect handler in test.c */
if (bus_connection_dispatch_one_message (connection))
_dbus_assert_not_reached ("something received on connection being killed other than the disconnect");
_dbus_assert (!dbus_connection_get_is_connected (connection));
dbus_connection_unref (connection);
connection = NULL;
_dbus_assert (!bus_test_client_listed (connection));
socd.expected_kind = SERVICE_DELETED;
socd.expected_service_name = base_service;
socd.failed = FALSE;
socd.skip_connection = NULL;
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
dbus_free (base_service);
if (socd.failed)
_dbus_assert_not_reached ("didn't get the expected NameOwnerChanged (deletion) messages");
if (!check_no_leftovers (context))
_dbus_assert_not_reached ("stuff left in message queues after disconnecting a client");
}
static void
kill_client_connection_unchecked (DBusConnection *connection)
{
/* This kills the connection without expecting it to affect
* the rest of the bus.
*/
_dbus_verbose ("Unchecked kill of connection %p\n", connection);
dbus_connection_ref (connection);
dbus_connection_close (connection);
/* dispatching disconnect handler will unref once */
if (bus_connection_dispatch_one_message (connection))
_dbus_assert_not_reached ("message other than disconnect dispatched after failure to register");
_dbus_assert (!bus_test_client_listed (connection));
dbus_connection_unref (connection);
}
typedef struct
{
dbus_bool_t failed;
} CheckNoMessagesData;
static dbus_bool_t
check_no_messages_foreach (DBusConnection *connection,
void *data)
{
CheckNoMessagesData *d = data;
DBusMessage *message;
message = pop_message_waiting_for_memory (connection);
if (message != NULL)
{
warn_unexpected (connection, message, "no messages");
d->failed = TRUE;
}
if (message)
dbus_message_unref (message);
return !d->failed;
}
static dbus_bool_t
check_no_leftovers (BusContext *context)
{
CheckNoMessagesData nmd;
nmd.failed = FALSE;
bus_test_clients_foreach (check_no_messages_foreach,
&nmd);
if (nmd.failed)
{
_dbus_verbose ("leftover message found\n");
return FALSE;
}
else
return TRUE;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_hello_message (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
DBusMessage *name_message;
dbus_uint32_t serial;
dbus_bool_t retval;
DBusError error;
const char *name;
const char *acquired;
retval = FALSE;
dbus_error_init (&error);
name = NULL;
acquired = NULL;
message = NULL;
name_message = NULL;
_dbus_verbose ("check_hello_message for %p\n", connection);
message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"Hello");
if (message == NULL)
return TRUE;
dbus_connection_ref (connection); /* because we may get disconnected */
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
dbus_connection_unref (connection);
return TRUE;
}
_dbus_assert (dbus_message_has_signature (message, ""));
dbus_message_unref (message);
message = NULL;
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected (presumably auth failed)\n");
dbus_connection_unref (connection);
return TRUE;
}
/* send our message */
bus_test_run_clients_loop (SEND_PENDING (connection));
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected (presumably auth failed)\n");
dbus_connection_unref (connection);
return TRUE;
}
block_connection_until_message_from_bus (context, connection, "reply to Hello");
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected (presumably auth failed)\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",
"Hello", 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
{
CheckServiceOwnerChangedData socd;
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
{
; /* good, expected */
}
else
{
warn_unexpected (connection, message, "method return for Hello");
goto out;
}
retry_get_hello_name:
if (!dbus_message_get_args (message, &error,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
_dbus_verbose ("no memory to get service name arg from hello\n");
dbus_error_free (&error);
_dbus_wait_for_memory ();
goto retry_get_hello_name;
}
else
{
_dbus_assert (dbus_error_is_set (&error));
_dbus_warn ("Did not get the expected single string argument to hello\n");
goto out;
}
}
_dbus_verbose ("Got hello name: %s\n", name);
while (!dbus_bus_set_unique_name (connection, name))
_dbus_wait_for_memory ();
socd.expected_kind = SERVICE_CREATED;
socd.expected_service_name = name;
socd.failed = FALSE;
socd.skip_connection = connection; /* we haven't done AddMatch so won't get it ourselves */
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
if (socd.failed)
goto out;
name_message = message;
/* Client should also have gotten ServiceAcquired */
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Expecting %s, got nothing\n",
"NameAcquired");
goto out;
}
if (! dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
"NameAcquired"))
{
_dbus_warn ("Expecting %s, got smthg else\n",
"NameAcquired");
goto out;
}
retry_get_acquired_name:
if (!dbus_message_get_args (message, &error,
DBUS_TYPE_STRING, &acquired,
DBUS_TYPE_INVALID))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
_dbus_verbose ("no memory to get service name arg from acquired\n");
dbus_error_free (&error);
_dbus_wait_for_memory ();
goto retry_get_acquired_name;
}
else
{
_dbus_assert (dbus_error_is_set (&error));
_dbus_warn ("Did not get the expected single string argument to ServiceAcquired\n");
goto out;
}
}
_dbus_verbose ("Got acquired name: %s\n", acquired);
if (strcmp (acquired, name) != 0)
{
_dbus_warn ("Acquired name is %s but expected %s\n",
acquired, name);
goto out;
}
acquired = NULL;
}
if (!check_no_leftovers (context))
goto out;
retval = TRUE;
out:
_dbus_verbose ("ending - retval = %d\n", retval);
dbus_error_free (&error);
if (message)
dbus_message_unref (message);
if (name_message)
dbus_message_unref (name_message);
return retval;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_double_hello_message (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
DBusError error;
retval = FALSE;
dbus_error_init (&error);
message = NULL;
_dbus_verbose ("check_double_hello_message for %p\n", connection);
message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"Hello");
if (message == NULL)
return TRUE;
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
/* send our message */
bus_test_run_clients_loop (SEND_PENDING (connection));
dbus_connection_ref (connection); /* because we may get disconnected */
block_connection_until_message_from_bus (context, connection, "reply to Hello");
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",
"Hello", 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)
{
warn_unexpected (connection, message, "method return for Hello");
goto out;
}
if (!check_no_leftovers (context))
goto out;
retval = TRUE;
out:
dbus_error_free (&error);
if (message)
dbus_message_unref (message);
return retval;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_get_connection_unix_user (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
DBusError error;
const char *base_service_name;
dbus_uint32_t uid;
retval = FALSE;
dbus_error_init (&error);
message = NULL;
_dbus_verbose ("check_get_connection_unix_user for %p\n", connection);
message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"GetConnectionUnixUser");
if (message == NULL)
return TRUE;
base_service_name = dbus_bus_get_unique_name (connection);
if (!dbus_message_append_args (message,
DBUS_TYPE_STRING, &base_service_name,
DBUS_TYPE_INVALID))
{
dbus_message_unref (message);
return TRUE;
}
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
/* send our message */
bus_test_run_clients_loop (SEND_PENDING (connection));
dbus_message_unref (message);
message = NULL;
dbus_connection_ref (connection); /* because we may get disconnected */
block_connection_until_message_from_bus (context, connection, "reply to GetConnectionUnixUser");
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",
"GetConnectionUnixUser", serial, connection);
goto out;
}
verbose_message_received (connection, message);
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 */
}
else
{
warn_unexpected (connection, message,
"method_return for GetConnectionUnixUser");
goto out;
}
retry_get_property:
if (!dbus_message_get_args (message, &error,
DBUS_TYPE_UINT32, &uid,
DBUS_TYPE_INVALID))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
_dbus_verbose ("no memory to get uid by GetConnectionUnixUser\n");
dbus_error_free (&error);
_dbus_wait_for_memory ();
goto retry_get_property;
}
else
{
_dbus_assert (dbus_error_is_set (&error));
_dbus_warn ("Did not get the expected DBUS_TYPE_UINT32 from GetConnectionUnixUser\n");
goto out;
}
}
}
if (!check_no_leftovers (context))
goto out;
retval = TRUE;
out:
dbus_error_free (&error);
if (message)
dbus_message_unref (message);
return retval;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_get_connection_unix_process_id (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
DBusError error;
const char *base_service_name;
dbus_uint32_t pid;
retval = FALSE;
dbus_error_init (&error);
message = NULL;
_dbus_verbose ("check_get_connection_unix_process_id for %p\n", connection);
message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"GetConnectionUnixProcessID");
if (message == NULL)
return TRUE;
base_service_name = dbus_bus_get_unique_name (connection);
if (!dbus_message_append_args (message,
DBUS_TYPE_STRING, &base_service_name,
DBUS_TYPE_INVALID))
{
dbus_message_unref (message);
return TRUE;
}
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
/* send our message */
bus_test_run_clients_loop (SEND_PENDING (connection));
dbus_message_unref (message);
message = NULL;
dbus_connection_ref (connection); /* because we may get disconnected */
block_connection_until_message_from_bus (context, connection, "reply to GetConnectionUnixProcessID");
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",
"GetConnectionUnixProcessID", serial, connection);
goto out;
}
verbose_message_received (connection, message);
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 */
}
#ifdef DBUS_WIN
else if (dbus_message_is_error (message, DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN))
{
/* We are expecting this error, since we know in the test suite we aren't
* talking to a client running on UNIX
*/
_dbus_verbose ("Windows correctly does not support GetConnectionUnixProcessID\n");
}
#endif
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
#ifdef DBUS_WIN
warn_unexpected (connection, message, "GetConnectionUnixProcessID to fail on Windows");
goto out;
#else
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
{
; /* good, expected */
}
else
{
warn_unexpected (connection, message,
"method_return for GetConnectionUnixProcessID");
goto out;
}
retry_get_property:
if (!dbus_message_get_args (message, &error,
DBUS_TYPE_UINT32, &pid,
DBUS_TYPE_INVALID))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
_dbus_verbose ("no memory to get pid by GetConnectionUnixProcessID\n");
dbus_error_free (&error);
_dbus_wait_for_memory ();
goto retry_get_property;
}
else
{
_dbus_assert (dbus_error_is_set (&error));
_dbus_warn ("Did not get the expected DBUS_TYPE_UINT32 from GetConnectionUnixProcessID\n");
goto out;
}
}
else
{
/* test if returned pid is the same as our own pid
*
* @todo It would probably be good to restructure the tests
* in a way so our parent is the bus that we're testing
* cause then we can test that the pid returned matches
* getppid()
*/
if (pid != (dbus_uint32_t) _dbus_getpid ())
{
_dbus_assert (dbus_error_is_set (&error));
_dbus_warn ("Result from GetConnectionUnixProcessID is not our own pid\n");
goto out;
}
}
#endif /* !DBUS_WIN */
}
if (!check_no_leftovers (context))
goto out;
retval = TRUE;
out:
dbus_error_free (&error);
if (message)
dbus_message_unref (message);
return retval;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_add_match_all (BusContext *context,
DBusConnection *connection)
{
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);
message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"AddMatch");
if (message == NULL)
return TRUE;
/* empty string match rule matches everything */
if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &empty,
DBUS_TYPE_INVALID))
{
dbus_message_unref (message);
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;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_hello_connection (BusContext *context)
{
DBusConnection *connection;
DBusError error;
dbus_error_init (&error);
connection = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
if (connection == NULL)
{
_DBUS_ASSERT_ERROR_IS_SET (&error);
dbus_error_free (&error);
return TRUE;
}
if (!bus_setup_debug_client (connection))
{
dbus_connection_close (connection);
dbus_connection_unref (connection);
return TRUE;
}
spin_connection_until_authenticated (context, connection);
if (!check_hello_message (context, connection))
return FALSE;
if (dbus_bus_get_unique_name (connection) == NULL)
{
/* We didn't successfully register, so we can't
* do the usual kill_client_connection() checks
*/
kill_client_connection_unchecked (connection);
}
else
{
if (!check_add_match_all (context, connection))
return FALSE;
kill_client_connection (context, connection);
}
return TRUE;
}
#define NONEXISTENT_SERVICE_NAME "test.this.service.does.not.exist.ewuoiurjdfxcvn"
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_nonexistent_service_no_auto_start (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
const char *nonexistent = NONEXISTENT_SERVICE_NAME;
dbus_uint32_t flags;
message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"StartServiceByName");
if (message == NULL)
return TRUE;
dbus_message_set_auto_start (message, FALSE);
flags = 0;
if (!dbus_message_append_args (message,
DBUS_TYPE_STRING, &nonexistent,
DBUS_TYPE_UINT32, &flags,
DBUS_TYPE_INVALID))
{
dbus_message_unref (message);
return TRUE;
}
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
block_connection_until_message_from_bus (context, connection, "reply to ActivateService on nonexistent");
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected\n");
return TRUE;
}
retval = FALSE;
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Did not receive a reply to %s %d on %p\n",
"StartServiceByName", serial, connection);
goto out;
}
verbose_message_received (connection, message);
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
{
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_is_error (message,
DBUS_ERROR_NO_MEMORY))
{
; /* good, this is a valid response */
}
else if (dbus_message_is_error (message,
DBUS_ERROR_SERVICE_UNKNOWN))
{
; /* good, this is expected also */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully activate %s\n",
NONEXISTENT_SERVICE_NAME);
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_nonexistent_service_auto_start (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
message = dbus_message_new_method_call (NONEXISTENT_SERVICE_NAME,
"/org/freedesktop/TestSuite",
"org.freedesktop.TestSuite",
"Echo");
if (message == NULL)
return TRUE;
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
block_connection_until_message_from_bus (context, connection, "reply to Echo");
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected\n");
return TRUE;
}
retval = FALSE;
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Did not receive a reply to %s %d on %p\n",
"Echo message (auto activation)", serial, connection);
goto out;
}
verbose_message_received (connection, message);
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
{
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_is_error (message,
DBUS_ERROR_NO_MEMORY))
{
; /* good, this is a valid response */
}
else if (dbus_message_is_error (message,
DBUS_ERROR_SERVICE_UNKNOWN))
{
; /* good, this is expected also */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully activate %s\n",
NONEXISTENT_SERVICE_NAME);
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
static dbus_bool_t
check_base_service_activated (BusContext *context,
DBusConnection *connection,
DBusMessage *initial_message,
const char **base_service_p)
{
DBusMessage *message;
dbus_bool_t retval;
DBusError error;
const char *base_service, *base_service_from_bus, *old_owner;
retval = FALSE;
dbus_error_init (&error);
base_service = NULL;
old_owner = NULL;
base_service_from_bus = NULL;
message = initial_message;
dbus_message_ref (message);
if (dbus_message_is_signal (message,
DBUS_INTERFACE_DBUS,
"NameOwnerChanged"))
{
CheckServiceOwnerChangedData socd;
reget_service_name_arg:
base_service = NULL;
old_owner = NULL;
base_service_from_bus = NULL;
if (!dbus_message_get_args (message, &error,
DBUS_TYPE_STRING, &base_service,
DBUS_TYPE_STRING, &old_owner,
DBUS_TYPE_STRING, &base_service_from_bus,
DBUS_TYPE_INVALID))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
dbus_error_free (&error);
_dbus_wait_for_memory ();
goto reget_service_name_arg;
}
else
{
_dbus_warn ("Message %s doesn't have a service name: %s\n",
"NameOwnerChanged (creation)",
error.message);
goto out;
}
}
if (*base_service != ':')
{
_dbus_warn ("Expected base service activation, got \"%s\" instead\n",
base_service);
goto out;
}
if (strcmp (base_service, base_service_from_bus) != 0)
{
_dbus_warn ("Expected base service activation, got \"%s\" instead with owner \"%s\"\n",
base_service, base_service_from_bus);
goto out;
}
if (old_owner[0])
{
_dbus_warn ("Received an old_owner argument during base service activation, \"%s\"\n",
old_owner);
goto out;
}
socd.expected_kind = SERVICE_CREATED;
socd.expected_service_name = base_service;
socd.failed = FALSE;
socd.skip_connection = connection;
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
if (socd.failed)
goto out;
}
else
{
warn_unexpected (connection, message, "NameOwnerChanged (creation) for base service");
goto out;
}
if (base_service_p)
*base_service_p = base_service;
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
dbus_error_free (&error);
return retval;
}
static dbus_bool_t
check_service_activated (BusContext *context,
DBusConnection *connection,
const char *activated_name,
const char *base_service_name,
DBusMessage *initial_message)
{
DBusMessage *message;
dbus_bool_t retval;
DBusError error;
dbus_uint32_t activation_result;
retval = FALSE;
dbus_error_init (&error);
message = initial_message;
dbus_message_ref (message);
if (dbus_message_is_signal (message,
DBUS_INTERFACE_DBUS,
"NameOwnerChanged"))
{
CheckServiceOwnerChangedData socd;
const char *service_name, *base_service_from_bus, *old_owner;
reget_service_name_arg:
service_name = NULL;
old_owner = NULL;
base_service_from_bus = NULL;
if (!dbus_message_get_args (message, &error,
DBUS_TYPE_STRING, &service_name,
DBUS_TYPE_STRING, &old_owner,
DBUS_TYPE_STRING, &base_service_from_bus,
DBUS_TYPE_INVALID))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
dbus_error_free (&error);
_dbus_wait_for_memory ();
goto reget_service_name_arg;
}
else
{
_dbus_warn ("Message %s doesn't have a service name: %s\n",
"NameOwnerChanged (creation)",
error.message);
goto out;
}
}
if (strcmp (service_name, activated_name) != 0)
{
_dbus_warn ("Expected to see service %s created, saw %s instead\n",
activated_name, service_name);
goto out;
}
if (strcmp (base_service_name, base_service_from_bus) != 0)
{
_dbus_warn ("NameOwnerChanged reports wrong base service: %s owner, expected %s instead\n",
base_service_from_bus, base_service_name);
goto out;
}
if (old_owner[0])
{
_dbus_warn ("expected a %s, got a %s\n",
"NameOwnerChanged (creation)",
"NameOwnerChanged (change)");
goto out;
}
socd.expected_kind = SERVICE_CREATED;
socd.skip_connection = connection;
socd.failed = FALSE;
socd.expected_service_name = service_name;
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
if (socd.failed)
goto out;
dbus_message_unref (message);
service_name = NULL;
old_owner = NULL;
base_service_from_bus = NULL;
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Expected a reply to %s, got nothing\n",
"StartServiceByName");
goto out;
}
}
else
{
warn_unexpected (connection, message, "NameOwnerChanged for the activated name");
goto out;
}
if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_RETURN)
{
warn_unexpected (connection, message, "reply to StartServiceByName");
goto out;
}
activation_result = 0;
if (!dbus_message_get_args (message, &error,
DBUS_TYPE_UINT32, &activation_result,
DBUS_TYPE_INVALID))
{
if (!dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
_dbus_warn ("Did not have activation result first argument to %s: %s\n",
"StartServiceByName", error.message);
goto out;
}
dbus_error_free (&error);
}
else
{
if (activation_result == DBUS_START_REPLY_SUCCESS)
; /* Good */
else if (activation_result == DBUS_START_REPLY_ALREADY_RUNNING)
; /* Good also */
else
{
_dbus_warn ("Activation result was %u, no good.\n",
activation_result);
goto out;
}
}
dbus_message_unref (message);
message = NULL;
if (!check_no_leftovers (context))
{
_dbus_warn ("Messages were left over after verifying existent activation results\n");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
dbus_error_free (&error);
return retval;
}
static dbus_bool_t
check_service_auto_activated (BusContext *context,
DBusConnection *connection,
const char *activated_name,
const char *base_service_name,
DBusMessage *initial_message)
{
DBusMessage *message;
dbus_bool_t retval;
DBusError error;
retval = FALSE;
dbus_error_init (&error);
message = initial_message;
dbus_message_ref (message);
if (dbus_message_is_signal (message,
DBUS_INTERFACE_DBUS,
"NameOwnerChanged"))
{
const char *service_name;
CheckServiceOwnerChangedData socd;
reget_service_name_arg:
if (!dbus_message_get_args (message, &error,
DBUS_TYPE_STRING, &service_name,
DBUS_TYPE_INVALID))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
dbus_error_free (&error);
_dbus_wait_for_memory ();
goto reget_service_name_arg;
}
else
{
_dbus_warn ("Message %s doesn't have a service name: %s\n",
"NameOwnerChanged",
error.message);
dbus_error_free (&error);
goto out;
}
}
if (strcmp (service_name, activated_name) != 0)
{
_dbus_warn ("Expected to see service %s created, saw %s instead\n",
activated_name, service_name);
goto out;
}
socd.expected_kind = SERVICE_CREATED;
socd.expected_service_name = service_name;
socd.failed = FALSE;
socd.skip_connection = connection;
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
if (socd.failed)
goto out;
/* Note that this differs from regular activation in that we don't get a
* reply to ActivateService here.
*/
dbus_message_unref (message);
message = NULL;
service_name = NULL;
}
else
{
warn_unexpected (connection, message, "NameOwnerChanged for the activated name");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
static dbus_bool_t
check_service_deactivated (BusContext *context,
DBusConnection *connection,
const char *activated_name,
const char *base_service)
{
dbus_bool_t retval;
CheckServiceOwnerChangedData socd;
retval = FALSE;
/* Now we are expecting ServiceOwnerChanged (deletion) messages for the base
* service and the activated_name. The base service
* notification is required to come last.
*/
socd.expected_kind = SERVICE_DELETED;
socd.expected_service_name = activated_name;
socd.failed = FALSE;
socd.skip_connection = NULL;
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
if (socd.failed)
goto out;
socd.expected_kind = SERVICE_DELETED;
socd.expected_service_name = base_service;
socd.failed = FALSE;
socd.skip_connection = NULL;
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
if (socd.failed)
goto out;
retval = TRUE;
out:
return retval;
}
static dbus_bool_t
check_send_exit_to_service (BusContext *context,
DBusConnection *connection,
const char *service_name,
const char *base_service)
{
dbus_bool_t got_error;
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
_dbus_verbose ("Sending exit message to the test service\n");
retval = FALSE;
/* Kill off the test service by sending it a quit message */
message = dbus_message_new_method_call (service_name,
"/org/freedesktop/TestSuite",
"org.freedesktop.TestSuite",
"Exit");
if (message == NULL)
{
/* Do this again; we still need the service to exit... */
if (!check_send_exit_to_service (context, connection,
service_name, base_service))
goto out;
return TRUE;
}
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
/* Do this again; we still need the service to exit... */
if (!check_send_exit_to_service (context, connection,
service_name, base_service))
goto out;
return TRUE;
}
dbus_message_unref (message);
message = NULL;
/* send message */
bus_test_run_clients_loop (SEND_PENDING (connection));
/* read it in and write it out to test service */
bus_test_run_bus_loop (context, FALSE);
/* see if we got an error during message bus dispatching */
bus_test_run_clients_loop (FALSE);
message = borrow_message_waiting_for_memory (connection);
got_error = message != NULL && dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR;
if (message)
{
dbus_connection_return_message (connection, message);
message = NULL;
}
if (!got_error)
{
/* If no error, wait for the test service to exit */
block_connection_until_message_from_bus (context, connection, "test service to exit");
bus_test_run_everything (context);
}
if (got_error)
{
message = pop_message_waiting_for_memory (connection);
_dbus_assert (message != NULL);
if (dbus_message_get_reply_serial (message) != serial)
{
warn_unexpected (connection, message,
"error with the correct reply serial");
goto out;
}
if (!dbus_message_is_error (message,
DBUS_ERROR_NO_MEMORY))
{
warn_unexpected (connection, message,
"a no memory error from asking test service to exit");
goto out;
}
_dbus_verbose ("Got error %s when asking test service to exit\n",
dbus_message_get_error_name (message));
/* Do this again; we still need the service to exit... */
if (!check_send_exit_to_service (context, connection,
service_name, base_service))
goto out;
}
else
{
if (!check_service_deactivated (context, connection,
service_name, base_service))
goto out;
/* Should now have a NoReply error from the Exit() method
* call; it should have come after all the deactivation
* stuff.
*/
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
warn_unexpected (connection, NULL,
"reply to Exit() method call");
goto out;
}
if (!dbus_message_is_error (message,
DBUS_ERROR_NO_REPLY))
{
warn_unexpected (connection, message,
"NoReply error from Exit() method call");
goto out;
}
if (dbus_message_get_reply_serial (message) != serial)
{
warn_unexpected (connection, message,
"error with the correct reply serial");
goto out;
}
_dbus_verbose ("Got error %s after test service exited\n",
dbus_message_get_error_name (message));
if (!check_no_leftovers (context))
{
_dbus_warn ("Messages were left over after %s\n",
_DBUS_FUNCTION_NAME);
goto out;
}
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
static dbus_bool_t
check_got_error (BusContext *context,
DBusConnection *connection,
const char *first_error_name,
...)
{
DBusMessage *message;
dbus_bool_t retval;
va_list ap;
dbus_bool_t error_found;
const char *error_name;
retval = FALSE;
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Did not get an expected error\n");
goto out;
}
if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR)
{
warn_unexpected (connection, message, "an error");
goto out;
}
error_found = FALSE;
va_start (ap, first_error_name);
error_name = first_error_name;
while (error_name != NULL)
{
if (dbus_message_is_error (message, error_name))
{
error_found = TRUE;
break;
}
error_name = va_arg (ap, char*);
}
va_end (ap);
if (!error_found)
{
_dbus_warn ("Expected error %s or other, got %s instead\n",
first_error_name,
dbus_message_get_error_name (message));
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
typedef enum
{
GOT_SERVICE_CREATED,
GOT_SERVICE_DELETED,
GOT_ERROR,
GOT_SOMETHING_ELSE
} GotServiceInfo;
static GotServiceInfo
check_got_service_info (DBusMessage *message)
{
GotServiceInfo message_kind;
if (dbus_message_is_signal (message,
DBUS_INTERFACE_DBUS,
"NameOwnerChanged"))
{
DBusError error;
const char *service_name, *old_owner, *new_owner;
dbus_error_init (&error);
reget_service_info_data:
service_name = NULL;
old_owner = NULL;
new_owner = NULL;
dbus_message_get_args (message, &error,
DBUS_TYPE_STRING, &service_name,
DBUS_TYPE_STRING, &old_owner,
DBUS_TYPE_STRING, &new_owner,
DBUS_TYPE_INVALID);
if (dbus_error_is_set (&error))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
dbus_error_free (&error);
goto reget_service_info_data;
}
else
{
_dbus_warn ("unexpected arguments for NameOwnerChanged message\n");
message_kind = GOT_SOMETHING_ELSE;
}
}
else if (!old_owner[0])
message_kind = GOT_SERVICE_CREATED;
else if (!new_owner[0])
message_kind = GOT_SERVICE_DELETED;
else
message_kind = GOT_SOMETHING_ELSE;
dbus_error_free (&error);
}
else if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
message_kind = GOT_ERROR;
else
message_kind = GOT_SOMETHING_ELSE;
return message_kind;
}
#define EXISTENT_SERVICE_NAME "org.freedesktop.DBus.TestSuiteEchoService"
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_existent_service_no_auto_start (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
DBusMessage *base_service_message;
const char *base_service;
dbus_uint32_t serial;
dbus_bool_t retval;
const char *existent = EXISTENT_SERVICE_NAME;
dbus_uint32_t flags;
base_service_message = NULL;
message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"StartServiceByName");
if (message == NULL)
return TRUE;
dbus_message_set_auto_start (message, FALSE);
flags = 0;
if (!dbus_message_append_args (message,
DBUS_TYPE_STRING, &existent,
DBUS_TYPE_UINT32, &flags,
DBUS_TYPE_INVALID))
{
dbus_message_unref (message);
return TRUE;
}
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
/* now wait for the message bus to hear back from the activated
* service.
*/
block_connection_until_message_from_bus (context, connection, "activated service to connect");
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected\n");
return TRUE;
}
retval = FALSE;
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Did not receive any messages after %s %d on %p\n",
"StartServiceByName", serial, connection);
goto out;
}
verbose_message_received (connection, message);
_dbus_verbose (" (after sending %s)\n", "StartServiceByName");
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
{
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_is_error (message,
DBUS_ERROR_NO_MEMORY))
{
; /* good, this is a valid response */
}
else if (dbus_message_is_error (message,
DBUS_ERROR_SPAWN_CHILD_EXITED) ||
dbus_message_is_error (message,
DBUS_ERROR_SPAWN_CHILD_SIGNALED) ||
dbus_message_is_error (message,
DBUS_ERROR_SPAWN_EXEC_FAILED))
{
; /* good, this is expected also */
}
else
{
_dbus_warn ("Did not expect error %s\n",
dbus_message_get_error_name (message));
goto out;
}
}
else
{
GotServiceInfo message_kind;
if (!check_base_service_activated (context, connection,
message, &base_service))
goto out;
base_service_message = message;
message = NULL;
/* We may need to block here for the test service to exit or finish up */
block_connection_until_message_from_bus (context, connection, "test service to exit or finish up");
message = dbus_connection_borrow_message (connection);
if (message == NULL)
{
_dbus_warn ("Did not receive any messages after base service creation notification\n");
goto out;
}
message_kind = check_got_service_info (message);
dbus_connection_return_message (connection, message);
message = NULL;
switch (message_kind)
{
case GOT_SOMETHING_ELSE:
_dbus_warn ("Unexpected message after ActivateService "
"(should be an error or a service announcement");
goto out;
case GOT_ERROR:
if (!check_got_error (context, connection,
DBUS_ERROR_SPAWN_CHILD_EXITED,
DBUS_ERROR_NO_MEMORY,
NULL))
goto out;
/* A service deleted should be coming along now after this error.
* We can also get the error *after* the service deleted.
*/
/* fall through */
case GOT_SERVICE_DELETED:
{
/* The service started up and got a base address, but then
* failed to register under EXISTENT_SERVICE_NAME
*/
CheckServiceOwnerChangedData socd;
socd.expected_kind = SERVICE_DELETED;
socd.expected_service_name = base_service;
socd.failed = FALSE;
socd.skip_connection = NULL;
bus_test_clients_foreach (check_service_owner_changed_foreach,
&socd);
if (socd.failed)
goto out;
/* Now we should get an error about the service exiting
* if we didn't get it before.
*/
if (message_kind != GOT_ERROR)
{
block_connection_until_message_from_bus (context, connection, "error about service exiting");
/* and process everything again */
bus_test_run_everything (context);
if (!check_got_error (context, connection,
DBUS_ERROR_SPAWN_CHILD_EXITED,
DBUS_ERROR_NO_MEMORY,
NULL))
goto out;
}
break;
}
case GOT_SERVICE_CREATED:
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Failed to pop message we just put back! "
"should have been a NameOwnerChanged (creation)\n");
goto out;
}
if (!check_service_activated (context, connection, EXISTENT_SERVICE_NAME,
base_service, message))
goto out;
dbus_message_unref (message);
message = NULL;
if (!check_no_leftovers (context))
{
_dbus_warn ("Messages were left over after successful activation\n");
goto out;
}
if (!check_send_exit_to_service (context, connection,
EXISTENT_SERVICE_NAME, base_service))
goto out;
break;
}
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
if (base_service_message)
dbus_message_unref (base_service_message);
return retval;
}
#ifndef DBUS_WIN_FIXME
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_segfault_service_no_auto_start (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
const char *segv_service;
dbus_uint32_t flags;
message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"StartServiceByName");
if (message == NULL)
return TRUE;
dbus_message_set_auto_start (message, FALSE);
segv_service = "org.freedesktop.DBus.TestSuiteSegfaultService";
flags = 0;
if (!dbus_message_append_args (message,
DBUS_TYPE_STRING, &segv_service,
DBUS_TYPE_UINT32, &flags,
DBUS_TYPE_INVALID))
{
dbus_message_unref (message);
return TRUE;
}
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
block_connection_until_message_from_bus (context, connection, "reply to activating segfault service");
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected\n");
return TRUE;
}
retval = FALSE;
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Did not receive a reply to %s %d on %p\n",
"StartServiceByName", serial, connection);
goto out;
}
verbose_message_received (connection, message);
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
{
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_is_error (message,
DBUS_ERROR_NO_MEMORY))
{
; /* good, this is a valid response */
}
else if (dbus_message_is_error (message,
DBUS_ERROR_FAILED))
{
const char *servicehelper;
servicehelper = bus_context_get_servicehelper (context);
/* make sure this only happens with the launch helper */
_dbus_assert (servicehelper != NULL);
}
else if (dbus_message_is_error (message,
DBUS_ERROR_SPAWN_CHILD_SIGNALED))
{
; /* good, this is expected also */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully activate segfault service\n");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_segfault_service_auto_start (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
message = dbus_message_new_method_call ("org.freedesktop.DBus.TestSuiteSegfaultService",
"/org/freedesktop/TestSuite",
"org.freedesktop.TestSuite",
"Echo");
if (message == NULL)
return TRUE;
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
block_connection_until_message_from_bus (context, connection, "reply to Echo on segfault service");
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_verbose ("connection was disconnected\n");
return TRUE;
}
retval = FALSE;
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Did not receive a reply to %s %d on %p\n",
"Echo message (auto activation)", serial, connection);
goto out;
}
verbose_message_received (connection, message);
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
{
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_is_error (message,
DBUS_ERROR_NO_MEMORY))
{
; /* good, this is a valid response */
}
else if (dbus_message_is_error (message,
DBUS_ERROR_SPAWN_CHILD_SIGNALED))
{
; /* good, this is expected also */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully activate segfault service\n");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
#endif
#define TEST_ECHO_MESSAGE "Test echo message"
#define TEST_RUN_HELLO_FROM_SELF_MESSAGE "Test sending message to self"
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_existent_hello_from_self (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
const char *text;
message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME,
"/org/freedesktop/TestSuite",
"org.freedesktop.TestSuite",
"RunHelloFromSelf");
if (message == NULL)
return TRUE;
text = TEST_RUN_HELLO_FROM_SELF_MESSAGE;
if (!dbus_message_append_args (message,
DBUS_TYPE_STRING, &text,
DBUS_TYPE_INVALID))
{
dbus_message_unref (message);
return TRUE;
}
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
/* Note: if this test is run in OOM mode, it will block when the bus
* doesn't send a reply due to OOM.
*/
block_connection_until_message_from_bus (context, connection, "reply from running hello from self");
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Failed to pop message! Should have been reply from RunHelloFromSelf message\n");
return FALSE;
}
if (dbus_message_get_reply_serial (message) != serial)
{
_dbus_warn ("Wrong reply serial\n");
dbus_message_unref (message);
return FALSE;
}
dbus_message_unref (message);
message = NULL;
return TRUE;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_existent_ping (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME,
"/org/freedesktop/TestSuite",
"org.freedesktop.DBus.Peer",
"Ping");
if (message == NULL)
return TRUE;
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
/* Note: if this test is run in OOM mode, it will block when the bus
* doesn't send a reply due to OOM.
*/
block_connection_until_message_from_bus (context, connection, "reply from running Ping");
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Failed to pop message! Should have been reply from Ping message\n");
return FALSE;
}
if (dbus_message_get_reply_serial (message) != serial)
{
_dbus_warn ("Wrong reply serial\n");
dbus_message_unref (message);
return FALSE;
}
if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_RETURN)
{
_dbus_warn ("Unexpected message return during Ping\n");
dbus_message_unref (message);
return FALSE;
}
dbus_message_unref (message);
message = NULL;
return TRUE;
}
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_existent_get_machine_id (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
const char *machine_id;
message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME,
"/org/freedesktop/TestSuite",
"org.freedesktop.DBus.Peer",
"GetMachineId");
if (message == NULL)
return TRUE;
if (!dbus_connection_send (connection, message, &serial))
{
dbus_message_unref (message);
return TRUE;
}
dbus_message_unref (message);
message = NULL;
bus_test_run_everything (context);
/* Note: if this test is run in OOM mode, it will block when the bus
* doesn't send a reply due to OOM.
*/
block_connection_until_message_from_bus (context, connection, "reply from running GetMachineId");
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Failed to pop message! Should have been reply from GetMachineId message\n");
return FALSE;
}
if (dbus_message_get_reply_serial (message) != serial)
{
_dbus_warn ("Wrong reply serial\n");
dbus_message_unref (message);
return FALSE;