dbus/bus/dispatch.c
Simon McVittie e9f0378bbf Merge tests' cmake and autotools bus configuration
In Unix, the tests listened on both debug-pipe (which is a socketpair,
or a TCP emulation of socketpair on Windows) and a Unix socket.

In the Windows port, the tests were hard-coded to listen on a particular
port, which allowed the dispatch test to connect to that port, as long
as no two tests ran simultaneously (which I don't think was ever guaranteed -
make -j can violate this). That's valid out-of-process, and also
fully-specified, so they only needed one <listen> directive, so the
CMake input only had one.

To make the tests work under CMake on Unix, there was a hack: the string
substituted for the content of the <listen> directive contained
</listen><listen> to get the other address in, which is pretty nasty.

Instead of doing that, I've made both build systems, on both Unix and
Windows, use both debug-pipe and a more normal transport (Unix or TCP).
debug-pipe has a Windows implementation and it's used in
dbus-spawn-win.c, so it'd better work. The use of debug-pipe is now
hard-coded rather than being a configure parameter (there's no reason
to vary it in different builds), and I used TEST_LISTEN as the name of the
Unix/TCP address, because it's a "vague" address (no specific Unix path, no
TCP port), that you can listen on but not connect to.

This in turn means that we can merge the Autoconf .in and CMake .cmake
files, similar to Bug #41033.

You might wonder why I've kept debug-pipe. I did try to get rid of it, but
it turns out that the tests in dispatch.c rely on
dbus_connection_open_private() not blocking, and normal socket
connections block on connect(). Until we fix that by adding an async
version of dbus_connection_open_private(), it won't be safe to have a
test like dispatch.c that "talks to itself", unless it uses a transport
as trivial as debug-pipe in which neither end has to block on the other.

Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
Reviewed-by: Ralf Habacker <ralf.habacker@freenet.de>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=41222
2011-09-28 19:00:56 +01:00

4910 lines
137 KiB
C

/* -*- 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;
}
if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_RETURN)
{
_dbus_warn ("Unexpected message return during GetMachineId\n");
dbus_message_unref (message);
return FALSE;
}
machine_id = NULL;
if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &machine_id, DBUS_TYPE_INVALID))
{
_dbus_warn ("Did not get a machine ID in reply to GetMachineId\n");
dbus_message_unref (message);
return FALSE;
}
if (machine_id == NULL || strlen (machine_id) != 32)
{
_dbus_warn ("Machine id looks bogus: '%s'\n", machine_id ? machine_id : "null");
dbus_message_unref (message);
return FALSE;
}
/* We can't check that the machine id is correct because during make check it is
* just made up for each process separately
*/
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_service_auto_start (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
DBusMessage *base_service_message;
dbus_uint32_t serial;
dbus_bool_t retval;
const char *base_service;
const char *text;
base_service_message = NULL;
message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME,
"/org/freedesktop/TestSuite",
"org.freedesktop.TestSuite",
"Echo");
if (message == NULL)
return TRUE;
text = TEST_ECHO_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);
/* now wait for the message bus to hear back from the activated
* service.
*/
block_connection_until_message_from_bus (context, connection, "reply to Echo on existent 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 any messages after auto start %d on %p\n",
serial, connection);
goto out;
}
verbose_message_received (connection, message);
_dbus_verbose (" (after sending %s)\n", "auto start");
/* we should get zero or two ServiceOwnerChanged signals */
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL)
{
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, "service to exit");
/* Should get a service creation notification for the activated
* service name, or a service deletion on the base service name
*/
message = dbus_connection_borrow_message (connection);
if (message == NULL)
{
_dbus_warn ("No message after auto activation "
"(should be a service announcement)\n");
dbus_connection_return_message (connection, message);
message = NULL;
goto out;
}
message_kind = check_got_service_info (message);
dbus_connection_return_message (connection, message);
message = NULL;
switch (message_kind)
{
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;
}
/* Check that ServiceOwnerChanged (creation) was correctly received */
if (!check_service_auto_activated (context, connection, EXISTENT_SERVICE_NAME,
base_service, message))
goto out;
dbus_message_unref (message);
message = NULL;
break;
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;
break;
}
case GOT_ERROR:
case GOT_SOMETHING_ELSE:
_dbus_warn ("Unexpected message after auto activation\n");
goto out;
}
}
/* OK, now we've dealt with ServiceOwnerChanged signals, now should
* come the method reply (or error) from the initial method call
*/
/* 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 echo message after auto-activation");
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Failed to pop message! Should have been reply from echo message\n");
goto out;
}
if (dbus_message_get_reply_serial (message) != serial)
{
_dbus_warn ("Wrong reply serial\n");
goto out;
}
dbus_message_unref (message);
message = NULL;
if (!check_existent_ping (context, connection))
goto out;
if (!check_existent_get_machine_id (context, connection))
goto out;
if (!check_existent_hello_from_self (context, connection))
goto out;
if (!check_send_exit_to_service (context, connection,
EXISTENT_SERVICE_NAME,
base_service))
goto out;
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
if (base_service_message)
dbus_message_unref (base_service_message);
return retval;
}
#define SERVICE_FILE_MISSING_NAME "org.freedesktop.DBus.TestSuiteEchoServiceDotServiceFileDoesNotExist"
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_launch_service_file_missing (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
message = dbus_message_new_method_call (SERVICE_FILE_MISSING_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 service file missing should fail to auto-start");
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))
{
_dbus_verbose("got service unknown\n");
; /* good, this is expected (only valid when using launch helper) */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully auto-start missing service\n");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
#define SERVICE_USER_MISSING_NAME "org.freedesktop.DBus.TestSuiteNoUser"
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_launch_service_user_missing (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
message = dbus_message_new_method_call (SERVICE_USER_MISSING_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 service which should fail to auto-start (missing User)");
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_warn ("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_FILE_INVALID))
{
_dbus_verbose("got service file invalid\n");
; /* good, this is expected (only valid when using launch helper) */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully auto-start missing service\n");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
#define SERVICE_EXEC_MISSING_NAME "org.freedesktop.DBus.TestSuiteNoExec"
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_launch_service_exec_missing (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
message = dbus_message_new_method_call (SERVICE_EXEC_MISSING_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 service which should fail to auto-start (missing Exec)");
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_warn ("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))
{
_dbus_verbose("could not activate as invalid service file was not added\n");
; /* good, this is expected as we shouldn't have been added to
* the activation list with a missing Exec key */
}
else if (dbus_message_is_error (message,
DBUS_ERROR_SPAWN_FILE_INVALID))
{
_dbus_verbose("got service file invalid\n");
; /* good, this is allowed, and is the message passed back from the
* launch helper */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully auto-start missing service\n");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
#define SERVICE_SERVICE_MISSING_NAME "org.freedesktop.DBus.TestSuiteNoService"
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_launch_service_service_missing (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
message = dbus_message_new_method_call (SERVICE_SERVICE_MISSING_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 service which should fail to auto-start (missing Service)");
bus_test_run_everything (context);
if (!dbus_connection_get_is_connected (connection))
{
_dbus_warn ("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))
{
_dbus_verbose("could not activate as invalid service file was not added\n");
; /* good, this is expected as we shouldn't have been added to
* the activation list with a missing Exec key */
}
else if (dbus_message_is_error (message,
DBUS_ERROR_SPAWN_FILE_INVALID))
{
_dbus_verbose("got service file invalid\n");
; /* good, this is allowed, and is the message passed back from the
* launch helper */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully auto-start missing service\n");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
#define SHELL_FAIL_SERVICE_NAME "org.freedesktop.DBus.TestSuiteShellEchoServiceFail"
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_shell_fail_service_auto_start (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
message = dbus_message_new_method_call (SHELL_FAIL_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 shell Echo on service which should fail to auto-start");
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_INVALID_ARGS))
{
_dbus_verbose("got invalid args\n");
; /* good, this is expected also */
}
else
{
warn_unexpected (connection, message, "not this error");
goto out;
}
}
else
{
_dbus_warn ("Did not expect to successfully auto-start shell fail service\n");
goto out;
}
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
return retval;
}
#define SHELL_SUCCESS_SERVICE_NAME "org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess"
/* returns TRUE if the correct thing happens,
* but the correct thing may include OOM errors.
*/
static dbus_bool_t
check_shell_service_success_auto_start (BusContext *context,
DBusConnection *connection)
{
DBusMessage *message;
DBusMessage *base_service_message;
dbus_uint32_t serial;
dbus_bool_t retval;
const char *base_service;
const char *argv[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
base_service_message = NULL;
message = dbus_message_new_method_call (SHELL_SUCCESS_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);
/* now wait for the message bus to hear back from the activated
* service.
*/
block_connection_until_message_from_bus (context, connection, "reply to Echo on shell success 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 any messages after auto start %d on %p\n",
serial, connection);
goto out;
}
verbose_message_received (connection, message);
_dbus_verbose (" (after sending %s)\n", "auto start");
/* we should get zero or two ServiceOwnerChanged signals */
if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL)
{
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, "service to exit");
/* Should get a service creation notification for the activated
* service name, or a service deletion on the base service name
*/
message = dbus_connection_borrow_message (connection);
if (message == NULL)
{
_dbus_warn ("No message after auto activation "
"(should be a service announcement)\n");
dbus_connection_return_message (connection, message);
message = NULL;
goto out;
}
message_kind = check_got_service_info (message);
dbus_connection_return_message (connection, message);
message = NULL;
switch (message_kind)
{
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;
}
/* Check that ServiceOwnerChanged (creation) was correctly received */
if (!check_service_auto_activated (context, connection, SHELL_SUCCESS_SERVICE_NAME,
base_service, message))
goto out;
dbus_message_unref (message);
message = NULL;
break;
case GOT_SERVICE_DELETED:
{
/* The service started up and got a base address, but then
* failed to register under SHELL_SUCCESS_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;
break;
}
case GOT_ERROR:
case GOT_SOMETHING_ELSE:
_dbus_warn ("Unexpected message after auto activation\n");
goto out;
}
}
/* OK, now we've dealt with ServiceOwnerChanged signals, now should
* come the method reply (or error) from the initial method call
*/
/* 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 echo message after auto-activation");
message = pop_message_waiting_for_memory (connection);
if (message == NULL)
{
_dbus_warn ("Failed to pop message! Should have been reply from echo message\n");
goto out;
}
if (dbus_message_get_reply_serial (message) != serial)
{
_dbus_warn ("Wrong reply serial\n");
goto out;
}
if (!dbus_message_get_args (message, NULL,
DBUS_TYPE_STRING, &argv[0],
DBUS_TYPE_STRING, &argv[1],
DBUS_TYPE_STRING, &argv[2],
DBUS_TYPE_STRING, &argv[3],
DBUS_TYPE_STRING, &argv[4],
DBUS_TYPE_STRING, &argv[5],
DBUS_TYPE_STRING, &argv[6],
DBUS_TYPE_INVALID))
{
_dbus_warn ("Error getting arguments from return\n");
goto out;
}
/* don't worry about arg[0] as it may be different
depending on the path to the tests
*/
if (strcmp("-test", argv[1]) != 0)
{
_dbus_warn ("Unexpected argv[1] in shell success service test (expected: %s, got: %s)\n",
"-test", argv[1]);
goto out;
}
if (strcmp("that", argv[2]) != 0)
{
_dbus_warn ("Unexpected argv[2] in shell success service test (expected: %s, got: %s)\n",
"that", argv[2]);
goto out;
}
if (strcmp("we get", argv[3]) != 0)
{
_dbus_warn ("Unexpected argv[3] in shell success service test (expected: %s, got: %s)\n",
"we get", argv[3]);
goto out;
}
if (strcmp("back", argv[4]) != 0)
{
_dbus_warn ("Unexpected argv[4] in shell success service test (expected: %s, got: %s)\n",
"back", argv[4]);
goto out;
}
if (strcmp("--what", argv[5]) != 0)
{
_dbus_warn ("Unexpected argv[5] in shell success service test (expected: %s, got: %s)\n",
"--what", argv[5]);
goto out;
}
if (strcmp("we put in", argv[6]) != 0)
{
_dbus_warn ("Unexpected argv[6] in shell success service test (expected: %s, got: %s)\n",
"we put in", argv[6]);
goto out;
}
dbus_message_unref (message);
message = NULL;
if (!check_send_exit_to_service (context, connection,
SHELL_SUCCESS_SERVICE_NAME,
base_service))
goto out;
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
if (base_service_message)
dbus_message_unref (base_service_message);
return retval;
}
typedef struct
{
Check1Func func;
BusContext *context;
} Check1Data;
static dbus_bool_t
check_oom_check1_func (void *data)
{
Check1Data *d = data;
if (! (* d->func) (d->context))
return FALSE;
if (!check_no_leftovers (d->context))
{
_dbus_warn ("Messages were left over, should be covered by test suite\n");
return FALSE;
}
return TRUE;
}
static void
check1_try_iterations (BusContext *context,
const char *description,
Check1Func func)
{
Check1Data d;
d.func = func;
d.context = context;
if (!_dbus_test_oom_handling (description, check_oom_check1_func,
&d))
_dbus_assert_not_reached ("test failed");
}
static dbus_bool_t
check_get_services (BusContext *context,
DBusConnection *connection,
const char *method,
char ***services,
int *len)
{
DBusMessage *message;
dbus_uint32_t serial;
dbus_bool_t retval;
DBusError error;
char **srvs;
int l;
retval = FALSE;
dbus_error_init (&error);
message = NULL;
message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
method);
if (message == NULL)
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 ListActivatableNames/ListNames");
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",
method, 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 ListActivatableNames/ListNames");
goto out;
}
retry_get_property:
if (!dbus_message_get_args (message, &error,
DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING,
&srvs, &l,
DBUS_TYPE_INVALID))
{
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
_dbus_verbose ("no memory to list services by %s\n", method);
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_ARRAY from %s\n", method);
goto out;
}
} else {
*services = srvs;
*len = l;
}
}
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_list_services (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;
char **services;
int len;
_dbus_verbose ("check_list_services for %p\n", connection);
if (!check_get_services (context, connection, "ListActivatableNames", &services, &len))
{
return TRUE;
}
if (!_dbus_string_array_contains ((const char **)services, existent))
{
_dbus_warn ("Did not get the expected %s from ListActivatableNames\n", existent);
dbus_free_string_array (services);
return FALSE;
}
dbus_free_string_array (services);
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:
case GOT_ERROR:
case GOT_SERVICE_DELETED:
_dbus_warn ("Unexpected message after ActivateService "
"(should be an error or a service announcement)\n");
goto out;
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;
}
break;
}
}
if (!check_get_services (context, connection, "ListNames", &services, &len))
{
return TRUE;
}
if (!_dbus_string_array_contains ((const char **)services, existent))
{
_dbus_warn ("Did not get the expected %s from ListNames\n", existent);
goto out;
}
dbus_free_string_array (services);
if (!check_send_exit_to_service (context, connection,
EXISTENT_SERVICE_NAME, base_service))
goto out;
retval = TRUE;
out:
if (message)
dbus_message_unref (message);
if (base_service_message)
dbus_message_unref (base_service_message);
return retval;
}
typedef struct
{
Check2Func func;
BusContext *context;
DBusConnection *connection;
} Check2Data;
static dbus_bool_t
check_oom_check2_func (void *data)
{
Check2Data *d = data;
if (! (* d->func) (d->context, d->connection))
return FALSE;
if (!check_no_leftovers (d->context))
{
_dbus_warn ("Messages were left over, should be covered by test suite\n");
return FALSE;
}
return TRUE;
}
static void
check2_try_iterations (BusContext *context,
DBusConnection *connection,
const char *description,
Check2Func func)
{
Check2Data d;
d.func = func;
d.context = context;
d.connection = connection;
if (!_dbus_test_oom_handling (description, check_oom_check2_func,
&d))
{
_dbus_warn ("%s failed during oom\n", description);
_dbus_assert_not_reached ("test failed");
}
}
static dbus_bool_t
setenv_TEST_LAUNCH_HELPER_CONFIG(const DBusString *test_data_dir,
const char *filename)
{
DBusString full;
DBusString file;
if (!_dbus_string_init (&full))
return FALSE;
if (!_dbus_string_copy (test_data_dir, 0, &full, 0))
{
_dbus_string_free (&full);
return FALSE;
}
_dbus_string_init_const (&file, filename);
if (!_dbus_concat_dir_and_file (&full, &file))
{
_dbus_string_free (&full);
return FALSE;
}
_dbus_verbose ("Setting TEST_LAUNCH_HELPER_CONFIG to '%s'\n",
_dbus_string_get_const_data (&full));
_dbus_setenv ("TEST_LAUNCH_HELPER_CONFIG", _dbus_string_get_const_data (&full));
_dbus_string_free (&full);
return TRUE;
}
static dbus_bool_t
bus_dispatch_test_conf (const DBusString *test_data_dir,
const char *filename,
dbus_bool_t use_launcher)
{
BusContext *context;
DBusConnection *foo;
DBusConnection *bar;
DBusConnection *baz;
DBusError error;
/* 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");
bar = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
if (bar == NULL)
_dbus_assert_not_reached ("could not alloc connection");
if (!bus_setup_debug_client (bar))
_dbus_assert_not_reached ("could not set up connection");
spin_connection_until_authenticated (context, bar);
if (!check_hello_message (context, bar))
_dbus_assert_not_reached ("hello message failed");
if (!check_add_match_all (context, bar))
_dbus_assert_not_reached ("AddMatch message failed");
baz = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
if (baz == NULL)
_dbus_assert_not_reached ("could not alloc connection");
if (!bus_setup_debug_client (baz))
_dbus_assert_not_reached ("could not set up connection");
spin_connection_until_authenticated (context, baz);
if (!check_hello_message (context, baz))
_dbus_assert_not_reached ("hello message failed");
if (!check_add_match_all (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_get_connection_unix_user (context, baz))
_dbus_assert_not_reached ("GetConnectionUnixUser message failed");
if (!check_get_connection_unix_process_id (context, baz))
_dbus_assert_not_reached ("GetConnectionUnixProcessID message failed");
#endif
if (!check_list_services (context, baz))
_dbus_assert_not_reached ("ListActivatableNames message failed");
if (!check_no_leftovers (context))
{
_dbus_warn ("Messages were left over after setting up initial connections\n");
_dbus_assert_not_reached ("initial connection setup failed");
}
check1_try_iterations (context, "create_and_hello",
check_hello_connection);
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",
check_shell_fail_service_auto_start);
/* specific to launcher */
if (use_launcher)
if (!check_launch_service_file_missing (context, foo))
_dbus_assert_not_reached ("did not get service file not found error");
#if 0
/* Note: need to resolve some issues with the testing code in order to run
* this in oom (handle that we sometimes don't get replies back from the bus
* when oom happens, without blocking the test).
*/
check2_try_iterations (context, foo, "existent_service_auto_auto_start",
check_existent_service_auto_start);
#endif
if (!check_existent_service_auto_start (context, foo))
_dbus_assert_not_reached ("existent service auto start failed");
if (!check_shell_service_success_auto_start (context, foo))
_dbus_assert_not_reached ("shell success service auto start failed");
_dbus_verbose ("Disconnecting foo, bar, and baz\n");
kill_client_connection_unchecked (foo);
kill_client_connection_unchecked (bar);
kill_client_connection_unchecked (baz);
bus_context_unref (context);
return TRUE;
}
static dbus_bool_t
bus_dispatch_test_conf_fail (const DBusString *test_data_dir,
const char *filename)
{
BusContext *context;
DBusConnection *foo;
DBusError error;
/* 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");
/* this only tests the activation.c user check */
if (!check_launch_service_user_missing (context, foo))
_dbus_assert_not_reached ("user missing did not trigger error");
/* this only tests the desktop.c exec check */
if (!check_launch_service_exec_missing (context, foo))
_dbus_assert_not_reached ("exec missing did not trigger error");
/* this only tests the desktop.c service check */
if (!check_launch_service_service_missing (context, foo))
_dbus_assert_not_reached ("service missing did not trigger error");
_dbus_verbose ("Disconnecting foo\n");
kill_client_connection_unchecked (foo);
bus_context_unref (context);
return TRUE;
}
dbus_bool_t
bus_dispatch_test (const DBusString *test_data_dir)
{
/* 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
/* run launch-helper activation tests */
_dbus_verbose ("Launch helper activation tests\n");
if (!bus_dispatch_test_conf (test_data_dir,
"valid-config-files-system/debug-allow-all-pass.conf", TRUE))
return FALSE;
/* run select launch-helper activation tests on broken service files */
if (!bus_dispatch_test_conf_fail (test_data_dir,
"valid-config-files-system/debug-allow-all-fail.conf"))
return FALSE;
#endif
return TRUE;
}
dbus_bool_t
bus_dispatch_sha1_test (const DBusString *test_data_dir)
{
BusContext *context;
DBusConnection *foo;
DBusError error;
dbus_error_init (&error);
/* Test SHA1 authentication */
_dbus_verbose ("Testing SHA1 context\n");
context = bus_context_new_test (test_data_dir,
"valid-config-files/debug-allow-all-sha1.conf");
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_add_match_all (context, foo))
_dbus_assert_not_reached ("addmatch message failed");
if (!check_no_leftovers (context))
{
_dbus_warn ("Messages were left over after setting up initial SHA-1 connection\n");
_dbus_assert_not_reached ("initial connection setup failed");
}
check1_try_iterations (context, "create_and_hello_sha1",
check_hello_connection);
kill_client_connection_unchecked (foo);
bus_context_unref (context);
return TRUE;
}
#ifdef HAVE_UNIX_FD_PASSING
dbus_bool_t
bus_unix_fds_passing_test(const DBusString *test_data_dir)
{
BusContext *context;
DBusConnection *foo, *bar;
DBusError error;
DBusMessage *m;
int one[2], two[2], x, y, z;
char r;
dbus_error_init (&error);
context = bus_context_new_test (test_data_dir, "valid-config-files/debug-allow-all.conf");
if (context == NULL)
_dbus_assert_not_reached ("could not alloc context");
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_add_match_all (context, foo))
_dbus_assert_not_reached ("AddMatch message failed");
bar = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
if (bar == NULL)
_dbus_assert_not_reached ("could not alloc connection");
if (!bus_setup_debug_client (bar))
_dbus_assert_not_reached ("could not set up connection");
spin_connection_until_authenticated (context, bar);
if (!check_hello_message (context, bar))
_dbus_assert_not_reached ("hello message failed");
if (!check_add_match_all (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)))
_dbus_assert_not_reached("Failed to allocate pipe #1");
if (!(_dbus_full_duplex_pipe(two, two+1, TRUE, &error)))
_dbus_assert_not_reached("Failed to allocate pipe #2");
if (!dbus_message_append_args(m,
DBUS_TYPE_UNIX_FD, one,
DBUS_TYPE_UNIX_FD, two,
DBUS_TYPE_UNIX_FD, two,
DBUS_TYPE_INVALID))
_dbus_assert_not_reached("Failed to attach fds.");
if (!_dbus_close(one[0], &error))
_dbus_assert_not_reached("Failed to close pipe #1 ");
if (!_dbus_close(two[0], &error))
_dbus_assert_not_reached("Failed to close pipe #2 ");
if (!(dbus_connection_can_send_type(foo, DBUS_TYPE_UNIX_FD)))
_dbus_assert_not_reached("Connection cannot do fd passing");
if (!(dbus_connection_can_send_type(bar, DBUS_TYPE_UNIX_FD)))
_dbus_assert_not_reached("Connection cannot do fd passing");
if (!dbus_connection_send (foo, m, NULL))
_dbus_assert_not_reached("Failed to send fds");
dbus_message_unref(m);
bus_test_run_clients_loop (SEND_PENDING (foo));
bus_test_run_everything (context);
block_connection_until_message_from_bus (context, foo, "unix fd reception on foo");
if (!(m = pop_message_waiting_for_memory (foo)))
_dbus_assert_not_reached("Failed to receive msg");
if (!dbus_message_is_signal(m, "a.b.c", "d"))
_dbus_assert_not_reached("bogus message received");
dbus_message_unref(m);
block_connection_until_message_from_bus (context, bar, "unix fd reception on bar");
if (!(m = pop_message_waiting_for_memory (bar)))
_dbus_assert_not_reached("Failed to receive msg");
if (!dbus_message_is_signal(m, "a.b.c", "d"))
_dbus_assert_not_reached("bogus message received");
if (!dbus_message_get_args(m,
&error,
DBUS_TYPE_UNIX_FD, &x,
DBUS_TYPE_UNIX_FD, &y,
DBUS_TYPE_UNIX_FD, &z,
DBUS_TYPE_INVALID))
_dbus_assert_not_reached("Failed to parse fds.");
dbus_message_unref(m);
if (write(x, "X", 1) != 1)
_dbus_assert_not_reached("Failed to write to pipe #1");
if (write(y, "Y", 1) != 1)
_dbus_assert_not_reached("Failed to write to pipe #2");
if (write(z, "Z", 1) != 1)
_dbus_assert_not_reached("Failed to write to pipe #2/2nd fd");
if (!_dbus_close(x, &error))
_dbus_assert_not_reached("Failed to close pipe #1/other side ");
if (!_dbus_close(y, &error))
_dbus_assert_not_reached("Failed to close pipe #2/other side ");
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')
_dbus_assert_not_reached("Failed to read value from pipe.");
if (read(two[1], &r, 1) != 1 || r != 'Y')
_dbus_assert_not_reached("Failed to read value from pipe.");
if (read(two[1], &r, 1) != 1 || r != 'Z')
_dbus_assert_not_reached("Failed to read value from pipe.");
if (!_dbus_close(one[1], &error))
_dbus_assert_not_reached("Failed to close pipe #1 ");
if (!_dbus_close(two[1], &error))
_dbus_assert_not_reached("Failed to close pipe #2 ");
_dbus_verbose ("Disconnecting foo\n");
kill_client_connection_unchecked (foo);
_dbus_verbose ("Disconnecting bar\n");
kill_client_connection_unchecked (bar);
bus_context_unref (context);
return TRUE;
}
#endif
#endif /* DBUS_BUILD_TESTS */