X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=bus%2Fdispatch.c;h=afb85a7397c22c9aae3f9dc885981c77127f4f14;hb=dbecdeabb20e0ce11121819c63373f0afba57c58;hp=52214dbfb7fedd78b1f1d7a44fda08e91416d5e0;hpb=fa05de9230d62e7c427b5313796fc6ccd4d0ff60;p=platform%2Fupstream%2Fdbus.git diff --git a/bus/dispatch.c b/bus/dispatch.c index 52214db..afb85a7 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -1,10 +1,11 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dispatch.c Message dispatcher * * Copyright (C) 2003 CodeFactory AB - * Copyright (C) 2003 Red Hat, Inc. + * Copyright (C) 2003, 2004, 2005 Red Hat, Inc. + * Copyright (C) 2004 Imendio HB * - * Licensed under the Academic Free License version 1.2 + * 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 @@ -18,44 +19,60 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ +#include #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 "loop.h" #include #include -static int message_handler_slot = -1; -static int message_handler_slot_refcount; +#ifdef HAVE_UNIX_FD_PASSING +#include +#include +#endif -typedef struct -{ - DBusMessage *message; - BusTransaction *transaction; - DBusError *error; -} SendMessageData; +#ifdef DBUS_UNIX +#define TEST_CONNECTION "debug-pipe:name=test-server" +#else +#define TEST_CONNECTION "tcp:host=localhost,port=1234" +#endif static dbus_bool_t -send_one_message (DBusConnection *connection, void *data) +send_one_message (DBusConnection *connection, + BusContext *context, + DBusConnection *sender, + DBusConnection *addressed_recipient, + DBusMessage *message, + BusTransaction *transaction, + DBusError *error) { - SendMessageData *d = data; - - if (!bus_connection_is_active (connection)) - return TRUE; + if (!bus_context_check_security_policy (context, transaction, + sender, + addressed_recipient, + connection, + message, + NULL)) + return TRUE; /* silently don't send it */ - if (!bus_transaction_send_message (d->transaction, - connection, - d->message)) + 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 (d->error); + BUS_SET_OOM (error); return FALSE; } @@ -63,103 +80,112 @@ send_one_message (DBusConnection *connection, void *data) } dbus_bool_t -bus_dispatch_broadcast_message (BusTransaction *transaction, - DBusMessage *message, - DBusError *error) +bus_dispatch_matches (BusTransaction *transaction, + DBusConnection *sender, + DBusConnection *addressed_recipient, + DBusMessage *message, + DBusError *error) { DBusError tmp_error; - SendMessageData d; 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); - connections = bus_transaction_get_connections (transaction); - - dbus_error_init (&tmp_error); - d.message = message; - d.transaction = transaction; - d.error = &tmp_error; - - bus_connections_foreach (connections, send_one_message, &d); + context = bus_transaction_get_context (transaction); - if (dbus_error_is_set (&tmp_error)) + /* First, send the message to the addressed_recipient, if there is one. */ + if (addressed_recipient != NULL) { - dbus_move_error (&tmp_error, error); - return FALSE; - } - else - return TRUE; -} + if (!bus_context_check_security_policy (context, transaction, + sender, addressed_recipient, + addressed_recipient, + message, error)) + return FALSE; -static dbus_bool_t -send_service_nonexistent_error (BusTransaction *transaction, - DBusConnection *connection, - const char *service_name, - DBusMessage *in_reply_to, - DBusError *error) -{ - DBusMessage *error_reply; - DBusString error_message; - const char *error_str; + 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; + } - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - /* Trying to send a message to a non-existant service, - * bounce back an error message. - */ - - if (!_dbus_string_init (&error_message)) - { - BUS_SET_OOM (error); - return FALSE; + /* Dispatch the message */ + if (!bus_transaction_send (transaction, addressed_recipient, message)) + { + BUS_SET_OOM (error); + return FALSE; + } } - if (!_dbus_string_append (&error_message, "Service \"") || - !_dbus_string_append (&error_message, service_name) || - !_dbus_string_append (&error_message, "\" does not exist")) + /* 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)) { - _dbus_string_free (&error_message); BUS_SET_OOM (error); return FALSE; } - - error_str = _dbus_string_get_const_data (&error_message); - error_reply = dbus_message_new_error_reply (in_reply_to, - DBUS_ERROR_SERVICE_DOES_NOT_EXIST, - error_str); - _dbus_string_free (&error_message); - - if (error_reply == NULL) + link = _dbus_list_get_first_link (&recipients); + while (link != NULL) { - BUS_SET_OOM (error); - return FALSE; + 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); } - - if (!bus_transaction_send_message (transaction, connection, error_reply)) + + _dbus_list_clear (&recipients); + + if (dbus_error_is_set (&tmp_error)) { - dbus_message_unref (error_reply); - BUS_SET_OOM (error); + dbus_move_error (&tmp_error, error); return FALSE; } - - dbus_message_unref (error_reply); - - return TRUE; + else + return TRUE; } -static void +static DBusHandlerResult bus_dispatch (DBusConnection *connection, DBusMessage *message) { - const char *sender, *service_name, *message_name; + 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); @@ -169,35 +195,52 @@ bus_dispatch (DBusConnection *connection, * until we can. */ while (!bus_connection_preallocate_oom_error (connection)) - bus_wait_for_memory (); + _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); - service_name = dbus_message_get_service (message); - message_name = dbus_message_get_name (message); - - _dbus_assert (message_name != NULL); /* DBusMessageLoader is supposed to check this */ +#ifdef DBUS_ENABLE_VERBOSE_MODE + { + const char *interface_name, *member_name, *error_name; - _dbus_verbose ("DISPATCH: %s to %s\n", - message_name, service_name ? service_name : "peer"); + 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, this is a message to the bus daemon, not intended - * to actually go "on the bus"; e.g. a peer-to-peer ping. Handle these - * immediately, especially disconnection messages. + /* 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 (strcmp (message_name, DBUS_MESSAGE_LOCAL_DISCONNECT) == 0) - bus_connection_disconnected (connection); + { + if (dbus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, + "Disconnected")) + { + bus_connection_disconnected (connection); + goto out; + } - /* DBusConnection also handles some of these automatically, we leave - * it to do so. - */ - 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; + } } - - _dbus_assert (service_name != NULL); /* this message is intended for bus routing */ /* Create our transaction */ transaction = bus_transaction_new (context); @@ -224,60 +267,82 @@ bus_dispatch (DBusConnection *connection, * reallocated, and thus the service_name pointer will become * invalid. */ - service_name = dbus_message_get_service (message); + service_name = dbus_message_get_destination (message); } - - if (strcmp (service_name, DBUS_SERVICE_DBUS) == 0) /* to bus driver */ + + 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; + 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_disconnect (connection); - } - /* FIXME what if we un-special-case this service and just have a flag - * on services that all service owners will get messages to it, not just - * the primary owner. - */ - else if (strcmp (service_name, DBUS_SERVICE_BROADCAST) == 0) /* spam! */ - { - if (!bus_dispatch_broadcast_message (transaction, message, &error)) - goto out; + dbus_connection_close (connection); + goto out; } - else /* route to named service */ + 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) - { - if (!send_service_nonexistent_error (transaction, connection, - service_name, - message, &error)) - goto out; - } - else + if (service == NULL && dbus_message_get_auto_start (message)) { - _dbus_assert (bus_service_get_primary_owner (service) != NULL); - - /* Dispatch the message */ - if (!bus_transaction_send_message (transaction, - bus_service_get_primary_owner (service), - 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)) { - BUS_SET_OOM (&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)) @@ -287,6 +352,7 @@ bus_dispatch (DBusConnection *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)) { @@ -305,12 +371,11 @@ bus_dispatch (DBusConnection *connection, * 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) { @@ -319,6 +384,7 @@ bus_dispatch (DBusConnection *connection, } } } + dbus_error_free (&error); } @@ -329,185 +395,249 @@ bus_dispatch (DBusConnection *connection, } dbus_connection_unref (connection); + + return result; } static DBusHandlerResult -bus_dispatch_message_handler (DBusMessageHandler *handler, - DBusConnection *connection, - DBusMessage *message, - void *user_data) +bus_dispatch_message_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) { - bus_dispatch (connection, message); + 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 DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + return TRUE; } -static dbus_bool_t -message_handler_slot_ref (void) +void +bus_dispatch_remove_connection (DBusConnection *connection) { - if (message_handler_slot < 0) - { - message_handler_slot = dbus_connection_allocate_data_slot (); - - if (message_handler_slot < 0) - return FALSE; + /* 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); +} - _dbus_assert (message_handler_slot_refcount == 0); - } +#ifdef DBUS_BUILD_TESTS - message_handler_slot_refcount += 1; +#include - return TRUE; -} +/* 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 -message_handler_slot_unref (void) +block_connection_until_message_from_bus (BusContext *context, + DBusConnection *connection, + const char *what_is_expected) { - _dbus_assert (message_handler_slot_refcount > 0); - - message_handler_slot_refcount -= 1; + _dbus_verbose ("expecting: %s\n", what_is_expected); - if (message_handler_slot_refcount == 0) + while (dbus_connection_get_dispatch_status (connection) == + DBUS_DISPATCH_COMPLETE && + dbus_connection_get_is_connected (connection)) { - dbus_connection_free_data_slot (message_handler_slot); - message_handler_slot = -1; + bus_test_run_bus_loop (context, TRUE); + bus_test_run_clients_loop (FALSE); } } static void -free_message_handler (void *data) +spin_connection_until_authenticated (BusContext *context, + DBusConnection *connection) { - DBusMessageHandler *handler = data; - - _dbus_assert (message_handler_slot >= 0); - _dbus_assert (message_handler_slot_refcount > 0); - - dbus_message_handler_unref (handler); - message_handler_slot_unref (); + _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); } -dbus_bool_t -bus_dispatch_add_connection (DBusConnection *connection) +/* compensate for fact that pop_message() can return #NULL due to OOM */ +static DBusMessage* +pop_message_waiting_for_memory (DBusConnection *connection) { - DBusMessageHandler *handler; - - if (!message_handler_slot_ref ()) - return FALSE; - - handler = dbus_message_handler_new (bus_dispatch_message_handler, NULL, NULL); - if (handler == NULL) - { - message_handler_slot_unref (); - return FALSE; - } - - if (!dbus_connection_add_filter (connection, handler)) - { - dbus_message_handler_unref (handler); - message_handler_slot_unref (); - - return FALSE; - } + while (dbus_connection_get_dispatch_status (connection) == + DBUS_DISPATCH_NEED_MEMORY) + _dbus_wait_for_memory (); - _dbus_assert (message_handler_slot >= 0); - _dbus_assert (message_handler_slot_refcount > 0); - - if (!dbus_connection_set_data (connection, - message_handler_slot, - handler, - free_message_handler)) - { - dbus_message_handler_unref (handler); - message_handler_slot_unref (); + return dbus_connection_pop_message (connection); +} - return FALSE; - } +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 TRUE; + return dbus_connection_borrow_message (connection); } -void -bus_dispatch_remove_connection (DBusConnection *connection) +static void +warn_unexpected_real (DBusConnection *connection, + DBusMessage *message, + const char *expected, + const char *function, + int line) { - /* Here we tell the bus driver that we want to get off. */ - bus_driver_remove_connection (connection); - - dbus_connection_set_data (connection, - message_handler_slot, - NULL, NULL); + 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); } -#ifdef DBUS_BUILD_TESTS +#define warn_unexpected(connection, message, expected) \ + warn_unexpected_real (connection, message, expected, _DBUS_FUNCTION_NAME, __LINE__) -typedef dbus_bool_t (* Check1Func) (BusContext *context); -typedef dbus_bool_t (* Check2Func) (BusContext *context, - DBusConnection *connection); +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); +} -static dbus_bool_t check_no_leftovers (BusContext *context); +typedef enum +{ + SERVICE_CREATED, + OWNER_CHANGED, + SERVICE_DELETED +} ServiceInfoKind; typedef struct { + ServiceInfoKind expected_kind; const char *expected_service_name; dbus_bool_t failed; -} CheckServiceDeletedData; + DBusConnection *skip_connection; +} CheckServiceOwnerChangedData; static dbus_bool_t -check_service_deleted_foreach (DBusConnection *connection, - void *data) +check_service_owner_changed_foreach (DBusConnection *connection, + void *data) { - CheckServiceDeletedData *d = data; + CheckServiceOwnerChangedData *d = data; DBusMessage *message; DBusError error; - char *service_name; + 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; - service_name = NULL; - message = dbus_connection_pop_message (connection); + message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a message on %p, expecting %s\n", - connection, DBUS_MESSAGE_SERVICE_DELETED); + connection, "NameOwnerChanged"); goto out; } - else if (!dbus_message_name_is (message, DBUS_MESSAGE_SERVICE_DELETED)) + else if (!dbus_message_is_signal (message, + DBUS_INTERFACE_DBUS, + "NameOwnerChanged")) { - _dbus_warn ("Received message %s on %p, expecting %s\n", - dbus_message_get_name (message), - connection, DBUS_MESSAGE_SERVICE_DELETED); + warn_unexpected (connection, message, "NameOwnerChanged"); + goto out; } else { - if (!dbus_message_get_args (message, &error, - DBUS_TYPE_STRING, &service_name, - DBUS_TYPE_INVALID)) + 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_verbose ("no memory to get service name arg\n"); + dbus_error_free (&error); + _dbus_wait_for_memory (); + goto reget_service_info_data; } else { - _dbus_assert (dbus_error_is_set (&error)); - _dbus_warn ("Did not get the expected single string argument\n"); + _dbus_warn ("Did not get the expected arguments\n"); goto out; } } - else if (strcmp (service_name, d->expected_service_name) != 0) + + 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 deletion of service %s, got deletion of %s\n", + _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_free (service_name); dbus_error_free (&error); if (message) @@ -516,29 +646,30 @@ check_service_deleted_foreach (DBusConnection *connection, return !d->failed; } + static void kill_client_connection (BusContext *context, DBusConnection *connection) { char *base_service; const char *s; - CheckServiceDeletedData csdd; + CheckServiceOwnerChangedData socd; _dbus_verbose ("killing connection %p\n", connection); - s = dbus_bus_get_base_service (connection); + s = dbus_bus_get_unique_name (connection); _dbus_assert (s != NULL); while ((base_service = _dbus_strdup (s)) == NULL) - bus_wait_for_memory (); + _dbus_wait_for_memory (); dbus_connection_ref (connection); /* kick in the disconnect handler that unrefs the connection */ - dbus_connection_disconnect (connection); - - bus_test_flush_bus (context); + dbus_connection_close (connection); + bus_test_run_everything (context); + _dbus_assert (bus_test_client_listed (connection)); /* Run disconnect handler in test.c */ @@ -550,16 +681,18 @@ kill_client_connection (BusContext *context, connection = NULL; _dbus_assert (!bus_test_client_listed (connection)); - csdd.expected_service_name = base_service; - csdd.failed = FALSE; - - bus_test_clients_foreach (check_service_deleted_foreach, - &csdd); + 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 (csdd.failed) - _dbus_assert_not_reached ("didn't get the expected ServiceDeleted messages"); + 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"); @@ -574,12 +707,13 @@ kill_client_connection_unchecked (DBusConnection *connection) _dbus_verbose ("Unchecked kill of connection %p\n", connection); dbus_connection_ref (connection); - dbus_connection_disconnect (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_connection_unref (connection); + _dbus_assert (!bus_test_client_listed (connection)); + dbus_connection_unref (connection); } typedef struct @@ -594,11 +728,11 @@ check_no_messages_foreach (DBusConnection *connection, CheckNoMessagesData *d = data; DBusMessage *message; - message = dbus_connection_pop_message (connection); + message = pop_message_waiting_for_memory (connection); if (message != NULL) { - _dbus_warn ("Received message %s on %p, expecting no messages\n", - dbus_message_get_name (message), connection); + warn_unexpected (connection, message, "no messages"); + d->failed = TRUE; } @@ -607,177 +741,149 @@ check_no_messages_foreach (DBusConnection *connection, return !d->failed; } -typedef struct +static dbus_bool_t +check_no_leftovers (BusContext *context) { - DBusConnection *skip_connection; - const char *expected_service_name; - dbus_bool_t failed; -} CheckServiceCreatedData; + CheckNoMessagesData nmd; + + nmd.failed = FALSE; + bus_test_clients_foreach (check_no_messages_foreach, + &nmd); + + if (nmd.failed) + { + _dbus_verbose ("%s: leftover message found\n", + _DBUS_FUNCTION_NAME); + 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_service_created_foreach (DBusConnection *connection, - void *data) +check_hello_message (BusContext *context, + DBusConnection *connection) { - CheckServiceCreatedData *d = data; DBusMessage *message; + DBusMessage *name_message; + dbus_uint32_t serial; + dbus_bool_t retval; DBusError error; - char *service_name; - - if (connection == d->skip_connection) - return TRUE; + const char *name; + const char *acquired; + retval = FALSE; dbus_error_init (&error); - d->failed = TRUE; - service_name = NULL; + name = NULL; + acquired = NULL; + message = NULL; + name_message = NULL; + + _dbus_verbose ("check_hello_message for %p\n", connection); - message = dbus_connection_pop_message (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_warn ("Did not receive a message on %p, expecting %s\n", - connection, DBUS_MESSAGE_SERVICE_CREATED); - goto out; + dbus_message_unref (message); + dbus_connection_unref (connection); + return TRUE; } - else if (!dbus_message_name_is (message, DBUS_MESSAGE_SERVICE_CREATED)) + + _dbus_assert (dbus_message_has_signature (message, "")); + + dbus_message_unref (message); + message = NULL; + + if (!dbus_connection_get_is_connected (connection)) { - _dbus_warn ("Received message %s on %p, expecting %s\n", - dbus_message_get_name (message), - connection, DBUS_MESSAGE_SERVICE_CREATED); - goto out; - } - else - { - 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_verbose ("no memory to get service name arg\n"); - } - else - { - _dbus_assert (dbus_error_is_set (&error)); - _dbus_warn ("Did not get the expected single string argument\n"); - goto out; - } - } - else if (strcmp (service_name, d->expected_service_name) != 0) - { - _dbus_warn ("expected creation of service %s, got creation of %s\n", - d->expected_service_name, - service_name); - goto out; - } + _dbus_verbose ("connection was disconnected (presumably auth failed)\n"); + + dbus_connection_unref (connection); + + return TRUE; } - - d->failed = FALSE; - - out: - dbus_free (service_name); - dbus_error_free (&error); - - 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) - 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; - dbus_int32_t serial; - dbus_bool_t retval; - DBusError error; - char *name; - char *acquired; - - dbus_error_init (&error); - name = NULL; - acquired = NULL; - message = dbus_message_new (DBUS_SERVICE_DBUS, - DBUS_MESSAGE_HELLO); + /* send our message */ + bus_test_run_clients_loop (SEND_PENDING (connection)); - if (message == NULL) - return TRUE; - - if (!dbus_connection_send (connection, message, &serial)) + if (!dbus_connection_get_is_connected (connection)) { - dbus_message_unref (message); + _dbus_verbose ("connection was disconnected (presumably auth failed)\n"); + + dbus_connection_unref (connection); + return TRUE; } - - dbus_message_unref (message); - message = NULL; - bus_test_flush_bus (context); + 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_verbose ("connection was disconnected (presumably auth failed)\n"); + + dbus_connection_unref (connection); + return TRUE; } + + dbus_connection_unref (connection); - retval = FALSE; - - message = dbus_connection_pop_message (connection); + message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", - DBUS_MESSAGE_HELLO, serial, connection); + "Hello", serial, connection); goto out; } - _dbus_verbose ("Received %s on %p\n", - dbus_message_get_name (message), connection); + verbose_message_received (connection, message); - if (dbus_message_get_is_error (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_name_is (message, - DBUS_ERROR_NO_MEMORY)) + if (dbus_message_is_error (message, + DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else { - _dbus_warn ("Did not expect error %s\n", - dbus_message_get_name (message)); + warn_unexpected (connection, message, "not this error"); + goto out; } } else { - CheckServiceCreatedData scd; + CheckServiceOwnerChangedData socd; - if (dbus_message_name_is (message, - DBUS_MESSAGE_HELLO)) + if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) { ; /* good, expected */ } else { - _dbus_warn ("Did not expect reply %s\n", - dbus_message_get_name (message)); + warn_unexpected (connection, message, "method return for Hello"); + goto out; } @@ -790,7 +896,7 @@ check_hello_message (BusContext *context, { _dbus_verbose ("no memory to get service name arg from hello\n"); dbus_error_free (&error); - bus_wait_for_memory (); + _dbus_wait_for_memory (); goto retry_get_hello_name; } else @@ -803,25 +909,34 @@ check_hello_message (BusContext *context, _dbus_verbose ("Got hello name: %s\n", name); - while (!dbus_bus_set_base_service (connection, name)) - bus_wait_for_memory (); + while (!dbus_bus_set_unique_name (connection, name)) + _dbus_wait_for_memory (); - scd.skip_connection = NULL; - scd.failed = FALSE; - scd.expected_service_name = name; - bus_test_clients_foreach (check_service_created_foreach, - &scd); + 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 (scd.failed) + if (socd.failed) goto out; - + + name_message = message; /* Client should also have gotten ServiceAcquired */ - dbus_message_unref (message); - message = dbus_connection_pop_message (connection); + + message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Expecting %s, got nothing\n", - DBUS_MESSAGE_SERVICE_ACQUIRED); + "NameAcquired"); + goto out; + } + if (! dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, + "NameAcquired")) + { + _dbus_warn ("Expecting %s, got smthg else\n", + "NameAcquired"); goto out; } @@ -834,7 +949,7 @@ check_hello_message (BusContext *context, { _dbus_verbose ("no memory to get service name arg from acquired\n"); dbus_error_free (&error); - bus_wait_for_memory (); + _dbus_wait_for_memory (); goto retry_get_acquired_name; } else @@ -853,6 +968,7 @@ check_hello_message (BusContext *context, acquired, name); goto out; } + acquired = NULL; } if (!check_no_leftovers (context)) @@ -861,13 +977,15 @@ check_hello_message (BusContext *context, retval = TRUE; out: - dbus_error_free (&error); + _dbus_verbose ("ending %s retval = %d\n", _DBUS_FUNCTION_NAME, retval); - dbus_free (name); - dbus_free (acquired); + dbus_error_free (&error); if (message) dbus_message_unref (message); + + if (name_message) + dbus_message_unref (name_message); return retval; } @@ -876,141 +994,3916 @@ check_hello_message (BusContext *context, * but the correct thing may include OOM errors. */ static dbus_bool_t -check_hello_connection (BusContext *context) +check_double_hello_message (BusContext *context, + DBusConnection *connection) { - DBusConnection *connection; + DBusMessage *message; + dbus_uint32_t serial; + dbus_bool_t retval; DBusError error; + retval = FALSE; dbus_error_init (&error); + message = NULL; - connection = dbus_connection_open ("debug-pipe:name=test-server", &error); - if (connection == 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_ASSERT_ERROR_IS_SET (&error); - dbus_error_free (&error); + dbus_message_unref (message); return TRUE; } - if (!bus_setup_debug_client (connection)) + 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_connection_disconnect (connection); + _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + dbus_connection_unref (connection); + return TRUE; } - if (!check_hello_message (context, connection)) - return FALSE; - - if (dbus_bus_get_base_service (connection) == NULL) - { - /* We didn't successfully register, so we can't - * do the usual kill_client_connection() checks - */ - kill_client_connection_unchecked (connection); - } - else + dbus_connection_unref (connection); + + message = pop_message_waiting_for_memory (connection); + if (message == NULL) { - kill_client_connection (context, connection); + _dbus_warn ("Did not receive a reply to %s %d on %p\n", + "Hello", serial, connection); + goto out; } - return TRUE; -} - -typedef struct -{ - Check1Func func; - BusContext *context; -} Check1Data; - -static dbus_bool_t -check_oom_check1_func (void *data) -{ - Check1Data *d = data; + verbose_message_received (connection, message); - if (! (* d->func) (d->context)) - return FALSE; + 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 (!check_no_leftovers (d->context)) + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR) { - _dbus_warn ("Messages were left over, should be covered by test suite"); - return FALSE; + warn_unexpected (connection, message, "method return for Hello"); + goto out; } - 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"); + if (!check_no_leftovers (context)) + goto out; + + retval = TRUE; + + out: + dbus_error_free (&error); + + if (message) + dbus_message_unref (message); + + return retval; } -dbus_bool_t -bus_dispatch_test (const DBusString *test_data_dir) +/* 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) { - BusContext *context; - DBusConnection *foo; - DBusConnection *bar; - DBusConnection *baz; + DBusMessage *message; + dbus_uint32_t serial; + dbus_bool_t retval; DBusError error; - - context = bus_context_new_test (test_data_dir, - "valid-config-files/debug-allow-all.conf"); - if (context == NULL) - return FALSE; + 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); - foo = dbus_connection_open ("debug-pipe:name=test-server", &error); - if (foo == NULL) - _dbus_assert_not_reached ("could not alloc connection"); + message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixUser"); - if (!bus_setup_debug_client (foo)) - _dbus_assert_not_reached ("could not set up connection"); + if (message == NULL) + return TRUE; - if (!check_hello_message (context, foo)) - _dbus_assert_not_reached ("hello message failed"); - - bar = dbus_connection_open ("debug-pipe:name=test-server", &error); - if (bar == NULL) - _dbus_assert_not_reached ("could not alloc connection"); + base_service_name = dbus_bus_get_unique_name (connection); - if (!bus_setup_debug_client (bar)) - _dbus_assert_not_reached ("could not set up connection"); + if (!dbus_message_append_args (message, + DBUS_TYPE_STRING, &base_service_name, + DBUS_TYPE_INVALID)) + { + dbus_message_unref (message); + return TRUE; + } - if (!check_hello_message (context, bar)) - _dbus_assert_not_reached ("hello message failed"); - - baz = dbus_connection_open ("debug-pipe:name=test-server", &error); - if (baz == NULL) - _dbus_assert_not_reached ("could not alloc connection"); + if (!dbus_connection_send (connection, message, &serial)) + { + dbus_message_unref (message); + return TRUE; + } - if (!bus_setup_debug_client (baz)) - _dbus_assert_not_reached ("could not set up connection"); + /* send our message */ + bus_test_run_clients_loop (SEND_PENDING (connection)); - if (!check_hello_message (context, baz)) - _dbus_assert_not_reached ("hello message failed"); + dbus_message_unref (message); + message = NULL; - check1_try_iterations (context, "create_and_hello", - check_hello_connection); - - _dbus_verbose ("Disconnecting foo, bar, and baz\n"); + dbus_connection_ref (connection); /* because we may get disconnected */ + block_connection_until_message_from_bus (context, connection, "reply to GetConnectionUnixUser"); - kill_client_connection_unchecked (foo); - kill_client_connection_unchecked (bar); - kill_client_connection_unchecked (baz); + if (!dbus_connection_get_is_connected (connection)) + { + _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + + 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_CONNECTION, &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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + + 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: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + 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_CONNECTION, &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_CONNECTION, &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_CONNECTION, &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_CONNECTION, &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; + + /* 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; + + 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_CONNECTION, &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; + dbus_bool_t b; + 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_CONNECTION, &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_CONNECTION, &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 */