From 7628b541258d906e27e2000a402ed2d02383479c Mon Sep 17 00:00:00 2001 From: "John (J5) Palmieri" Date: Fri, 14 Jul 2006 01:17:59 +0000 Subject: * bus/activation.[ch] (bus_activation_list_services): new function to get the list of services that can be activated * bus/dispatch.c: test coverage for the new bus method ListActivatableNames * bus/driver.c: new bus method ListActivatableNames to get the list of services that can be activated * doc/dbus-specification.xml: ListActivatableNames method documentation --- ChangeLog | 13 ++ bus/activation.c | 45 ++++++ bus/activation.h | 3 + bus/dispatch.c | 349 ++++++++++++++++++++++++++++++++++++++++++++- bus/driver.c | 100 ++++++++++++- doc/dbus-specification.xml | 31 ++++ 6 files changed, 537 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 437ac04d..d9f45113 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2006-07-13 Carlos Garcia Campos + + * bus/activation.[ch] (bus_activation_list_services): new function to + get the list of services that can be activated + + * bus/dispatch.c: test coverage for the new bus method + ListActivatableNames + + * bus/driver.c: new bus method ListActivatableNames to get the list of + services that can be activated + + * doc/dbus-specification.xml: ListActivatableNames method documentation + 2006-07-12 John (J5) Palmieri * dbus/Makefile.am: add dbus-pending-call-internal.h to the list of source files diff --git a/bus/activation.c b/bus/activation.c index 1cdedb9f..40221938 100644 --- a/bus/activation.c +++ b/bus/activation.c @@ -1565,6 +1565,51 @@ bus_activation_activate_service (BusActivation *activation, return TRUE; } +dbus_bool_t +bus_activation_list_services (BusActivation *activation, + char ***listp, + int *array_len) +{ + int i, j, len; + char **retval; + DBusHashIter iter; + + len = _dbus_hash_table_get_n_entries (activation->entries); + retval = dbus_new (char *, len + 1); + + if (retval == NULL) + return FALSE; + + _dbus_hash_iter_init (activation->entries, &iter); + i = 0; + while (_dbus_hash_iter_next (&iter)) + { + BusActivationEntry *entry = _dbus_hash_iter_get_value (&iter); + + retval[i] = _dbus_strdup (entry->name); + if (retval[i] == NULL) + goto error; + + i++; + } + + retval[i] = NULL; + + if (array_len) + *array_len = len; + + *listp = retval; + return TRUE; + + error: + for (j = 0; j < i; j++) + dbus_free (retval[i]); + dbus_free (retval); + + return FALSE; +} + + #ifdef DBUS_BUILD_TESTS #include diff --git a/bus/activation.h b/bus/activation.h index d12d8718..88d5bbce 100644 --- a/bus/activation.h +++ b/bus/activation.h @@ -45,6 +45,9 @@ dbus_bool_t bus_activation_service_created (BusActivation *activation, const char *service_name, BusTransaction *transaction, DBusError *error); +dbus_bool_t bus_activation_list_services (BusActivation *registry, + char ***listp, + int *array_len); dbus_bool_t bus_activation_send_pending_auto_activation_messages (BusActivation *activation, BusService *service, diff --git a/bus/dispatch.c b/bus/dispatch.c index 526c0182..e1dd001f 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -2590,9 +2590,9 @@ check_existent_service_no_auto_start (BusContext *context, goto out; } - if (!check_send_exit_to_service (context, connection, + if (!check_send_exit_to_service (context, connection, EXISTENT_SERVICE_NAME, base_service)) - goto out; + goto out; break; } @@ -3512,6 +3512,348 @@ check1_try_iterations (BusContext *context, _dbus_assert_not_reached ("test failed"); } +static dbus_bool_t +check_get_services (BusContext *context, + DBusConnection *connection, + const char *method, + char ***services, + int *len) +{ + DBusMessage *message; + dbus_uint32_t serial; + dbus_bool_t retval; + DBusError error; + char **srvs; + int l; + + retval = FALSE; + dbus_error_init (&error); + message = NULL; + + message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + method); + + if (message == NULL) + return TRUE; + + if (!dbus_connection_send (connection, message, &serial)) + { + dbus_message_unref (message); + return TRUE; + } + + /* send our message */ + bus_test_run_clients_loop (SEND_PENDING (connection)); + + dbus_message_unref (message); + message = NULL; + + dbus_connection_ref (connection); /* because we may get disconnected */ + block_connection_until_message_from_bus (context, connection, "reply to ListActivatableNames/ListNames"); + + if (!dbus_connection_get_is_connected (connection)) + { + _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + + dbus_connection_unref (connection); + + return TRUE; + } + + dbus_connection_unref (connection); + + message = pop_message_waiting_for_memory (connection); + if (message == NULL) + { + _dbus_warn ("Did not receive a reply to %s %d on %p\n", + method, serial, connection); + goto out; + } + + verbose_message_received (connection, message); + + if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) + { + if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY)) + { + ; /* good, this is a valid response */ + } + else + { + warn_unexpected (connection, message, "not this error"); + + goto out; + } + } + else + { + if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) + { + ; /* good, expected */ + } + else + { + warn_unexpected (connection, message, + "method_return for ListActivatableNames/ListNames"); + + goto out; + } + + retry_get_property: + + if (!dbus_message_get_args (message, &error, + DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING, + &srvs, &l, + DBUS_TYPE_INVALID)) + { + if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) + { + _dbus_verbose ("no memory to list services by %s\n", method); + dbus_error_free (&error); + _dbus_wait_for_memory (); + goto retry_get_property; + } + else + { + _dbus_assert (dbus_error_is_set (&error)); + _dbus_warn ("Did not get the expected DBUS_TYPE_ARRAY from %s\n", method); + goto out; + } + } else { + *services = srvs; + *len = l; + } + } + + if (!check_no_leftovers (context)) + goto out; + + retval = TRUE; + + out: + dbus_error_free (&error); + + if (message) + dbus_message_unref (message); + + return retval; +} + +/* returns TRUE if the correct thing happens, + * but the correct thing may include OOM errors. + */ +static dbus_bool_t +check_list_services (BusContext *context, + DBusConnection *connection) +{ + DBusMessage *message; + DBusMessage *base_service_message; + const char *base_service; + dbus_uint32_t serial; + dbus_bool_t retval; + const char *existent = EXISTENT_SERVICE_NAME; + dbus_uint32_t flags; + char **services; + int len; + + _dbus_verbose ("check_list_services for %p\n", connection); + + if (!check_get_services (context, connection, "ListActivatableNames", &services, &len)) + { + return TRUE; + } + + if (!_dbus_string_array_contains ((const char **)services, existent)) + { + _dbus_warn ("Did not get the expected %s from ListActivatableNames\n", existent); + return FALSE; + } + + dbus_free_string_array (services); + + base_service_message = NULL; + + message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "StartServiceByName"); + + if (message == NULL) + return TRUE; + + dbus_message_set_auto_start (message, FALSE); + + flags = 0; + if (!dbus_message_append_args (message, + DBUS_TYPE_STRING, &existent, + DBUS_TYPE_UINT32, &flags, + DBUS_TYPE_INVALID)) + { + dbus_message_unref (message); + return TRUE; + } + + if (!dbus_connection_send (connection, message, &serial)) + { + dbus_message_unref (message); + return TRUE; + } + + dbus_message_unref (message); + message = NULL; + + bus_test_run_everything (context); + + /* now wait for the message bus to hear back from the activated + * service. + */ + block_connection_until_message_from_bus (context, connection, "activated service to connect"); + + bus_test_run_everything (context); + + if (!dbus_connection_get_is_connected (connection)) + { + _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__); + return TRUE; + } + + retval = FALSE; + + message = pop_message_waiting_for_memory (connection); + if (message == NULL) + { + _dbus_warn ("Did not receive any messages after %s %d on %p\n", + "StartServiceByName", serial, connection); + goto out; + } + + verbose_message_received (connection, message); + _dbus_verbose (" (after sending %s)\n", "StartServiceByName"); + + if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) + { + if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS)) + { + _dbus_warn ("Message has wrong sender %s\n", + dbus_message_get_sender (message) ? + dbus_message_get_sender (message) : "(none)"); + goto out; + } + + if (dbus_message_is_error (message, + DBUS_ERROR_NO_MEMORY)) + { + ; /* good, this is a valid response */ + } + else if (dbus_message_is_error (message, + DBUS_ERROR_SPAWN_CHILD_EXITED) || + dbus_message_is_error (message, + DBUS_ERROR_SPAWN_CHILD_SIGNALED) || + dbus_message_is_error (message, + DBUS_ERROR_SPAWN_EXEC_FAILED)) + { + ; /* good, this is expected also */ + } + else + { + _dbus_warn ("Did not expect error %s\n", + dbus_message_get_error_name (message)); + goto out; + } + } + else + { + GotServiceInfo message_kind; + + if (!check_base_service_activated (context, connection, + message, &base_service)) + goto out; + + base_service_message = message; + message = NULL; + + /* We may need to block here for the test service to exit or finish up */ + block_connection_until_message_from_bus (context, connection, "test service to exit or finish up"); + + message = dbus_connection_borrow_message (connection); + if (message == NULL) + { + _dbus_warn ("Did not receive any messages after base service creation notification\n"); + goto out; + } + + message_kind = check_got_service_info (message); + + dbus_connection_return_message (connection, message); + message = NULL; + + switch (message_kind) + { + case GOT_SOMETHING_ELSE: + case GOT_ERROR: + case GOT_SERVICE_DELETED: + _dbus_warn ("Unexpected message after ActivateService " + "(should be an error or a service announcement"); + 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; @@ -3625,6 +3967,9 @@ bus_dispatch_test (const DBusString *test_data_dir) if (!check_get_connection_unix_process_id (context, baz)) _dbus_assert_not_reached ("GetConnectionUnixProcessID message failed"); + + if (!check_list_services (context, baz)) + _dbus_assert_not_reached ("ListActivatableNames message failed"); if (!check_no_leftovers (context)) { diff --git a/bus/driver.c b/bus/driver.c index 10f37bd2..636c8cad 100644 --- a/bus/driver.c +++ b/bus/driver.c @@ -436,16 +436,108 @@ bus_driver_handle_list_services (DBusConnection *connection, ++i; } + dbus_free_string_array (services); + if (!dbus_message_iter_close_container (&iter, &sub)) { - dbus_free_string_array (services); dbus_message_unref (reply); BUS_SET_OOM (error); return FALSE; } + if (!bus_transaction_send_from_driver (transaction, connection, reply)) + { + dbus_message_unref (reply); + BUS_SET_OOM (error); + return FALSE; + } + else + { + dbus_message_unref (reply); + return TRUE; + } +} + +static dbus_bool_t +bus_driver_handle_list_activatable_services (DBusConnection *connection, + BusTransaction *transaction, + DBusMessage *message, + DBusError *error) +{ + DBusMessage *reply; + int len; + char **services; + BusActivation *activation; + int i; + DBusMessageIter iter; + DBusMessageIter sub; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + activation = bus_connection_get_activation (connection); + + reply = dbus_message_new_method_return (message); + if (reply == NULL) + { + BUS_SET_OOM (error); + return FALSE; + } + + if (!bus_activation_list_services (activation, &services, &len)) + { + dbus_message_unref (reply); + BUS_SET_OOM (error); + return FALSE; + } + + dbus_message_iter_init_append (reply, &iter); + + if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, + &sub)) + { + dbus_free_string_array (services); + dbus_message_unref (reply); + BUS_SET_OOM (error); + return FALSE; + } + + { + /* Include the bus driver in the list */ + const char *v_STRING = DBUS_SERVICE_DBUS; + if (!dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING, + &v_STRING)) + { + dbus_free_string_array (services); + dbus_message_unref (reply); + BUS_SET_OOM (error); + return FALSE; + } + } + + i = 0; + while (i < len) + { + if (!dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING, + &services[i])) + { + dbus_free_string_array (services); + dbus_message_unref (reply); + BUS_SET_OOM (error); + return FALSE; + } + ++i; + } + dbus_free_string_array (services); - + + if (!dbus_message_iter_close_container (&iter, &sub)) + { + dbus_message_unref (reply); + BUS_SET_OOM (error); + return FALSE; + } + if (!bus_transaction_send_from_driver (transaction, connection, reply)) { dbus_message_unref (reply); @@ -1328,6 +1420,10 @@ struct "", DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING, bus_driver_handle_list_services }, + { "ListActivatableNames", + "", + DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING, + bus_driver_handle_list_activatable_services }, { "AddMatch", DBUS_TYPE_STRING_AS_STRING, "", diff --git a/doc/dbus-specification.xml b/doc/dbus-specification.xml index 52422277..05f6ad1c 100644 --- a/doc/dbus-specification.xml +++ b/doc/dbus-specification.xml @@ -3267,6 +3267,37 @@ Returns a list of all currently-owned names on the bus. + + <literal>org.freedesktop.DBus.ListActivatableNames</literal> + + As a method: + + ARRAY of STRING ListActivatableNames () + + Reply arguments: + + + + + Argument + Type + Description + + + + + 0 + ARRAY of STRING + Array of strings where each string is a bus name + + + + + + + Returns a list of all names that can be activated on the bus. + + <literal>org.freedesktop.DBus.NameHasOwner</literal> -- cgit