* bus/activation.[ch] (bus_activation_list_services): new function to
authorJohn (J5) Palmieri <johnp@redhat.com>
Fri, 14 Jul 2006 01:17:59 +0000 (01:17 +0000)
committerJohn (J5) Palmieri <johnp@redhat.com>
Fri, 14 Jul 2006 01:17:59 +0000 (01:17 +0000)
  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
bus/activation.c
bus/activation.h
bus/dispatch.c
bus/driver.c
doc/dbus-specification.xml

index 437ac04..d9f4511 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2006-07-13  Carlos Garcia Campos  <carlosgc@gnome.org>
+
+       * 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  <johnp@redhat.com>
        * dbus/Makefile.am: add dbus-pending-call-internal.h to the list of
        source files
index 1cdedb9..4022193 100644 (file)
@@ -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 <stdio.h>
index d12d871..88d5bbc 100644 (file)
@@ -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,
index 526c018..e1dd001 100644 (file)
@@ -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))
     {
index 10f37bd..636c8ca 100644 (file)
@@ -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,
     "",
index 5242227..05f6ad1 100644 (file)
           Returns a list of all currently-owned names on the bus.
         </para>
       </sect3>
+      <sect3 id="bus-messages-list-activatable-names">
+        <title><literal>org.freedesktop.DBus.ListActivatableNames</literal></title>
+        <para>
+          As a method:
+          <programlisting>
+            ARRAY of STRING ListActivatableNames ()
+          </programlisting>
+          Reply arguments:
+          <informaltable>
+            <tgroup cols="3">
+              <thead>
+                <row>
+                  <entry>Argument</entry>
+                  <entry>Type</entry>
+                  <entry>Description</entry>
+                </row>
+              </thead>
+              <tbody>
+                <row>
+                  <entry>0</entry>
+                  <entry>ARRAY of STRING</entry>
+                  <entry>Array of strings where each string is a bus name</entry>
+                </row>
+              </tbody>
+            </tgroup>
+          </informaltable>
+        </para>
+        <para>
+          Returns a list of all names that can be activated on the bus.
+        </para>
+      </sect3>
       <sect3 id="bus-messages-name-exists">
         <title><literal>org.freedesktop.DBus.NameHasOwner</literal></title>
         <para>