/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dispatch.c Message dispatcher * * Copyright (C) 2003 CodeFactory AB * Copyright (C) 2003, 2004, 2005 Red Hat, Inc. * Copyright (C) 2004 Imendio HB * * Licensed under the Academic Free License version 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #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 #include #include #ifdef HAVE_UNIX_FD_PASSING #include #include #endif /* This is hard-coded in the files in valid-config-files-*. We have to use * the debug-pipe transport because the tests in this file require that * dbus_connection_open_private() does not block. */ #define TEST_DEBUG_PIPE "debug-pipe:name=test-server" static inline const char * nonnull (const char *maybe_null, const char *if_null) { return (maybe_null ? maybe_null : if_null); } static dbus_bool_t send_one_message (DBusConnection *connection, BusContext *context, DBusConnection *sender, DBusConnection *addressed_recipient, DBusMessage *message, BusTransaction *transaction, DBusError *error) { DBusError stack_error = DBUS_ERROR_INIT; if (!bus_context_check_security_policy (context, transaction, sender, addressed_recipient, connection, message, &stack_error)) { if (!bus_transaction_capture_error_reply (transaction, &stack_error, message)) { bus_context_log (context, DBUS_SYSTEM_LOG_WARNING, "broadcast rejected, but not enough " "memory to tell monitors"); } dbus_error_free (&stack_error); return TRUE; /* don't send it but don't return an error either */ } if (dbus_message_contains_unix_fds(message) && !dbus_connection_can_send_type(connection, DBUS_TYPE_UNIX_FD)) { dbus_set_error (&stack_error, DBUS_ERROR_NOT_SUPPORTED, "broadcast cannot be delivered to %s (%s) because " "it does not support receiving Unix fds", bus_connection_get_name (connection), bus_connection_get_loginfo (connection)); if (!bus_transaction_capture_error_reply (transaction, &stack_error, message)) { bus_context_log (context, DBUS_SYSTEM_LOG_WARNING, "broadcast with Unix fd not delivered, but not " "enough memory to tell monitors"); } dbus_error_free (&stack_error); return TRUE; /* don't send it but don't return an error either */ } if (!bus_transaction_send (transaction, connection, message)) { BUS_SET_OOM (error); return FALSE; } return TRUE; } dbus_bool_t bus_dispatch_matches (BusTransaction *transaction, DBusConnection *sender, DBusConnection *addressed_recipient, DBusMessage *message, DBusError *error) { DBusError tmp_error; BusConnections *connections; DBusList *recipients; BusMatchmaker *matchmaker; DBusList *link; BusContext *context; _DBUS_ASSERT_ERROR_IS_CLEAR (error); /* sender and recipient can both be NULL for the bus driver, * or for signals with no particular recipient */ _dbus_assert (sender == NULL || bus_connection_is_active (sender)); _dbus_assert (dbus_message_get_sender (message) != NULL); context = bus_transaction_get_context (transaction); /* First, send the message to the addressed_recipient, if there is one. */ if (addressed_recipient != NULL) { if (!bus_context_check_security_policy (context, transaction, sender, addressed_recipient, addressed_recipient, message, error)) return FALSE; if (dbus_message_contains_unix_fds (message) && !dbus_connection_can_send_type (addressed_recipient, DBUS_TYPE_UNIX_FD)) { dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, "Tried to send message with Unix file descriptors" "to a client that doesn't support that."); return FALSE; } /* Dispatch the message */ if (!bus_transaction_send (transaction, addressed_recipient, message)) { BUS_SET_OOM (error); return FALSE; } } /* Now dispatch to others who look interested in this message */ connections = bus_context_get_connections (context); dbus_error_init (&tmp_error); matchmaker = bus_context_get_matchmaker (context); recipients = NULL; if (!bus_matchmaker_get_recipients (matchmaker, connections, sender, addressed_recipient, message, &recipients)) { BUS_SET_OOM (error); return FALSE; } link = _dbus_list_get_first_link (&recipients); while (link != NULL) { DBusConnection *dest; dest = link->data; if (!send_one_message (dest, context, sender, addressed_recipient, message, transaction, &tmp_error)) break; link = _dbus_list_get_next_link (&recipients, link); } _dbus_list_clear (&recipients); if (dbus_error_is_set (&tmp_error)) { dbus_move_error (&tmp_error, error); return FALSE; } else return TRUE; } static DBusHandlerResult bus_dispatch (DBusConnection *connection, DBusMessage *message) { const char *sender, *service_name; DBusError error; BusTransaction *transaction; BusContext *context; DBusHandlerResult result; DBusConnection *addressed_recipient; result = DBUS_HANDLER_RESULT_HANDLED; transaction = NULL; addressed_recipient = NULL; dbus_error_init (&error); context = bus_connection_get_context (connection); _dbus_assert (context != NULL); /* If we can't even allocate an OOM error, we just go to sleep * until we can. */ while (!bus_connection_preallocate_oom_error (connection)) _dbus_wait_for_memory (); /* Ref connection in case we disconnect it at some point in here */ dbus_connection_ref (connection); /* Monitors aren't meant to send messages to us. */ if (bus_connection_is_monitor (connection)) { sender = bus_connection_get_name (connection); /* should never happen */ if (sender == NULL) sender = "(unknown)"; if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")) { bus_context_log (context, DBUS_SYSTEM_LOG_INFO, "Monitoring connection %s closed.", sender); bus_connection_disconnected (connection); goto out; } else { /* Monitors are not allowed to send messages, because that * probably indicates that the monitor is incorrectly replying * to its eavesdropped messages, and we want the authors of * such monitors to fix them. */ bus_context_log (context, DBUS_SYSTEM_LOG_WARNING, "Monitoring connection %s (%s) is not allowed " "to send messages; closing it. Please fix the " "monitor to not do that. " "(message type=\"%s\" interface=\"%s\" " "member=\"%s\" error name=\"%s\" " "destination=\"%s\")", sender, bus_connection_get_loginfo (connection), dbus_message_type_to_string ( dbus_message_get_type (message)), nonnull (dbus_message_get_interface (message), "(unset)"), nonnull (dbus_message_get_member (message), "(unset)"), nonnull (dbus_message_get_error_name (message), "(unset)"), nonnull (dbus_message_get_destination (message), DBUS_SERVICE_DBUS)); dbus_connection_close (connection); goto out; } } service_name = dbus_message_get_destination (message); #ifdef DBUS_ENABLE_VERBOSE_MODE { const char *interface_name, *member_name, *error_name; interface_name = dbus_message_get_interface (message); member_name = dbus_message_get_member (message); error_name = dbus_message_get_error_name (message); _dbus_verbose ("DISPATCH: %s %s %s to %s\n", interface_name ? interface_name : "(no interface)", member_name ? member_name : "(no member)", error_name ? error_name : "(no error name)", service_name ? service_name : "peer"); } #endif /* DBUS_ENABLE_VERBOSE_MODE */ /* If service_name is NULL, if it's a signal we send it to all * connections with a match rule. If it's not a signal, there * are some special cases here but mostly we just bail out. */ if (service_name == NULL) { if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")) { bus_connection_disconnected (connection); goto out; } if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL) { /* DBusConnection also handles some of these automatically, we leave * it to do so. * * FIXME: this means monitors won't get the opportunity to see * non-signals with NULL destination, or their replies (which in * practice are UnknownMethod errors) */ result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; goto out; } } /* Create our transaction */ transaction = bus_transaction_new (context); if (transaction == NULL) { BUS_SET_OOM (&error); goto out; } /* Assign a sender to the message */ if (bus_connection_is_active (connection)) { sender = bus_connection_get_name (connection); _dbus_assert (sender != NULL); if (!dbus_message_set_sender (message, sender)) { BUS_SET_OOM (&error); goto out; } } else { /* For monitors' benefit: we don't want the sender to be able to * trick the monitor by supplying a forged sender, and we also * don't want the message to have no sender at all. */ if (!dbus_message_set_sender (message, ":not.active.yet")) { BUS_SET_OOM (&error); goto out; } } /* We need to refetch the service name here, because * dbus_message_set_sender can cause the header to be * reallocated, and thus the service_name pointer will become * invalid. */ service_name = dbus_message_get_destination (message); if (!bus_transaction_capture (transaction, connection, message)) { BUS_SET_OOM (&error); goto out; } if (service_name && strcmp (service_name, DBUS_SERVICE_DBUS) == 0) /* to bus driver */ { if (!bus_context_check_security_policy (context, transaction, connection, NULL, NULL, message, &error)) { _dbus_verbose ("Security policy rejected message\n"); goto out; } _dbus_verbose ("Giving message to %s\n", DBUS_SERVICE_DBUS); if (!bus_driver_handle_message (connection, transaction, message, &error)) goto out; } else if (!bus_connection_is_active (connection)) /* clients must talk to bus driver first */ { _dbus_verbose ("Received message from non-registered client. Disconnecting.\n"); dbus_connection_close (connection); goto out; } else if (service_name != NULL) /* route to named service */ { DBusString service_string; BusService *service; BusRegistry *registry; _dbus_assert (service_name != NULL); registry = bus_connection_get_registry (connection); _dbus_string_init_const (&service_string, service_name); service = bus_registry_lookup (registry, &service_string); if (service == NULL && dbus_message_get_auto_start (message)) { BusActivation *activation; /* We can't do the security policy check here, since the addressed * recipient service doesn't exist yet. We do it before sending the * message after the service has been created. */ activation = bus_connection_get_activation (connection); if (!bus_activation_activate_service (activation, connection, transaction, TRUE, message, service_name, &error)) { _DBUS_ASSERT_ERROR_IS_SET (&error); _dbus_verbose ("bus_activation_activate_service() failed: %s\n", error.name); goto out; } goto out; } else if (service == NULL) { dbus_set_error (&error, DBUS_ERROR_NAME_HAS_NO_OWNER, "Name \"%s\" does not exist", service_name); goto out; } else { addressed_recipient = bus_service_get_primary_owners_connection (service); _dbus_assert (addressed_recipient != NULL); } } /* Now send the message to its destination (or not, if * addressed_recipient == NULL), and match it against other connections' * match rules. */ if (!bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error)) goto out; out: if (dbus_error_is_set (&error)) { /* Even if we disconnected it, pretend to send it any pending error * messages so that monitors can observe them. */ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { bus_connection_send_oom_error (connection, message); /* cancel transaction due to OOM */ if (transaction != NULL) { bus_transaction_cancel_and_free (transaction); transaction = NULL; } } else { /* Try to send the real error, if no mem to do that, send * the OOM error */ _dbus_assert (transaction != NULL); if (!bus_transaction_send_error_reply (transaction, connection, &error, message)) { bus_connection_send_oom_error (connection, message); /* cancel transaction due to OOM */ if (transaction != NULL) { bus_transaction_cancel_and_free (transaction); transaction = NULL; } } } dbus_error_free (&error); } if (transaction != NULL) { bus_transaction_execute_and_free (transaction); } dbus_connection_unref (connection); return result; } static DBusHandlerResult bus_dispatch_message_filter (DBusConnection *connection, DBusMessage *message, void *user_data) { return bus_dispatch (connection, message); } dbus_bool_t bus_dispatch_add_connection (DBusConnection *connection) { if (!dbus_connection_add_filter (connection, bus_dispatch_message_filter, NULL, NULL)) return FALSE; return TRUE; } void bus_dispatch_remove_connection (DBusConnection *connection) { /* Here we tell the bus driver that we want to get off. */ bus_driver_remove_connection (connection); dbus_connection_remove_filter (connection, bus_dispatch_message_filter, NULL); } #ifdef DBUS_ENABLE_EMBEDDED_TESTS #include #include "stats.h" /* This is used to know whether we need to block in order to finish * sending a message, or whether the initial dbus_connection_send() * already flushed the queue. */ #define SEND_PENDING(connection) (dbus_connection_has_messages_to_send (connection)) typedef dbus_bool_t (* Check1Func) (BusContext *context); typedef dbus_bool_t (* Check2Func) (BusContext *context, DBusConnection *connection); static dbus_bool_t check_no_leftovers (BusContext *context); static void block_connection_until_message_from_bus (BusContext *context, DBusConnection *connection, const char *what_is_expected) { _dbus_verbose ("expecting: %s\n", what_is_expected); while (dbus_connection_get_dispatch_status (connection) == DBUS_DISPATCH_COMPLETE && dbus_connection_get_is_connected (connection)) { bus_test_run_bus_loop (context, TRUE); bus_test_run_clients_loop (FALSE); } } static void spin_connection_until_authenticated (BusContext *context, DBusConnection *connection) { _dbus_verbose ("Spinning to auth connection %p\n", connection); while (!dbus_connection_get_is_authenticated (connection) && dbus_connection_get_is_connected (connection)) { bus_test_run_bus_loop (context, FALSE); bus_test_run_clients_loop (FALSE); } _dbus_verbose (" ... done spinning to auth connection %p\n", connection); } /* compensate for fact that pop_message() can return #NULL due to OOM */ static DBusMessage* pop_message_waiting_for_memory (DBusConnection *connection) { while (dbus_connection_get_dispatch_status (connection) == DBUS_DISPATCH_NEED_MEMORY) _dbus_wait_for_memory (); return dbus_connection_pop_message (connection); } static DBusMessage* borrow_message_waiting_for_memory (DBusConnection *connection) { while (dbus_connection_get_dispatch_status (connection) == DBUS_DISPATCH_NEED_MEMORY) _dbus_wait_for_memory (); return dbus_connection_borrow_message (connection); } static void warn_unexpected_real (DBusConnection *connection, DBusMessage *message, const char *expected, const char *function, int line) { if (message) _dbus_warn ("%s:%d received message interface \"%s\" member \"%s\" error name \"%s\" on %p, expecting %s\n", function, line, dbus_message_get_interface (message) ? dbus_message_get_interface (message) : "(unset)", dbus_message_get_member (message) ? dbus_message_get_member (message) : "(unset)", dbus_message_get_error_name (message) ? dbus_message_get_error_name (message) : "(unset)", connection, expected); else _dbus_warn ("%s:%d received no message on %p, expecting %s\n", function, line, connection, expected); } #define warn_unexpected(connection, message, expected) \ warn_unexpected_real (connection, message, expected, _DBUS_FUNCTION_NAME, __LINE__) static void verbose_message_received (DBusConnection *connection, DBusMessage *message) { _dbus_verbose ("Received message interface \"%s\" member \"%s\" error name \"%s\" on %p\n", dbus_message_get_interface (message) ? dbus_message_get_interface (message) : "(unset)", dbus_message_get_member (message) ? dbus_message_get_member (message) : "(unset)", dbus_message_get_error_name (message) ? dbus_message_get_error_name (message) : "(unset)", connection); } typedef enum { SERVICE_CREATED, OWNER_CHANGED, SERVICE_DELETED } ServiceInfoKind; typedef struct { ServiceInfoKind expected_kind; const char *expected_service_name; dbus_bool_t failed; DBusConnection *skip_connection; } CheckServiceOwnerChangedData; static dbus_bool_t check_service_owner_changed_foreach (DBusConnection *connection, void *data) { CheckServiceOwnerChangedData *d = data; DBusMessage *message; DBusError error; const char *service_name, *old_owner, *new_owner; if (d->expected_kind == SERVICE_CREATED && connection == d->skip_connection) return TRUE; dbus_error_init (&error); d->failed = TRUE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a message on %p, expecting %s\n", connection, "NameOwnerChanged"); goto out; } else if (!dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { warn_unexpected (connection, message, "NameOwnerChanged"); goto out; } else { reget_service_info_data: service_name = NULL; old_owner = NULL; new_owner = NULL; dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &service_name, DBUS_TYPE_STRING, &old_owner, DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID); if (dbus_error_is_set (&error)) { if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { dbus_error_free (&error); _dbus_wait_for_memory (); goto reget_service_info_data; } else { _dbus_warn ("Did not get the expected arguments\n"); goto out; } } if ((d->expected_kind == SERVICE_CREATED && ( old_owner[0] || !new_owner[0])) || (d->expected_kind == OWNER_CHANGED && (!old_owner[0] || !new_owner[0])) || (d->expected_kind == SERVICE_DELETED && (!old_owner[0] || new_owner[0]))) { _dbus_warn ("inconsistent NameOwnerChanged arguments\n"); goto out; } if (strcmp (service_name, d->expected_service_name) != 0) { _dbus_warn ("expected info on service %s, got info on %s\n", d->expected_service_name, service_name); goto out; } if (*service_name == ':' && new_owner[0] && strcmp (service_name, new_owner) != 0) { _dbus_warn ("inconsistent ServiceOwnedChanged message (\"%s\" [ %s -> %s ])\n", service_name, old_owner, new_owner); goto out; } } d->failed = FALSE; out: dbus_error_free (&error); if (message) dbus_message_unref (message); return !d->failed; } static void kill_client_connection (BusContext *context, DBusConnection *connection) { char *base_service; const char *s; CheckServiceOwnerChangedData socd; _dbus_verbose ("killing connection %p\n", connection); s = dbus_bus_get_unique_name (connection); _dbus_assert (s != NULL); while ((base_service = _dbus_strdup (s)) == NULL) _dbus_wait_for_memory (); dbus_connection_ref (connection); /* kick in the disconnect handler that unrefs the connection */ dbus_connection_close (connection); bus_test_run_everything (context); _dbus_assert (bus_test_client_listed (connection)); /* Run disconnect handler in test.c */ if (bus_connection_dispatch_one_message (connection)) _dbus_assert_not_reached ("something received on connection being killed other than the disconnect"); _dbus_assert (!dbus_connection_get_is_connected (connection)); dbus_connection_unref (connection); connection = NULL; _dbus_assert (!bus_test_client_listed (connection)); socd.expected_kind = SERVICE_DELETED; socd.expected_service_name = base_service; socd.failed = FALSE; socd.skip_connection = NULL; bus_test_clients_foreach (check_service_owner_changed_foreach, &socd); dbus_free (base_service); if (socd.failed) _dbus_assert_not_reached ("didn't get the expected NameOwnerChanged (deletion) messages"); if (!check_no_leftovers (context)) _dbus_assert_not_reached ("stuff left in message queues after disconnecting a client"); } static void kill_client_connection_unchecked (DBusConnection *connection) { /* This kills the connection without expecting it to affect * the rest of the bus. */ _dbus_verbose ("Unchecked kill of connection %p\n", connection); dbus_connection_ref (connection); dbus_connection_close (connection); /* dispatching disconnect handler will unref once */ if (bus_connection_dispatch_one_message (connection)) _dbus_assert_not_reached ("message other than disconnect dispatched after failure to register"); _dbus_assert (!bus_test_client_listed (connection)); dbus_connection_unref (connection); } typedef struct { dbus_bool_t failed; } CheckNoMessagesData; static dbus_bool_t check_no_messages_foreach (DBusConnection *connection, void *data) { CheckNoMessagesData *d = data; DBusMessage *message; message = pop_message_waiting_for_memory (connection); if (message != NULL) { warn_unexpected (connection, message, "no messages"); d->failed = TRUE; } if (message) dbus_message_unref (message); return !d->failed; } static dbus_bool_t check_no_leftovers (BusContext *context) { CheckNoMessagesData nmd; nmd.failed = FALSE; bus_test_clients_foreach (check_no_messages_foreach, &nmd); if (nmd.failed) { _dbus_verbose ("leftover message found\n"); return FALSE; } else return TRUE; } /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_hello_message (BusContext *context, DBusConnection *connection) { DBusMessage *message; DBusMessage *name_message; dbus_uint32_t serial; dbus_bool_t retval; DBusError error; const char *name; const char *acquired; retval = FALSE; dbus_error_init (&error); name = NULL; acquired = NULL; message = NULL; name_message = NULL; _dbus_verbose ("check_hello_message for %p\n", connection); message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "Hello"); if (message == NULL) return TRUE; dbus_connection_ref (connection); /* because we may get disconnected */ if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); dbus_connection_unref (connection); return TRUE; } _dbus_assert (dbus_message_has_signature (message, "")); dbus_message_unref (message); message = NULL; if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected (presumably auth failed)\n"); dbus_connection_unref (connection); return TRUE; } /* send our message */ bus_test_run_clients_loop (SEND_PENDING (connection)); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected (presumably auth failed)\n"); dbus_connection_unref (connection); return TRUE; } block_connection_until_message_from_bus (context, connection, "reply to Hello"); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected (presumably auth failed)\n"); dbus_connection_unref (connection); return TRUE; } dbus_connection_unref (connection); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "Hello", serial, connection); goto out; } verbose_message_received (connection, message); if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { CheckServiceOwnerChangedData socd; if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) { ; /* good, expected */ } else { warn_unexpected (connection, message, "method return for Hello"); goto out; } retry_get_hello_name: if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) { if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { _dbus_verbose ("no memory to get service name arg from hello\n"); dbus_error_free (&error); _dbus_wait_for_memory (); goto retry_get_hello_name; } else { _dbus_assert (dbus_error_is_set (&error)); _dbus_warn ("Did not get the expected single string argument to hello\n"); goto out; } } _dbus_verbose ("Got hello name: %s\n", name); while (!dbus_bus_set_unique_name (connection, name)) _dbus_wait_for_memory (); socd.expected_kind = SERVICE_CREATED; socd.expected_service_name = name; socd.failed = FALSE; socd.skip_connection = connection; /* we haven't done AddMatch so won't get it ourselves */ bus_test_clients_foreach (check_service_owner_changed_foreach, &socd); if (socd.failed) goto out; name_message = message; /* Client should also have gotten ServiceAcquired */ message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Expecting %s, got nothing\n", "NameAcquired"); goto out; } if (! dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameAcquired")) { _dbus_warn ("Expecting %s, got smthg else\n", "NameAcquired"); goto out; } retry_get_acquired_name: if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &acquired, DBUS_TYPE_INVALID)) { if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { _dbus_verbose ("no memory to get service name arg from acquired\n"); dbus_error_free (&error); _dbus_wait_for_memory (); goto retry_get_acquired_name; } else { _dbus_assert (dbus_error_is_set (&error)); _dbus_warn ("Did not get the expected single string argument to ServiceAcquired\n"); goto out; } } _dbus_verbose ("Got acquired name: %s\n", acquired); if (strcmp (acquired, name) != 0) { _dbus_warn ("Acquired name is %s but expected %s\n", acquired, name); goto out; } acquired = NULL; } if (!check_no_leftovers (context)) goto out; retval = TRUE; out: _dbus_verbose ("ending - retval = %d\n", retval); dbus_error_free (&error); if (message) dbus_message_unref (message); if (name_message) dbus_message_unref (name_message); return retval; } /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_double_hello_message (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; DBusError error; retval = FALSE; dbus_error_init (&error); message = NULL; _dbus_verbose ("check_double_hello_message for %p\n", connection); message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "Hello"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; /* send our message */ bus_test_run_clients_loop (SEND_PENDING (connection)); dbus_connection_ref (connection); /* because we may get disconnected */ block_connection_until_message_from_bus (context, connection, "reply to Hello"); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); dbus_connection_unref (connection); return TRUE; } dbus_connection_unref (connection); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "Hello", serial, connection); goto out; } verbose_message_received (connection, message); if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR) { warn_unexpected (connection, message, "method return for Hello"); goto out; } if (!check_no_leftovers (context)) goto out; retval = TRUE; out: dbus_error_free (&error); if (message) dbus_message_unref (message); return retval; } /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_get_connection_unix_user (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; DBusError error; const char *base_service_name; dbus_uint32_t uid; retval = FALSE; dbus_error_init (&error); message = NULL; _dbus_verbose ("check_get_connection_unix_user for %p\n", connection); message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetConnectionUnixUser"); if (message == NULL) return TRUE; base_service_name = dbus_bus_get_unique_name (connection); if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &base_service_name, DBUS_TYPE_INVALID)) { dbus_message_unref (message); return TRUE; } if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } /* send our message */ bus_test_run_clients_loop (SEND_PENDING (connection)); dbus_message_unref (message); message = NULL; dbus_connection_ref (connection); /* because we may get disconnected */ block_connection_until_message_from_bus (context, connection, "reply to GetConnectionUnixUser"); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); dbus_connection_unref (connection); return TRUE; } dbus_connection_unref (connection); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "GetConnectionUnixUser", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) { ; /* good, expected */ } else { warn_unexpected (connection, message, "method_return for GetConnectionUnixUser"); goto out; } retry_get_property: if (!dbus_message_get_args (message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID)) { if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { _dbus_verbose ("no memory to get uid by GetConnectionUnixUser\n"); dbus_error_free (&error); _dbus_wait_for_memory (); goto retry_get_property; } else { _dbus_assert (dbus_error_is_set (&error)); _dbus_warn ("Did not get the expected DBUS_TYPE_UINT32 from GetConnectionUnixUser\n"); goto out; } } } if (!check_no_leftovers (context)) goto out; retval = TRUE; out: dbus_error_free (&error); if (message) dbus_message_unref (message); return retval; } /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_get_connection_unix_process_id (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; DBusError error; const char *base_service_name; dbus_uint32_t pid; retval = FALSE; dbus_error_init (&error); message = NULL; _dbus_verbose ("check_get_connection_unix_process_id for %p\n", connection); message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetConnectionUnixProcessID"); if (message == NULL) return TRUE; base_service_name = dbus_bus_get_unique_name (connection); if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &base_service_name, DBUS_TYPE_INVALID)) { dbus_message_unref (message); return TRUE; } if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } /* send our message */ bus_test_run_clients_loop (SEND_PENDING (connection)); dbus_message_unref (message); message = NULL; dbus_connection_ref (connection); /* because we may get disconnected */ block_connection_until_message_from_bus (context, connection, "reply to GetConnectionUnixProcessID"); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); dbus_connection_unref (connection); return TRUE; } dbus_connection_unref (connection); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "GetConnectionUnixProcessID", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } #ifdef DBUS_WIN else if (dbus_message_is_error (message, DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN)) { /* We are expecting this error, since we know in the test suite we aren't * talking to a client running on UNIX */ _dbus_verbose ("Windows correctly does not support GetConnectionUnixProcessID\n"); } #endif else { #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ defined(__linux__) || \ defined(__OpenBSD__) /* In principle NetBSD should also be in that list, but * its implementation of PID-passing doesn't work * over a socketpair() as used in the debug-pipe transport. * We test this functionality in a more realistic situation * in test/dbus-daemon.c. */ warn_unexpected (connection, message, "not this error"); goto out; #else _dbus_verbose ("does not support GetConnectionUnixProcessID but perhaps that's OK?\n"); #endif } } 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 (BusContext *context, DBusConnection *connection, const char *rule) { DBusMessage *message; dbus_bool_t retval; dbus_uint32_t serial; DBusError error; retval = FALSE; dbus_error_init (&error); message = NULL; _dbus_verbose ("check_add_match for connection %p, rule %s\n", connection, rule); message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "AddMatch"); if (message == NULL) return TRUE; if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &rule, DBUS_TYPE_INVALID)) { dbus_message_unref (message); return TRUE; } if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; dbus_connection_ref (connection); /* because we may get disconnected */ /* send our message */ bus_test_run_clients_loop (SEND_PENDING (connection)); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); dbus_connection_unref (connection); return TRUE; } block_connection_until_message_from_bus (context, connection, "reply to AddMatch"); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); dbus_connection_unref (connection); return TRUE; } dbus_connection_unref (connection); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "AddMatch", serial, connection); goto out; } verbose_message_received (connection, message); if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) { ; /* good, expected */ _dbus_assert (dbus_message_get_reply_serial (message) == serial); } else { warn_unexpected (connection, message, "method return for AddMatch"); goto out; } } if (!check_no_leftovers (context)) goto out; retval = TRUE; out: dbus_error_free (&error); if (message) dbus_message_unref (message); return retval; } #ifdef DBUS_ENABLE_STATS /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_get_all_match_rules (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_bool_t retval; dbus_uint32_t serial; DBusError error; retval = FALSE; dbus_error_init (&error); message = NULL; _dbus_verbose ("check_get_all_match_rules for connection %p\n", connection); message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, BUS_INTERFACE_STATS, "GetAllMatchRules"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; dbus_connection_ref (connection); /* because we may get disconnected */ /* send our message */ bus_test_run_clients_loop (SEND_PENDING (connection)); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); dbus_connection_unref (connection); return TRUE; } block_connection_until_message_from_bus (context, connection, "reply to AddMatch"); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); dbus_connection_unref (connection); return TRUE; } dbus_connection_unref (connection); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "AddMatch", serial, connection); goto out; } verbose_message_received (connection, message); if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) { ; /* good, expected */ _dbus_assert (dbus_message_get_reply_serial (message) == serial); } else { warn_unexpected (connection, message, "method return for AddMatch"); goto out; } } if (!check_no_leftovers (context)) goto out; retval = TRUE; out: dbus_error_free (&error); if (message) dbus_message_unref (message); return retval; } #endif /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_hello_connection (BusContext *context) { DBusConnection *connection; DBusError error; dbus_error_init (&error); connection = dbus_connection_open_private (TEST_DEBUG_PIPE, &error); if (connection == NULL) { _DBUS_ASSERT_ERROR_IS_SET (&error); dbus_error_free (&error); return TRUE; } if (!bus_setup_debug_client (connection)) { dbus_connection_close (connection); dbus_connection_unref (connection); return TRUE; } spin_connection_until_authenticated (context, connection); if (!check_hello_message (context, connection)) return FALSE; if (dbus_bus_get_unique_name (connection) == NULL) { /* We didn't successfully register, so we can't * do the usual kill_client_connection() checks */ kill_client_connection_unchecked (connection); } else { if (!check_add_match (context, connection, "")) return FALSE; kill_client_connection (context, connection); } return TRUE; } #define NONEXISTENT_SERVICE_NAME "test.this.service.does.not.exist.ewuoiurjdfxcvn" /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_nonexistent_service_no_auto_start (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; const char *nonexistent = NONEXISTENT_SERVICE_NAME; dbus_uint32_t flags; message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "StartServiceByName"); if (message == NULL) return TRUE; dbus_message_set_auto_start (message, FALSE); flags = 0; if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &nonexistent, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID)) { dbus_message_unref (message); return TRUE; } if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); block_connection_until_message_from_bus (context, connection, "reply to ActivateService on nonexistent"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "StartServiceByName", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_SERVICE_UNKNOWN)) { ; /* good, this is expected also */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { _dbus_warn ("Did not expect to successfully activate %s\n", NONEXISTENT_SERVICE_NAME); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_nonexistent_service_auto_start (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; message = dbus_message_new_method_call (NONEXISTENT_SERVICE_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "Echo"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); block_connection_until_message_from_bus (context, connection, "reply to Echo"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "Echo message (auto activation)", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_SERVICE_UNKNOWN)) { ; /* good, this is expected also */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { _dbus_warn ("Did not expect to successfully activate %s\n", NONEXISTENT_SERVICE_NAME); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } static dbus_bool_t check_base_service_activated (BusContext *context, DBusConnection *connection, DBusMessage *initial_message, const char **base_service_p) { DBusMessage *message; dbus_bool_t retval; DBusError error; const char *base_service, *base_service_from_bus, *old_owner; retval = FALSE; dbus_error_init (&error); base_service = NULL; old_owner = NULL; base_service_from_bus = NULL; message = initial_message; dbus_message_ref (message); if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { CheckServiceOwnerChangedData socd; reget_service_name_arg: base_service = NULL; old_owner = NULL; base_service_from_bus = NULL; if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &base_service, DBUS_TYPE_STRING, &old_owner, DBUS_TYPE_STRING, &base_service_from_bus, DBUS_TYPE_INVALID)) { if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { dbus_error_free (&error); _dbus_wait_for_memory (); goto reget_service_name_arg; } else { _dbus_warn ("Message %s doesn't have a service name: %s\n", "NameOwnerChanged (creation)", error.message); goto out; } } if (*base_service != ':') { _dbus_warn ("Expected base service activation, got \"%s\" instead\n", base_service); goto out; } if (strcmp (base_service, base_service_from_bus) != 0) { _dbus_warn ("Expected base service activation, got \"%s\" instead with owner \"%s\"\n", base_service, base_service_from_bus); goto out; } if (old_owner[0]) { _dbus_warn ("Received an old_owner argument during base service activation, \"%s\"\n", old_owner); goto out; } socd.expected_kind = SERVICE_CREATED; socd.expected_service_name = base_service; socd.failed = FALSE; socd.skip_connection = connection; bus_test_clients_foreach (check_service_owner_changed_foreach, &socd); if (socd.failed) goto out; } else { warn_unexpected (connection, message, "NameOwnerChanged (creation) for base service"); goto out; } if (base_service_p) *base_service_p = base_service; retval = TRUE; out: if (message) dbus_message_unref (message); dbus_error_free (&error); return retval; } static dbus_bool_t check_service_activated (BusContext *context, DBusConnection *connection, const char *activated_name, const char *base_service_name, DBusMessage *initial_message) { DBusMessage *message; dbus_bool_t retval; DBusError error; dbus_uint32_t activation_result; retval = FALSE; dbus_error_init (&error); message = initial_message; dbus_message_ref (message); if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { CheckServiceOwnerChangedData socd; const char *service_name, *base_service_from_bus, *old_owner; reget_service_name_arg: service_name = NULL; old_owner = NULL; base_service_from_bus = NULL; if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &service_name, DBUS_TYPE_STRING, &old_owner, DBUS_TYPE_STRING, &base_service_from_bus, DBUS_TYPE_INVALID)) { if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { dbus_error_free (&error); _dbus_wait_for_memory (); goto reget_service_name_arg; } else { _dbus_warn ("Message %s doesn't have a service name: %s\n", "NameOwnerChanged (creation)", error.message); goto out; } } if (strcmp (service_name, activated_name) != 0) { _dbus_warn ("Expected to see service %s created, saw %s instead\n", activated_name, service_name); goto out; } if (strcmp (base_service_name, base_service_from_bus) != 0) { _dbus_warn ("NameOwnerChanged reports wrong base service: %s owner, expected %s instead\n", base_service_from_bus, base_service_name); goto out; } if (old_owner[0]) { _dbus_warn ("expected a %s, got a %s\n", "NameOwnerChanged (creation)", "NameOwnerChanged (change)"); goto out; } socd.expected_kind = SERVICE_CREATED; socd.skip_connection = connection; socd.failed = FALSE; socd.expected_service_name = service_name; bus_test_clients_foreach (check_service_owner_changed_foreach, &socd); if (socd.failed) goto out; dbus_message_unref (message); service_name = NULL; old_owner = NULL; base_service_from_bus = NULL; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Expected a reply to %s, got nothing\n", "StartServiceByName"); goto out; } } else { warn_unexpected (connection, message, "NameOwnerChanged for the activated name"); goto out; } if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_RETURN) { warn_unexpected (connection, message, "reply to StartServiceByName"); goto out; } activation_result = 0; if (!dbus_message_get_args (message, &error, DBUS_TYPE_UINT32, &activation_result, DBUS_TYPE_INVALID)) { if (!dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { _dbus_warn ("Did not have activation result first argument to %s: %s\n", "StartServiceByName", error.message); goto out; } dbus_error_free (&error); } else { if (activation_result == DBUS_START_REPLY_SUCCESS) ; /* Good */ else if (activation_result == DBUS_START_REPLY_ALREADY_RUNNING) ; /* Good also */ else { _dbus_warn ("Activation result was %u, no good.\n", activation_result); goto out; } } dbus_message_unref (message); message = NULL; if (!check_no_leftovers (context)) { _dbus_warn ("Messages were left over after verifying existent activation results\n"); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); dbus_error_free (&error); return retval; } static dbus_bool_t check_service_auto_activated (BusContext *context, DBusConnection *connection, const char *activated_name, const char *base_service_name, DBusMessage *initial_message) { DBusMessage *message; dbus_bool_t retval; DBusError error; retval = FALSE; dbus_error_init (&error); message = initial_message; dbus_message_ref (message); if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { const char *service_name; CheckServiceOwnerChangedData socd; reget_service_name_arg: if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &service_name, DBUS_TYPE_INVALID)) { if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { dbus_error_free (&error); _dbus_wait_for_memory (); goto reget_service_name_arg; } else { _dbus_warn ("Message %s doesn't have a service name: %s\n", "NameOwnerChanged", error.message); dbus_error_free (&error); goto out; } } if (strcmp (service_name, activated_name) != 0) { _dbus_warn ("Expected to see service %s created, saw %s instead\n", activated_name, service_name); goto out; } socd.expected_kind = SERVICE_CREATED; socd.expected_service_name = service_name; socd.failed = FALSE; socd.skip_connection = connection; bus_test_clients_foreach (check_service_owner_changed_foreach, &socd); if (socd.failed) goto out; /* Note that this differs from regular activation in that we don't get a * reply to ActivateService here. */ dbus_message_unref (message); message = NULL; service_name = NULL; } else { warn_unexpected (connection, message, "NameOwnerChanged for the activated name"); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } static dbus_bool_t check_service_deactivated (BusContext *context, DBusConnection *connection, const char *activated_name, const char *base_service) { dbus_bool_t retval; CheckServiceOwnerChangedData socd; retval = FALSE; /* Now we are expecting ServiceOwnerChanged (deletion) messages for the base * service and the activated_name. The base service * notification is required to come last. */ socd.expected_kind = SERVICE_DELETED; socd.expected_service_name = activated_name; socd.failed = FALSE; socd.skip_connection = NULL; bus_test_clients_foreach (check_service_owner_changed_foreach, &socd); if (socd.failed) goto out; socd.expected_kind = SERVICE_DELETED; socd.expected_service_name = base_service; socd.failed = FALSE; socd.skip_connection = NULL; bus_test_clients_foreach (check_service_owner_changed_foreach, &socd); if (socd.failed) goto out; retval = TRUE; out: return retval; } static dbus_bool_t check_send_exit_to_service (BusContext *context, DBusConnection *connection, const char *service_name, const char *base_service) { dbus_bool_t got_error; DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; _dbus_verbose ("Sending exit message to the test service\n"); retval = FALSE; /* Kill off the test service by sending it a quit message */ message = dbus_message_new_method_call (service_name, "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "Exit"); if (message == NULL) { /* Do this again; we still need the service to exit... */ if (!check_send_exit_to_service (context, connection, service_name, base_service)) goto out; return TRUE; } if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); /* Do this again; we still need the service to exit... */ if (!check_send_exit_to_service (context, connection, service_name, base_service)) goto out; return TRUE; } dbus_message_unref (message); message = NULL; /* send message */ bus_test_run_clients_loop (SEND_PENDING (connection)); /* read it in and write it out to test service */ bus_test_run_bus_loop (context, FALSE); /* see if we got an error during message bus dispatching */ bus_test_run_clients_loop (FALSE); message = borrow_message_waiting_for_memory (connection); got_error = message != NULL && dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR; if (message) { dbus_connection_return_message (connection, message); message = NULL; } if (!got_error) { /* If no error, wait for the test service to exit */ block_connection_until_message_from_bus (context, connection, "test service to exit"); bus_test_run_everything (context); } if (got_error) { message = pop_message_waiting_for_memory (connection); _dbus_assert (message != NULL); if (dbus_message_get_reply_serial (message) != serial) { warn_unexpected (connection, message, "error with the correct reply serial"); goto out; } if (!dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { warn_unexpected (connection, message, "a no memory error from asking test service to exit"); goto out; } _dbus_verbose ("Got error %s when asking test service to exit\n", dbus_message_get_error_name (message)); /* Do this again; we still need the service to exit... */ if (!check_send_exit_to_service (context, connection, service_name, base_service)) goto out; } else { if (!check_service_deactivated (context, connection, service_name, base_service)) goto out; /* Should now have a NoReply error from the Exit() method * call; it should have come after all the deactivation * stuff. */ message = pop_message_waiting_for_memory (connection); if (message == NULL) { warn_unexpected (connection, NULL, "reply to Exit() method call"); goto out; } if (!dbus_message_is_error (message, DBUS_ERROR_NO_REPLY)) { warn_unexpected (connection, message, "NoReply error from Exit() method call"); goto out; } if (dbus_message_get_reply_serial (message) != serial) { warn_unexpected (connection, message, "error with the correct reply serial"); goto out; } _dbus_verbose ("Got error %s after test service exited\n", dbus_message_get_error_name (message)); if (!check_no_leftovers (context)) { _dbus_warn ("Messages were left over after %s\n", _DBUS_FUNCTION_NAME); goto out; } } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } static dbus_bool_t check_got_error (BusContext *context, DBusConnection *connection, const char *first_error_name, ...) { DBusMessage *message; dbus_bool_t retval; va_list ap; dbus_bool_t error_found; const char *error_name; retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not get an expected error\n"); goto out; } if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR) { warn_unexpected (connection, message, "an error"); goto out; } error_found = FALSE; va_start (ap, first_error_name); error_name = first_error_name; while (error_name != NULL) { if (dbus_message_is_error (message, error_name)) { error_found = TRUE; break; } error_name = va_arg (ap, char*); } va_end (ap); if (!error_found) { _dbus_warn ("Expected error %s or other, got %s instead\n", first_error_name, dbus_message_get_error_name (message)); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } typedef enum { GOT_SERVICE_CREATED, GOT_SERVICE_DELETED, GOT_ERROR, GOT_SOMETHING_ELSE } GotServiceInfo; static GotServiceInfo check_got_service_info (DBusMessage *message) { GotServiceInfo message_kind; if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { DBusError error; const char *service_name, *old_owner, *new_owner; dbus_error_init (&error); reget_service_info_data: service_name = NULL; old_owner = NULL; new_owner = NULL; dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &service_name, DBUS_TYPE_STRING, &old_owner, DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID); if (dbus_error_is_set (&error)) { if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { dbus_error_free (&error); goto reget_service_info_data; } else { _dbus_warn ("unexpected arguments for NameOwnerChanged message\n"); message_kind = GOT_SOMETHING_ELSE; } } else if (!old_owner[0]) message_kind = GOT_SERVICE_CREATED; else if (!new_owner[0]) message_kind = GOT_SERVICE_DELETED; else message_kind = GOT_SOMETHING_ELSE; dbus_error_free (&error); } else if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) message_kind = GOT_ERROR; else message_kind = GOT_SOMETHING_ELSE; return message_kind; } #define EXISTENT_SERVICE_NAME "org.freedesktop.DBus.TestSuiteEchoService" /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_existent_service_no_auto_start (BusContext *context, DBusConnection *connection) { DBusMessage *message; DBusMessage *base_service_message; const char *base_service; dbus_uint32_t serial; dbus_bool_t retval; const char *existent = EXISTENT_SERVICE_NAME; dbus_uint32_t flags; base_service_message = NULL; message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "StartServiceByName"); if (message == NULL) return TRUE; dbus_message_set_auto_start (message, FALSE); flags = 0; if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &existent, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID)) { dbus_message_unref (message); return TRUE; } if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); /* now wait for the message bus to hear back from the activated * service. */ block_connection_until_message_from_bus (context, connection, "activated service to connect"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive any messages after %s %d on %p\n", "StartServiceByName", serial, connection); goto out; } verbose_message_received (connection, message); _dbus_verbose (" (after sending %s)\n", "StartServiceByName"); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_SPAWN_CHILD_EXITED) || dbus_message_is_error (message, DBUS_ERROR_SPAWN_CHILD_SIGNALED) || dbus_message_is_error (message, DBUS_ERROR_SPAWN_EXEC_FAILED)) { ; /* good, this is expected also */ } else { _dbus_warn ("Did not expect error %s\n", dbus_message_get_error_name (message)); goto out; } } else { GotServiceInfo message_kind; if (!check_base_service_activated (context, connection, message, &base_service)) goto out; base_service_message = message; message = NULL; /* We may need to block here for the test service to exit or finish up */ block_connection_until_message_from_bus (context, connection, "test service to exit or finish up"); message = dbus_connection_borrow_message (connection); if (message == NULL) { _dbus_warn ("Did not receive any messages after base service creation notification\n"); goto out; } message_kind = check_got_service_info (message); dbus_connection_return_message (connection, message); message = NULL; switch (message_kind) { case GOT_SOMETHING_ELSE: _dbus_warn ("Unexpected message after ActivateService " "(should be an error or a service announcement"); goto out; case GOT_ERROR: if (!check_got_error (context, connection, DBUS_ERROR_SPAWN_CHILD_EXITED, DBUS_ERROR_NO_MEMORY, NULL)) goto out; /* A service deleted should be coming along now after this error. * We can also get the error *after* the service deleted. */ /* fall through */ case GOT_SERVICE_DELETED: { /* The service started up and got a base address, but then * failed to register under EXISTENT_SERVICE_NAME */ CheckServiceOwnerChangedData socd; socd.expected_kind = SERVICE_DELETED; socd.expected_service_name = base_service; socd.failed = FALSE; socd.skip_connection = NULL; bus_test_clients_foreach (check_service_owner_changed_foreach, &socd); if (socd.failed) goto out; /* Now we should get an error about the service exiting * if we didn't get it before. */ if (message_kind != GOT_ERROR) { block_connection_until_message_from_bus (context, connection, "error about service exiting"); /* and process everything again */ bus_test_run_everything (context); if (!check_got_error (context, connection, DBUS_ERROR_SPAWN_CHILD_EXITED, DBUS_ERROR_NO_MEMORY, NULL)) goto out; } break; } case GOT_SERVICE_CREATED: message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Failed to pop message we just put back! " "should have been a NameOwnerChanged (creation)\n"); goto out; } if (!check_service_activated (context, connection, EXISTENT_SERVICE_NAME, base_service, message)) goto out; dbus_message_unref (message); message = NULL; if (!check_no_leftovers (context)) { _dbus_warn ("Messages were left over after successful activation\n"); goto out; } if (!check_send_exit_to_service (context, connection, EXISTENT_SERVICE_NAME, base_service)) goto out; break; } } retval = TRUE; out: if (message) dbus_message_unref (message); if (base_service_message) dbus_message_unref (base_service_message); return retval; } #ifndef DBUS_WIN_FIXME /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_segfault_service_no_auto_start (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; const char *segv_service; dbus_uint32_t flags; message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "StartServiceByName"); if (message == NULL) return TRUE; dbus_message_set_auto_start (message, FALSE); segv_service = "org.freedesktop.DBus.TestSuiteSegfaultService"; flags = 0; if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &segv_service, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID)) { dbus_message_unref (message); return TRUE; } if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); block_connection_until_message_from_bus (context, connection, "reply to activating segfault service"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "StartServiceByName", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_FAILED)) { const char *servicehelper; servicehelper = bus_context_get_servicehelper (context); /* make sure this only happens with the launch helper */ _dbus_assert (servicehelper != NULL); } else if (dbus_message_is_error (message, DBUS_ERROR_SPAWN_CHILD_SIGNALED)) { ; /* good, this is expected also */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { _dbus_warn ("Did not expect to successfully activate segfault service\n"); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_segfault_service_auto_start (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; message = dbus_message_new_method_call ("org.freedesktop.DBus.TestSuiteSegfaultService", "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "Echo"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); block_connection_until_message_from_bus (context, connection, "reply to Echo on segfault service"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "Echo message (auto activation)", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_SPAWN_CHILD_SIGNALED)) { ; /* good, this is expected also */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { _dbus_warn ("Did not expect to successfully activate segfault service\n"); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } #endif #define TEST_ECHO_MESSAGE "Test echo message" #define TEST_RUN_HELLO_FROM_SELF_MESSAGE "Test sending message to self" /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_existent_hello_from_self (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; const char *text; message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "RunHelloFromSelf"); if (message == NULL) return TRUE; text = TEST_RUN_HELLO_FROM_SELF_MESSAGE; if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID)) { dbus_message_unref (message); return TRUE; } if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); /* Note: if this test is run in OOM mode, it will block when the bus * doesn't send a reply due to OOM. */ block_connection_until_message_from_bus (context, connection, "reply from running hello from self"); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Failed to pop message! Should have been reply from RunHelloFromSelf message\n"); return FALSE; } if (dbus_message_get_reply_serial (message) != serial) { _dbus_warn ("Wrong reply serial\n"); dbus_message_unref (message); return FALSE; } dbus_message_unref (message); message = NULL; return TRUE; } /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_existent_ping (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.DBus.Peer", "Ping"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); /* Note: if this test is run in OOM mode, it will block when the bus * doesn't send a reply due to OOM. */ block_connection_until_message_from_bus (context, connection, "reply from running Ping"); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Failed to pop message! Should have been reply from Ping message\n"); return FALSE; } if (dbus_message_get_reply_serial (message) != serial) { _dbus_warn ("Wrong reply serial\n"); dbus_message_unref (message); return FALSE; } if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_RETURN) { _dbus_warn ("Unexpected message return during Ping\n"); dbus_message_unref (message); return FALSE; } dbus_message_unref (message); message = NULL; return TRUE; } /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_existent_get_machine_id (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; const char *machine_id; message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.DBus.Peer", "GetMachineId"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); /* Note: if this test is run in OOM mode, it will block when the bus * doesn't send a reply due to OOM. */ block_connection_until_message_from_bus (context, connection, "reply from running GetMachineId"); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Failed to pop message! Should have been reply from GetMachineId message\n"); return FALSE; } if (dbus_message_get_reply_serial (message) != serial) { _dbus_warn ("Wrong reply serial\n"); dbus_message_unref (message); return FALSE; } if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_RETURN) { _dbus_warn ("Unexpected message return during GetMachineId\n"); dbus_message_unref (message); return FALSE; } machine_id = NULL; if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &machine_id, DBUS_TYPE_INVALID)) { _dbus_warn ("Did not get a machine ID in reply to GetMachineId\n"); dbus_message_unref (message); return FALSE; } if (machine_id == NULL || strlen (machine_id) != 32) { _dbus_warn ("Machine id looks bogus: '%s'\n", machine_id ? machine_id : "null"); dbus_message_unref (message); return FALSE; } /* We can't check that the machine id is correct because during make check it is * just made up for each process separately */ dbus_message_unref (message); message = NULL; return TRUE; } /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_existent_service_auto_start (BusContext *context, DBusConnection *connection) { DBusMessage *message; DBusMessage *base_service_message; dbus_uint32_t serial; dbus_bool_t retval; const char *base_service; const char *text; base_service_message = NULL; message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "Echo"); if (message == NULL) return TRUE; text = TEST_ECHO_MESSAGE; if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID)) { dbus_message_unref (message); return TRUE; } if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); /* now wait for the message bus to hear back from the activated * service. */ block_connection_until_message_from_bus (context, connection, "reply to Echo on existent service"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive any messages after auto start %d on %p\n", serial, connection); goto out; } verbose_message_received (connection, message); _dbus_verbose (" (after sending %s)\n", "auto start"); /* we should get zero or two ServiceOwnerChanged signals */ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL) { GotServiceInfo message_kind; if (!check_base_service_activated (context, connection, message, &base_service)) goto out; base_service_message = message; message = NULL; /* We may need to block here for the test service to exit or finish up */ block_connection_until_message_from_bus (context, connection, "service to exit"); /* Should get a service creation notification for the activated * service name, or a service deletion on the base service name */ message = dbus_connection_borrow_message (connection); if (message == NULL) { _dbus_warn ("No message after auto activation " "(should be a service announcement)\n"); dbus_connection_return_message (connection, message); message = NULL; goto out; } message_kind = check_got_service_info (message); dbus_connection_return_message (connection, message); message = NULL; switch (message_kind) { case GOT_SERVICE_CREATED: message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Failed to pop message we just put back! " "should have been a NameOwnerChanged (creation)\n"); goto out; } /* Check that ServiceOwnerChanged (creation) was correctly received */ if (!check_service_auto_activated (context, connection, EXISTENT_SERVICE_NAME, base_service, message)) goto out; dbus_message_unref (message); message = NULL; break; case GOT_SERVICE_DELETED: { /* The service started up and got a base address, but then * failed to register under EXISTENT_SERVICE_NAME */ CheckServiceOwnerChangedData socd; socd.expected_kind = SERVICE_DELETED; socd.expected_service_name = base_service; socd.failed = FALSE; socd.skip_connection = NULL; bus_test_clients_foreach (check_service_owner_changed_foreach, &socd); if (socd.failed) goto out; break; } case GOT_ERROR: case GOT_SOMETHING_ELSE: _dbus_warn ("Unexpected message after auto activation\n"); goto out; } } /* OK, now we've dealt with ServiceOwnerChanged signals, now should * come the method reply (or error) from the initial method call */ /* Note: if this test is run in OOM mode, it will block when the bus * doesn't send a reply due to OOM. */ block_connection_until_message_from_bus (context, connection, "reply from echo message after auto-activation"); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Failed to pop message! Should have been reply from echo message\n"); goto out; } if (dbus_message_get_reply_serial (message) != serial) { _dbus_warn ("Wrong reply serial\n"); goto out; } dbus_message_unref (message); message = NULL; if (!check_existent_ping (context, connection)) goto out; if (!check_existent_get_machine_id (context, connection)) goto out; if (!check_existent_hello_from_self (context, connection)) goto out; if (!check_send_exit_to_service (context, connection, EXISTENT_SERVICE_NAME, base_service)) goto out; retval = TRUE; out: if (message) dbus_message_unref (message); if (base_service_message) dbus_message_unref (base_service_message); return retval; } #define SERVICE_FILE_MISSING_NAME "org.freedesktop.DBus.TestSuiteEchoServiceDotServiceFileDoesNotExist" /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_launch_service_file_missing (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; message = dbus_message_new_method_call (SERVICE_FILE_MISSING_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "Echo"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); block_connection_until_message_from_bus (context, connection, "reply to service file missing should fail to auto-start"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "Echo message (auto activation)", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_SERVICE_UNKNOWN)) { _dbus_verbose("got service unknown\n"); ; /* good, this is expected (only valid when using launch helper) */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { _dbus_warn ("Did not expect to successfully auto-start missing service\n"); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } #define SERVICE_USER_MISSING_NAME "org.freedesktop.DBus.TestSuiteNoUser" /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_launch_service_user_missing (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; message = dbus_message_new_method_call (SERVICE_USER_MISSING_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "Echo"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); block_connection_until_message_from_bus (context, connection, "reply to service which should fail to auto-start (missing User)"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_warn ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "Echo message (auto activation)", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_SPAWN_FILE_INVALID)) { _dbus_verbose("got service file invalid\n"); ; /* good, this is expected (only valid when using launch helper) */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { _dbus_warn ("Did not expect to successfully auto-start missing service\n"); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } #define SERVICE_EXEC_MISSING_NAME "org.freedesktop.DBus.TestSuiteNoExec" /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_launch_service_exec_missing (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; message = dbus_message_new_method_call (SERVICE_EXEC_MISSING_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "Echo"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); block_connection_until_message_from_bus (context, connection, "reply to service which should fail to auto-start (missing Exec)"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_warn ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "Echo message (auto activation)", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_SERVICE_UNKNOWN)) { _dbus_verbose("could not activate as invalid service file was not added\n"); ; /* good, this is expected as we shouldn't have been added to * the activation list with a missing Exec key */ } else if (dbus_message_is_error (message, DBUS_ERROR_SPAWN_FILE_INVALID)) { _dbus_verbose("got service file invalid\n"); ; /* good, this is allowed, and is the message passed back from the * launch helper */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { _dbus_warn ("Did not expect to successfully auto-start missing service\n"); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } #define SERVICE_SERVICE_MISSING_NAME "org.freedesktop.DBus.TestSuiteNoService" /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_launch_service_service_missing (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; message = dbus_message_new_method_call (SERVICE_SERVICE_MISSING_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "Echo"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); block_connection_until_message_from_bus (context, connection, "reply to service which should fail to auto-start (missing Service)"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_warn ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "Echo message (auto activation)", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_SERVICE_UNKNOWN)) { _dbus_verbose("could not activate as invalid service file was not added\n"); ; /* good, this is expected as we shouldn't have been added to * the activation list with a missing Exec key */ } else if (dbus_message_is_error (message, DBUS_ERROR_SPAWN_FILE_INVALID)) { _dbus_verbose("got service file invalid\n"); ; /* good, this is allowed, and is the message passed back from the * launch helper */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { _dbus_warn ("Did not expect to successfully auto-start missing service\n"); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } #define SHELL_FAIL_SERVICE_NAME "org.freedesktop.DBus.TestSuiteShellEchoServiceFail" /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_shell_fail_service_auto_start (BusContext *context, DBusConnection *connection) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; message = dbus_message_new_method_call (SHELL_FAIL_SERVICE_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "Echo"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); block_connection_until_message_from_bus (context, connection, "reply to shell Echo on service which should fail to auto-start"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", "Echo message (auto activation)", serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_INVALID_ARGS)) { _dbus_verbose("got invalid args\n"); ; /* good, this is expected also */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { _dbus_warn ("Did not expect to successfully auto-start shell fail service\n"); goto out; } retval = TRUE; out: if (message) dbus_message_unref (message); return retval; } #define SHELL_SUCCESS_SERVICE_NAME "org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess" /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_shell_service_success_auto_start (BusContext *context, DBusConnection *connection) { DBusMessage *message; DBusMessage *base_service_message; dbus_uint32_t serial; dbus_bool_t retval; const char *base_service; const char *argv[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL}; base_service_message = NULL; message = dbus_message_new_method_call (SHELL_SUCCESS_SERVICE_NAME, "/org/freedesktop/TestSuite", "org.freedesktop.TestSuite", "Echo"); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); /* now wait for the message bus to hear back from the activated * service. */ block_connection_until_message_from_bus (context, connection, "reply to Echo on shell success service"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive any messages after auto start %d on %p\n", serial, connection); goto out; } verbose_message_received (connection, message); _dbus_verbose (" (after sending %s)\n", "auto start"); /* we should get zero or two ServiceOwnerChanged signals */ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL) { GotServiceInfo message_kind; if (!check_base_service_activated (context, connection, message, &base_service)) goto out; base_service_message = message; message = NULL; /* We may need to block here for the test service to exit or finish up */ block_connection_until_message_from_bus (context, connection, "service to exit"); /* Should get a service creation notification for the activated * service name, or a service deletion on the base service name */ message = dbus_connection_borrow_message (connection); if (message == NULL) { _dbus_warn ("No message after auto activation " "(should be a service announcement)\n"); dbus_connection_return_message (connection, message); message = NULL; goto out; } message_kind = check_got_service_info (message); dbus_connection_return_message (connection, message); message = NULL; switch (message_kind) { case GOT_SERVICE_CREATED: message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Failed to pop message we just put back! " "should have been a NameOwnerChanged (creation)\n"); goto out; } /* Check that ServiceOwnerChanged (creation) was correctly received */ if (!check_service_auto_activated (context, connection, SHELL_SUCCESS_SERVICE_NAME, base_service, message)) goto out; dbus_message_unref (message); message = NULL; break; case GOT_SERVICE_DELETED: { /* The service started up and got a base address, but then * failed to register under SHELL_SUCCESS_SERVICE_NAME */ CheckServiceOwnerChangedData socd; socd.expected_kind = SERVICE_DELETED; socd.expected_service_name = base_service; socd.failed = FALSE; socd.skip_connection = NULL; bus_test_clients_foreach (check_service_owner_changed_foreach, &socd); if (socd.failed) goto out; break; } case GOT_ERROR: case GOT_SOMETHING_ELSE: _dbus_warn ("Unexpected message after auto activation\n"); goto out; } } /* OK, now we've dealt with ServiceOwnerChanged signals, now should * come the method reply (or error) from the initial method call */ /* Note: if this test is run in OOM mode, it will block when the bus * doesn't send a reply due to OOM. */ block_connection_until_message_from_bus (context, connection, "reply from echo message after auto-activation"); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Failed to pop message! Should have been reply from echo message\n"); goto out; } if (dbus_message_get_reply_serial (message) != serial) { _dbus_warn ("Wrong reply serial\n"); goto out; } if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &argv[0], DBUS_TYPE_STRING, &argv[1], DBUS_TYPE_STRING, &argv[2], DBUS_TYPE_STRING, &argv[3], DBUS_TYPE_STRING, &argv[4], DBUS_TYPE_STRING, &argv[5], DBUS_TYPE_STRING, &argv[6], DBUS_TYPE_INVALID)) { _dbus_warn ("Error getting arguments from return\n"); goto out; } /* don't worry about arg[0] as it may be different depending on the path to the tests */ if (strcmp("-test", argv[1]) != 0) { _dbus_warn ("Unexpected argv[1] in shell success service test (expected: %s, got: %s)\n", "-test", argv[1]); goto out; } if (strcmp("that", argv[2]) != 0) { _dbus_warn ("Unexpected argv[2] in shell success service test (expected: %s, got: %s)\n", "that", argv[2]); goto out; } if (strcmp("we get", argv[3]) != 0) { _dbus_warn ("Unexpected argv[3] in shell success service test (expected: %s, got: %s)\n", "we get", argv[3]); goto out; } if (strcmp("back", argv[4]) != 0) { _dbus_warn ("Unexpected argv[4] in shell success service test (expected: %s, got: %s)\n", "back", argv[4]); goto out; } if (strcmp("--what", argv[5]) != 0) { _dbus_warn ("Unexpected argv[5] in shell success service test (expected: %s, got: %s)\n", "--what", argv[5]); goto out; } if (strcmp("we put in", argv[6]) != 0) { _dbus_warn ("Unexpected argv[6] in shell success service test (expected: %s, got: %s)\n", "we put in", argv[6]); goto out; } dbus_message_unref (message); message = NULL; if (!check_send_exit_to_service (context, connection, SHELL_SUCCESS_SERVICE_NAME, base_service)) goto out; retval = TRUE; out: if (message) dbus_message_unref (message); if (base_service_message) dbus_message_unref (base_service_message); return retval; } typedef struct { Check1Func func; BusContext *context; } Check1Data; static dbus_bool_t check_oom_check1_func (void *data) { Check1Data *d = data; if (! (* d->func) (d->context)) return FALSE; if (!check_no_leftovers (d->context)) { _dbus_warn ("Messages were left over, should be covered by test suite\n"); return FALSE; } return TRUE; } static void check1_try_iterations (BusContext *context, const char *description, Check1Func func) { Check1Data d; d.func = func; d.context = context; if (!_dbus_test_oom_handling (description, check_oom_check1_func, &d)) _dbus_assert_not_reached ("test failed"); } static dbus_bool_t check_get_services (BusContext *context, DBusConnection *connection, const char *method, char ***services, int *len) { DBusMessage *message; dbus_uint32_t serial; dbus_bool_t retval; DBusError error; char **srvs; int l; retval = FALSE; dbus_error_init (&error); message = NULL; message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, method); if (message == NULL) return TRUE; if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } /* send our message */ bus_test_run_clients_loop (SEND_PENDING (connection)); dbus_message_unref (message); message = NULL; dbus_connection_ref (connection); /* because we may get disconnected */ block_connection_until_message_from_bus (context, connection, "reply to ListActivatableNames/ListNames"); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); dbus_connection_unref (connection); return TRUE; } dbus_connection_unref (connection); message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive a reply to %s %d on %p\n", method, serial, connection); goto out; } verbose_message_received (connection, message); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else { warn_unexpected (connection, message, "not this error"); goto out; } } else { if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) { ; /* good, expected */ } else { warn_unexpected (connection, message, "method_return for ListActivatableNames/ListNames"); goto out; } retry_get_property: if (!dbus_message_get_args (message, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &srvs, &l, DBUS_TYPE_INVALID)) { if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { _dbus_verbose ("no memory to list services by %s\n", method); dbus_error_free (&error); _dbus_wait_for_memory (); goto retry_get_property; } else { _dbus_assert (dbus_error_is_set (&error)); _dbus_warn ("Did not get the expected DBUS_TYPE_ARRAY from %s\n", method); goto out; } } else { *services = srvs; *len = l; } } if (!check_no_leftovers (context)) goto out; retval = TRUE; out: dbus_error_free (&error); if (message) dbus_message_unref (message); return retval; } /* returns TRUE if the correct thing happens, * but the correct thing may include OOM errors. */ static dbus_bool_t check_list_services (BusContext *context, DBusConnection *connection) { DBusMessage *message; DBusMessage *base_service_message; const char *base_service; dbus_uint32_t serial; dbus_bool_t retval; const char *existent = EXISTENT_SERVICE_NAME; dbus_uint32_t flags; char **services; int len; _dbus_verbose ("check_list_services for %p\n", connection); if (!check_get_services (context, connection, "ListActivatableNames", &services, &len)) { return TRUE; } if (!_dbus_string_array_contains ((const char **)services, existent)) { _dbus_warn ("Did not get the expected %s from ListActivatableNames\n", existent); dbus_free_string_array (services); return FALSE; } dbus_free_string_array (services); base_service_message = NULL; message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "StartServiceByName"); if (message == NULL) return TRUE; dbus_message_set_auto_start (message, FALSE); flags = 0; if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &existent, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID)) { dbus_message_unref (message); return TRUE; } if (!dbus_connection_send (connection, message, &serial)) { dbus_message_unref (message); return TRUE; } dbus_message_unref (message); message = NULL; bus_test_run_everything (context); /* now wait for the message bus to hear back from the activated * service. */ block_connection_until_message_from_bus (context, connection, "activated service to connect"); bus_test_run_everything (context); if (!dbus_connection_get_is_connected (connection)) { _dbus_verbose ("connection was disconnected\n"); return TRUE; } retval = FALSE; message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Did not receive any messages after %s %d on %p\n", "StartServiceByName", serial, connection); goto out; } verbose_message_received (connection, message); _dbus_verbose (" (after sending %s)\n", "StartServiceByName"); if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) { if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) { _dbus_warn ("Message has wrong sender %s\n", dbus_message_get_sender (message) ? dbus_message_get_sender (message) : "(none)"); goto out; } if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) { ; /* good, this is a valid response */ } else if (dbus_message_is_error (message, DBUS_ERROR_SPAWN_CHILD_EXITED) || dbus_message_is_error (message, DBUS_ERROR_SPAWN_CHILD_SIGNALED) || dbus_message_is_error (message, DBUS_ERROR_SPAWN_EXEC_FAILED)) { ; /* good, this is expected also */ } else { _dbus_warn ("Did not expect error %s\n", dbus_message_get_error_name (message)); goto out; } } else { GotServiceInfo message_kind; if (!check_base_service_activated (context, connection, message, &base_service)) goto out; base_service_message = message; message = NULL; /* We may need to block here for the test service to exit or finish up */ block_connection_until_message_from_bus (context, connection, "test service to exit or finish up"); message = dbus_connection_borrow_message (connection); if (message == NULL) { _dbus_warn ("Did not receive any messages after base service creation notification\n"); goto out; } message_kind = check_got_service_info (message); dbus_connection_return_message (connection, message); message = NULL; switch (message_kind) { case GOT_SOMETHING_ELSE: case GOT_ERROR: case GOT_SERVICE_DELETED: _dbus_warn ("Unexpected message after ActivateService " "(should be an error or a service announcement)\n"); goto out; case GOT_SERVICE_CREATED: message = pop_message_waiting_for_memory (connection); if (message == NULL) { _dbus_warn ("Failed to pop message we just put back! " "should have been a NameOwnerChanged (creation)\n"); goto out; } if (!check_service_activated (context, connection, EXISTENT_SERVICE_NAME, base_service, message)) goto out; dbus_message_unref (message); message = NULL; if (!check_no_leftovers (context)) { _dbus_warn ("Messages were left over after successful activation\n"); goto out; } break; } } if (!check_get_services (context, connection, "ListNames", &services, &len)) { return TRUE; } if (!_dbus_string_array_contains ((const char **)services, existent)) { _dbus_warn ("Did not get the expected %s from ListNames\n", existent); goto out; } dbus_free_string_array (services); if (!check_send_exit_to_service (context, connection, EXISTENT_SERVICE_NAME, base_service)) goto out; retval = TRUE; out: if (message) dbus_message_unref (message); if (base_service_message) dbus_message_unref (base_service_message); return retval; } typedef struct { Check2Func func; BusContext *context; DBusConnection *connection; } Check2Data; static dbus_bool_t check_oom_check2_func (void *data) { Check2Data *d = data; if (! (* d->func) (d->context, d->connection)) return FALSE; if (!check_no_leftovers (d->context)) { _dbus_warn ("Messages were left over, should be covered by test suite\n"); return FALSE; } return TRUE; } static void check2_try_iterations (BusContext *context, DBusConnection *connection, const char *description, Check2Func func) { Check2Data d; d.func = func; d.context = context; d.connection = connection; if (!_dbus_test_oom_handling (description, check_oom_check2_func, &d)) { _dbus_warn ("%s failed during oom\n", description); _dbus_assert_not_reached ("test failed"); } } static dbus_bool_t setenv_TEST_LAUNCH_HELPER_CONFIG(const DBusString *test_data_dir, const char *filename) { DBusString full; DBusString file; if (!_dbus_string_init (&full)) return FALSE; if (!_dbus_string_copy (test_data_dir, 0, &full, 0)) { _dbus_string_free (&full); return FALSE; } _dbus_string_init_const (&file, filename); if (!_dbus_concat_dir_and_file (&full, &file)) { _dbus_string_free (&full); return FALSE; } _dbus_verbose ("Setting TEST_LAUNCH_HELPER_CONFIG to '%s'\n", _dbus_string_get_const_data (&full)); dbus_setenv ("TEST_LAUNCH_HELPER_CONFIG", _dbus_string_get_const_data (&full)); _dbus_string_free (&full); return TRUE; } static dbus_bool_t bus_dispatch_test_conf (const DBusString *test_data_dir, const char *filename, dbus_bool_t use_launcher) { BusContext *context; DBusConnection *foo; DBusConnection *bar; DBusConnection *baz; DBusError error; /* save the config name for the activation helper */ if (!setenv_TEST_LAUNCH_HELPER_CONFIG (test_data_dir, filename)) _dbus_assert_not_reached ("no memory setting TEST_LAUNCH_HELPER_CONFIG"); dbus_error_init (&error); context = bus_context_new_test (test_data_dir, filename); if (context == NULL) return FALSE; foo = dbus_connection_open_private (TEST_DEBUG_PIPE, &error); if (foo == NULL) _dbus_assert_not_reached ("could not alloc connection"); if (!bus_setup_debug_client (foo)) _dbus_assert_not_reached ("could not set up connection"); spin_connection_until_authenticated (context, foo); if (!check_hello_message (context, foo)) _dbus_assert_not_reached ("hello message failed"); if (!check_double_hello_message (context, foo)) _dbus_assert_not_reached ("double hello message failed"); if (!check_add_match (context, foo, "")) _dbus_assert_not_reached ("AddMatch message failed"); bar = dbus_connection_open_private (TEST_DEBUG_PIPE, &error); if (bar == NULL) _dbus_assert_not_reached ("could not alloc connection"); if (!bus_setup_debug_client (bar)) _dbus_assert_not_reached ("could not set up connection"); spin_connection_until_authenticated (context, bar); if (!check_hello_message (context, bar)) _dbus_assert_not_reached ("hello message failed"); if (!check_add_match (context, bar, "")) _dbus_assert_not_reached ("AddMatch message failed"); baz = dbus_connection_open_private (TEST_DEBUG_PIPE, &error); if (baz == NULL) _dbus_assert_not_reached ("could not alloc connection"); if (!bus_setup_debug_client (baz)) _dbus_assert_not_reached ("could not set up connection"); spin_connection_until_authenticated (context, baz); if (!check_hello_message (context, baz)) _dbus_assert_not_reached ("hello message failed"); if (!check_add_match (context, baz, "")) _dbus_assert_not_reached ("AddMatch message failed"); if (!check_add_match (context, baz, "interface='com.example'")) _dbus_assert_not_reached ("AddMatch message failed"); #ifdef DBUS_ENABLE_STATS if (!check_get_all_match_rules (context, baz)) _dbus_assert_not_reached ("GetAllMatchRules message failed"); #endif #ifdef DBUS_WIN_FIXME _dbus_warn("TODO: testing of GetConnectionUnixUser message skipped for now\n"); _dbus_warn("TODO: testing of GetConnectionUnixProcessID message skipped for now\n"); #else if (!check_get_connection_unix_user (context, baz)) _dbus_assert_not_reached ("GetConnectionUnixUser message failed"); if (!check_get_connection_unix_process_id (context, baz)) _dbus_assert_not_reached ("GetConnectionUnixProcessID message failed"); #endif if (!check_list_services (context, baz)) _dbus_assert_not_reached ("ListActivatableNames message failed"); if (!check_no_leftovers (context)) { _dbus_warn ("Messages were left over after setting up initial connections\n"); _dbus_assert_not_reached ("initial connection setup failed"); } check1_try_iterations (context, "create_and_hello", check_hello_connection); check2_try_iterations (context, foo, "nonexistent_service_no_auto_start", check_nonexistent_service_no_auto_start); #ifdef DBUS_WIN_FIXME _dbus_warn("TODO: dispatch.c segfault_service_no_auto_start test\n"); #else check2_try_iterations (context, foo, "segfault_service_no_auto_start", check_segfault_service_no_auto_start); #endif check2_try_iterations (context, foo, "existent_service_no_auto_start", check_existent_service_no_auto_start); check2_try_iterations (context, foo, "nonexistent_service_auto_start", check_nonexistent_service_auto_start); #ifdef DBUS_WIN_FIXME _dbus_warn("TODO: dispatch.c segfault_service_auto_start test\n"); #else /* only do the segfault test if we are not using the launcher */ check2_try_iterations (context, foo, "segfault_service_auto_start", check_segfault_service_auto_start); #endif /* only do the shell fail test if we are not using the launcher */ check2_try_iterations (context, foo, "shell_fail_service_auto_start", check_shell_fail_service_auto_start); /* specific to launcher */ if (use_launcher) if (!check_launch_service_file_missing (context, foo)) _dbus_assert_not_reached ("did not get service file not found error"); #if 0 /* Note: need to resolve some issues with the testing code in order to run * this in oom (handle that we sometimes don't get replies back from the bus * when oom happens, without blocking the test). */ check2_try_iterations (context, foo, "existent_service_auto_auto_start", check_existent_service_auto_start); #endif if (!check_existent_service_auto_start (context, foo)) _dbus_assert_not_reached ("existent service auto start failed"); if (!check_shell_service_success_auto_start (context, foo)) _dbus_assert_not_reached ("shell success service auto start failed"); _dbus_verbose ("Disconnecting foo, bar, and baz\n"); kill_client_connection_unchecked (foo); kill_client_connection_unchecked (bar); kill_client_connection_unchecked (baz); bus_context_unref (context); return TRUE; } static dbus_bool_t bus_dispatch_test_conf_fail (const DBusString *test_data_dir, const char *filename) { BusContext *context; DBusConnection *foo; DBusError error; /* save the config name for the activation helper */ if (!setenv_TEST_LAUNCH_HELPER_CONFIG (test_data_dir, filename)) _dbus_assert_not_reached ("no memory setting TEST_LAUNCH_HELPER_CONFIG"); dbus_error_init (&error); context = bus_context_new_test (test_data_dir, filename); if (context == NULL) return FALSE; foo = dbus_connection_open_private (TEST_DEBUG_PIPE, &error); if (foo == NULL) _dbus_assert_not_reached ("could not alloc connection"); if (!bus_setup_debug_client (foo)) _dbus_assert_not_reached ("could not set up connection"); spin_connection_until_authenticated (context, foo); if (!check_hello_message (context, foo)) _dbus_assert_not_reached ("hello message failed"); if (!check_double_hello_message (context, foo)) _dbus_assert_not_reached ("double hello message failed"); if (!check_add_match (context, foo, "")) _dbus_assert_not_reached ("AddMatch message failed"); /* this only tests the activation.c user check */ if (!check_launch_service_user_missing (context, foo)) _dbus_assert_not_reached ("user missing did not trigger error"); /* this only tests the desktop.c exec check */ if (!check_launch_service_exec_missing (context, foo)) _dbus_assert_not_reached ("exec missing did not trigger error"); /* this only tests the desktop.c service check */ if (!check_launch_service_service_missing (context, foo)) _dbus_assert_not_reached ("service missing did not trigger error"); _dbus_verbose ("Disconnecting foo\n"); kill_client_connection_unchecked (foo); bus_context_unref (context); return TRUE; } dbus_bool_t bus_dispatch_test (const DBusString *test_data_dir) { /* run normal activation tests */ _dbus_verbose ("Normal activation tests\n"); if (!bus_dispatch_test_conf (test_data_dir, "valid-config-files/debug-allow-all.conf", FALSE)) return FALSE; #ifdef DBUS_WIN _dbus_warn("Info: Launch helper activation tests skipped because launch-helper is not supported yet\n"); #else /* run launch-helper activation tests */ _dbus_verbose ("Launch helper activation tests\n"); if (!bus_dispatch_test_conf (test_data_dir, "valid-config-files-system/debug-allow-all-pass.conf", TRUE)) return FALSE; /* run select launch-helper activation tests on broken service files */ if (!bus_dispatch_test_conf_fail (test_data_dir, "valid-config-files-system/debug-allow-all-fail.conf")) return FALSE; #endif return TRUE; } dbus_bool_t bus_dispatch_sha1_test (const DBusString *test_data_dir) { BusContext *context; DBusConnection *foo; DBusError error; dbus_error_init (&error); /* Test SHA1 authentication */ _dbus_verbose ("Testing SHA1 context\n"); context = bus_context_new_test (test_data_dir, "valid-config-files/debug-allow-all-sha1.conf"); if (context == NULL) return FALSE; foo = dbus_connection_open_private (TEST_DEBUG_PIPE, &error); if (foo == NULL) _dbus_assert_not_reached ("could not alloc connection"); if (!bus_setup_debug_client (foo)) _dbus_assert_not_reached ("could not set up connection"); spin_connection_until_authenticated (context, foo); if (!check_hello_message (context, foo)) _dbus_assert_not_reached ("hello message failed"); if (!check_add_match (context, foo, "")) _dbus_assert_not_reached ("addmatch message failed"); if (!check_no_leftovers (context)) { _dbus_warn ("Messages were left over after setting up initial SHA-1 connection\n"); _dbus_assert_not_reached ("initial connection setup failed"); } check1_try_iterations (context, "create_and_hello_sha1", check_hello_connection); kill_client_connection_unchecked (foo); bus_context_unref (context); return TRUE; } #ifdef HAVE_UNIX_FD_PASSING dbus_bool_t bus_unix_fds_passing_test(const DBusString *test_data_dir) { BusContext *context; DBusConnection *foo, *bar; DBusError error; DBusMessage *m; int one[2], two[2], x, y, z; char r; dbus_error_init (&error); context = bus_context_new_test (test_data_dir, "valid-config-files/debug-allow-all.conf"); if (context == NULL) _dbus_assert_not_reached ("could not alloc context"); foo = dbus_connection_open_private (TEST_DEBUG_PIPE, &error); if (foo == NULL) _dbus_assert_not_reached ("could not alloc connection"); if (!bus_setup_debug_client (foo)) _dbus_assert_not_reached ("could not set up connection"); spin_connection_until_authenticated (context, foo); if (!check_hello_message (context, foo)) _dbus_assert_not_reached ("hello message failed"); if (!check_add_match (context, foo, "")) _dbus_assert_not_reached ("AddMatch message failed"); bar = dbus_connection_open_private (TEST_DEBUG_PIPE, &error); if (bar == NULL) _dbus_assert_not_reached ("could not alloc connection"); if (!bus_setup_debug_client (bar)) _dbus_assert_not_reached ("could not set up connection"); spin_connection_until_authenticated (context, bar); if (!check_hello_message (context, bar)) _dbus_assert_not_reached ("hello message failed"); if (!check_add_match (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_ENABLE_EMBEDDED_TESTS */