Checking in Rodrigo's patch along with my fixes to the patch
authorJohn (J5) Palmieri <johnp@redhat.com>
Thu, 14 Jul 2005 20:44:15 +0000 (20:44 +0000)
committerJohn (J5) Palmieri <johnp@redhat.com>
Thu, 14 Jul 2005 20:44:15 +0000 (20:44 +0000)
2005-07-14  John (J5) Palmieri  <johnp@redhat.com>

* bus/activation.c: clean up all tabs to be 8 spaces
(bus_activation_activate_service): make sure we clean up
if activation fails

* bus/dispatch.c: clean up all tabs to be 8 spaces
(check_shell_fail_service_auto_start): New function
tests to make sure we get fail properly when trying to auto start a service
with a faulty command line
(check_shell_service_success_auto_start): New function tests to make sure
auto started services get the arguments on the command line

* test/test-shell-service.c: Added service for testing auto-starting with
command line arguments

* test/data/valid-service-files/debug-shell-echo-fail.service.in,
test/data/valid-service-files/debug-shell-echo-success.service.in:
Added service files for testing auto-starting with command line arguments

* */.cvsignore: added a bunch of generated files to various .cvsignore files

2005-07-14  Rodrigo Moya  <rodrigo@novell.com>

* dbus/dbus-shell.[ch]: copy/pasted code from GLib.

* dbus/Makefile.am: added new files to build.

* bus/activation.c (bus_activation_activate_service): support
activation commands with parameters.

* test/shell-test.c: added test program for the shell parsing
code.

16 files changed:
ChangeLog
bus/activation.c
bus/dispatch.c
configure.in
dbus/Makefile.am
dbus/dbus-shell.c [new file with mode: 0644]
dbus/dbus-shell.h [new file with mode: 0644]
python/.cvsignore
python/Makefile.am
test/.cvsignore
test/Makefile.am
test/data/valid-service-files/.cvsignore
test/data/valid-service-files/debug-shell-echo-fail.service.in [new file with mode: 0644]
test/data/valid-service-files/debug-shell-echo-success.service.in [new file with mode: 0644]
test/shell-test.c [new file with mode: 0644]
test/test-shell-service.c [new file with mode: 0644]

index 6f3e670..45640f7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+2005-07-14  John (J5) Palmieri  <johnp@redhat.com>
+
+       * bus/activation.c: clean up all tabs to be 8 spaces 
+       (bus_activation_activate_service): make sure we clean up
+       if activation fails
+
+       * bus/dispatch.c: clean up all tabs to be 8 spaces 
+       (check_shell_fail_service_auto_start): New function
+       tests to make sure we get fail properly when trying to auto start a service
+       with a faulty command line
+       (check_shell_service_success_auto_start): New function tests to make sure
+       auto started services get the arguments on the command line
+
+       * test/test-shell-service.c: Added service for testing auto-starting with 
+       command line arguments
+
+       * test/data/valid-service-files/debug-shell-echo-fail.service.in, 
+       test/data/valid-service-files/debug-shell-echo-success.service.in:
+       Added service files for testing auto-starting with command line arguments
+
+       * */.cvsignore: added a bunch of generated files to various .cvsignore files
+
+2005-07-14  Rodrigo Moya  <rodrigo@novell.com>
+
+       * dbus/dbus-shell.[ch]: copy/pasted code from GLib.
+       
+       * dbus/Makefile.am: added new files to build.
+
+       * bus/activation.c (bus_activation_activate_service): support
+       activation commands with parameters.
+
+       * test/shell-test.c: added test program for the shell parsing
+       code.
+
 2005-07-13  David Zeuthen  <davidz@redhat.com>
 
        * tools/dbus-send.c (append_arg, type_from_name): Also support 16 and
index 10839b9..2faa42b 100644 (file)
@@ -30,6 +30,7 @@
 #include <dbus/dbus-internals.h>
 #include <dbus/dbus-hash.h>
 #include <dbus/dbus-list.h>
+#include <dbus/dbus-shell.h>
 #include <dbus/dbus-spawn.h>
 #include <dbus/dbus-timeout.h>
 #include <dirent.h>
@@ -49,7 +50,7 @@ struct BusActivation
   int n_pending_activations; /**< This is in fact the number of BusPendingActivationEntry,
                               * i.e. number of pending activation requests, not pending
                               * activations per se
-                             */
+                              */
   DBusHashTable *directories;
 };
 
@@ -244,10 +245,10 @@ bus_activation_entry_unref (BusActivationEntry *entry)
 
 static dbus_bool_t
 update_desktop_file_entry (BusActivation       *activation,
-                          BusServiceDirectory *s_dir,
-                          DBusString          *filename,
-                          BusDesktopFile      *desktop_file,
-                          DBusError           *error)
+                           BusServiceDirectory *s_dir,
+                           DBusString          *filename,
+                           BusDesktopFile      *desktop_file,
+                           DBusError           *error)
 {
   char *name, *exec;
   BusActivationEntry *entry;
@@ -276,14 +277,14 @@ update_desktop_file_entry (BusActivation       *activation,
   if (!_dbus_stat (&file_path, &stat_buf, NULL)) 
     {
       dbus_set_error (error, DBUS_ERROR_FAILED,
-                     "Can't stat the service file\n");
+                      "Can't stat the service file\n");
       goto failed;
     }
  
   if (!bus_desktop_file_get_string (desktop_file,
-                                   DBUS_SERVICE_SECTION,
-                                   DBUS_SERVICE_NAME,
-                                   &name))
+                                    DBUS_SERVICE_SECTION,
+                                    DBUS_SERVICE_NAME,
+                                    &name))
     {
       dbus_set_error (error, DBUS_ERROR_FAILED,
                       "No \""DBUS_SERVICE_NAME"\" key in .service file\n");
@@ -291,9 +292,9 @@ update_desktop_file_entry (BusActivation       *activation,
     }
 
   if (!bus_desktop_file_get_string (desktop_file,
-                                   DBUS_SERVICE_SECTION,
-                                   DBUS_SERVICE_EXEC,
-                                   &exec))
+                                    DBUS_SERVICE_SECTION,
+                                    DBUS_SERVICE_EXEC,
+                                    &exec))
     {
       dbus_set_error (error, DBUS_ERROR_FAILED,
                       "No \""DBUS_SERVICE_EXEC"\" key in .service file\n");
@@ -301,25 +302,25 @@ update_desktop_file_entry (BusActivation       *activation,
     }
 
   entry = _dbus_hash_table_lookup_string (s_dir->entries, 
-                                         _dbus_string_get_const_data (filename));
+                                          _dbus_string_get_const_data (filename));
   if (entry == NULL) /* New file */
     { 
       /* FIXME we need a better-defined algorithm for which service file to
        * pick than "whichever one is first in the directory listing"
        */
       if (_dbus_hash_table_lookup_string (activation->entries, name))
-       {
-         dbus_set_error (error, DBUS_ERROR_FAILED,
-                         "Service %s already exists in activation entry list\n", name);
-         goto failed;
-       }
+        {
+          dbus_set_error (error, DBUS_ERROR_FAILED,
+                          "Service %s already exists in activation entry list\n", name);
+          goto failed;
+        }
       
       entry = dbus_new0 (BusActivationEntry, 1);
       if (entry == NULL)
-       {
-         BUS_SET_OOM (error);
-         goto failed;
-       }
+        {
+          BUS_SET_OOM (error);
+          goto failed;
+        }
      
       entry->name = name;
       entry->exec = exec;
@@ -328,24 +329,24 @@ update_desktop_file_entry (BusActivation       *activation,
       entry->s_dir = s_dir;
       entry->filename = _dbus_strdup (_dbus_string_get_const_data (filename));
       if (!entry->filename)
-       {
-         BUS_SET_OOM (error);
-         goto failed;
-       }
+        {
+          BUS_SET_OOM (error);
+          goto failed;
+        }
 
       if (!_dbus_hash_table_insert_string (activation->entries, entry->name, bus_activation_entry_ref (entry)))
-       {
-         BUS_SET_OOM (error);
-         goto failed;
-       }
+        {
+          BUS_SET_OOM (error);
+          goto failed;
+        }
      
       if (!_dbus_hash_table_insert_string (s_dir->entries, entry->filename, bus_activation_entry_ref (entry)))
-       {
-         /* Revert the insertion in the entries table */
-         _dbus_hash_table_remove_string (activation->entries, entry->name);
-         BUS_SET_OOM (error);
-         goto failed;
-       }
+        {
+          /* Revert the insertion in the entries table */
+          _dbus_hash_table_remove_string (activation->entries, entry->name);
+          BUS_SET_OOM (error);
+          goto failed;
+        }
 
       _dbus_verbose ("Added \"%s\" to list of services\n", entry->name);
     }
@@ -355,27 +356,27 @@ update_desktop_file_entry (BusActivation       *activation,
       _dbus_hash_table_remove_string (activation->entries, entry->name);
 
       if (_dbus_hash_table_lookup_string (activation->entries, name))
-       {
-         _dbus_verbose ("The new service name \"%s\" of service file \"%s\" already in cache, ignoring\n",
-                        name, _dbus_string_get_const_data (&file_path));
-         goto failed;
-       }
+        {
+          _dbus_verbose ("The new service name \"%s\" of service file \"%s\" already in cache, ignoring\n",
+                         name, _dbus_string_get_const_data (&file_path));
+          goto failed;
+        }
  
       dbus_free (entry->name);
       dbus_free (entry->exec);
       entry->name = name;
       entry->exec = exec;
       if (!_dbus_hash_table_insert_string (activation->entries,
-                                          entry->name, bus_activation_entry_ref(entry)))
-       {
-         BUS_SET_OOM (error);
-         /* Also remove path to entries hash since we want this in sync with
-          * the entries hash table */
-         _dbus_hash_table_remove_string (entry->s_dir->entries, 
-                                         entry->filename);
-         bus_activation_entry_unref (entry);
-         return FALSE;
-       }
+                                           entry->name, bus_activation_entry_ref(entry)))
+        {
+          BUS_SET_OOM (error);
+          /* Also remove path to entries hash since we want this in sync with
+           * the entries hash table */
+          _dbus_hash_table_remove_string (entry->s_dir->entries, 
+                                          entry->filename);
+          bus_activation_entry_unref (entry);
+          return FALSE;
+        }
     }
   
   entry->mtime = stat_buf.mtime;
@@ -398,9 +399,9 @@ failed:
 
 static dbus_bool_t
 check_service_file (BusActivation       *activation,
-                   BusActivationEntry  *entry,
-                   BusActivationEntry **updated_entry,
-                   DBusError           *error)
+                    BusActivationEntry  *entry,
+                    BusActivationEntry **updated_entry,
+                    DBusError           *error)
 {
   DBusStat stat_buf;
   dbus_bool_t retval;
@@ -430,7 +431,7 @@ check_service_file (BusActivation       *activation,
   if (!_dbus_stat (&file_path, &stat_buf, NULL))
     {
       _dbus_verbose ("****** Can't stat file \"%s\", removing from cache\n",
-                    _dbus_string_get_const_data (&file_path));
+                     _dbus_string_get_const_data (&file_path));
 
       _dbus_hash_table_remove_string (activation->entries, entry->name);
       _dbus_hash_table_remove_string (entry->s_dir->entries, entry->filename);
@@ -442,46 +443,46 @@ check_service_file (BusActivation       *activation,
   else 
     {
       if (stat_buf.mtime > entry->mtime) 
-       {
-         BusDesktopFile *desktop_file;
-         DBusError tmp_error;
-         
-         dbus_error_init (&tmp_error);
-         
-         desktop_file = bus_desktop_file_load (&file_path, &tmp_error);
-         if (desktop_file == NULL)
-           {
-             _dbus_verbose ("Could not load %s: %s\n",
-                            _dbus_string_get_const_data (&file_path), 
-                            tmp_error.message);
-             if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
-               {
-                 dbus_move_error (&tmp_error, error);
-                 retval = FALSE;
-                 goto out;
-               }
-             dbus_error_free (&tmp_error);
-             retval = TRUE;
-             goto out;
-           }
-         
-         if (!update_desktop_file_entry (activation, entry->s_dir, &filename, desktop_file, &tmp_error))
-           {
-             bus_desktop_file_free (desktop_file);
-             if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
-               {
-                 dbus_move_error (&tmp_error, error);
-                 retval = FALSE;
-                 goto out;
-               }
-             dbus_error_free (&tmp_error);
-             retval = TRUE;
-             goto out;
-           }
-        
-         bus_desktop_file_free (desktop_file);
-         retval = TRUE;
-       }
+        {
+          BusDesktopFile *desktop_file;
+          DBusError tmp_error;
+          
+          dbus_error_init (&tmp_error);
+          
+          desktop_file = bus_desktop_file_load (&file_path, &tmp_error);
+          if (desktop_file == NULL)
+            {
+              _dbus_verbose ("Could not load %s: %s\n",
+                             _dbus_string_get_const_data (&file_path), 
+                             tmp_error.message);
+              if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+                {
+                  dbus_move_error (&tmp_error, error);
+                  retval = FALSE;
+                  goto out;
+                }
+              dbus_error_free (&tmp_error);
+              retval = TRUE;
+              goto out;
+            }
+          
+          if (!update_desktop_file_entry (activation, entry->s_dir, &filename, desktop_file, &tmp_error))
+            {
+              bus_desktop_file_free (desktop_file);
+              if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+                {
+                  dbus_move_error (&tmp_error, error);
+                  retval = FALSE;
+                  goto out;
+                }
+              dbus_error_free (&tmp_error);
+              retval = TRUE;
+              goto out;
+            }
+         
+          bus_desktop_file_free (desktop_file);
+          retval = TRUE;
+        }
     }
   
 out:
@@ -498,8 +499,8 @@ out:
  */
 static dbus_bool_t
 update_directory (BusActivation       *activation,
-                 BusServiceDirectory *s_dir,
-                 DBusError           *error)
+                  BusServiceDirectory *s_dir,
+                  DBusError           *error)
 {
   DBusDirIter *iter;
   DBusString dir, filename;
@@ -537,8 +538,8 @@ update_directory (BusActivation       *activation,
   if (iter == NULL)
     {
       _dbus_verbose ("Failed to open directory %s: %s\n",
-                    s_dir->dir_c, 
-                    error ? error->message : "unknown");
+                     s_dir->dir_c, 
+                     error ? error->message : "unknown");
       goto out;
     }
   
@@ -551,35 +552,35 @@ update_directory (BusActivation       *activation,
       _dbus_string_set_length (&full_path, 0);
       
       if (!_dbus_string_ends_with_c_str (&filename, ".service"))
-       {
-         _dbus_verbose ("Skipping non-.service file %s\n",
+        {
+          _dbus_verbose ("Skipping non-.service file %s\n",
                          _dbus_string_get_const_data (&filename));
-         continue;
-       }
+          continue;
+        }
 
       entry = _dbus_hash_table_lookup_string (s_dir->entries, _dbus_string_get_const_data (&filename));
       if (entry) /* Already has this service file in the cache */ 
-       {
-         if (!check_service_file (activation, entry, NULL, error))
-           goto out;
+        {
+          if (!check_service_file (activation, entry, NULL, error))
+            goto out;
 
-         continue;
-       }
+          continue;
+        }
       
       if (!_dbus_string_append (&full_path, s_dir->dir_c) ||
-         !_dbus_concat_dir_and_file (&full_path, &filename))
+          !_dbus_concat_dir_and_file (&full_path, &filename))
         {
-         BUS_SET_OOM (error);
-         goto out;
-       }
+          BUS_SET_OOM (error);
+          goto out;
+        }
           
       /* New file */
       desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
       if (desktop_file == NULL)
-       {
-         _dbus_verbose ("Could not load %s: %s\n",
+        {
+          _dbus_verbose ("Could not load %s: %s\n",
                          _dbus_string_get_const_data (&full_path),
-                        tmp_error.message);
+                         tmp_error.message);
 
           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
             {
@@ -587,16 +588,16 @@ update_directory (BusActivation       *activation,
               goto out;
             }
           
-         dbus_error_free (&tmp_error);
-         continue;
-       }
+          dbus_error_free (&tmp_error);
+          continue;
+        }
 
       if (!update_desktop_file_entry (activation, s_dir, &filename, desktop_file, &tmp_error))
-       {
-         bus_desktop_file_free (desktop_file);
+        {
+          bus_desktop_file_free (desktop_file);
           desktop_file = NULL;
-         
-         _dbus_verbose ("Could not add %s to activation entry list: %s\n",
+          
+          _dbus_verbose ("Could not add %s to activation entry list: %s\n",
                          _dbus_string_get_const_data (&full_path), tmp_error.message);
 
           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
@@ -606,8 +607,8 @@ update_directory (BusActivation       *activation,
             }
 
           dbus_error_free (&tmp_error);
-         continue;
-       }
+          continue;
+        }
       else
         {
           bus_desktop_file_free (desktop_file);
@@ -642,7 +643,7 @@ update_directory (BusActivation       *activation,
 
 BusActivation*
 bus_activation_new (BusContext        *context,
-                   const DBusString  *address,
+                    const DBusString  *address,
                     DBusList         **directories,
                     DBusError         *error)
 {
@@ -678,7 +679,7 @@ bus_activation_new (BusContext        *context,
     }
 
   activation->pending_activations = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
-                                                         (DBusFreeFunction)bus_pending_activation_unref);
+                                                          (DBusFreeFunction)bus_pending_activation_unref);
 
   if (activation->pending_activations == NULL)
     {
@@ -687,7 +688,7 @@ bus_activation_new (BusContext        *context,
     }
 
   activation->directories = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
-                                                 (DBusFreeFunction)bus_service_directory_unref);
+                                                  (DBusFreeFunction)bus_service_directory_unref);
   
   if (activation->directories == NULL) 
     {
@@ -703,41 +704,41 @@ bus_activation_new (BusContext        *context,
       
       dir = _dbus_strdup ((const char *) link->data);
       if (!dir)
-       {
-         BUS_SET_OOM (error);
-         goto failed;
-       }
+        {
+          BUS_SET_OOM (error);
+          goto failed;
+        }
       
       s_dir = dbus_new0 (BusServiceDirectory, 1);
       if (!s_dir)
-       {
-         dbus_free (dir);
-         BUS_SET_OOM (error);
-         goto failed;
-       }
+        {
+          dbus_free (dir);
+          BUS_SET_OOM (error);
+          goto failed;
+        }
 
       s_dir->refcount = 1;
       s_dir->dir_c = dir;
       
       s_dir->entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
-                                            (DBusFreeFunction)bus_activation_entry_unref);
+                                             (DBusFreeFunction)bus_activation_entry_unref);
 
       if (!s_dir->entries)
-       {
-         bus_service_directory_unref (s_dir);
-         BUS_SET_OOM (error);
-         goto failed;
-       }
+        {
+          bus_service_directory_unref (s_dir);
+          BUS_SET_OOM (error);
+          goto failed;
+        }
 
       if (!_dbus_hash_table_insert_string (activation->directories, s_dir->dir_c, s_dir))
-       {
-         bus_service_directory_unref (s_dir);
-         BUS_SET_OOM (error);
-         goto failed;
-       }
+        {
+          bus_service_directory_unref (s_dir);
+          BUS_SET_OOM (error);
+          goto failed;
+        }
 
       if (!update_directory (activation, s_dir, error))
-       goto failed;
+        goto failed;
       
       link = _dbus_list_get_next_link (directories, link);
     }
@@ -884,9 +885,9 @@ add_restore_pending_to_transaction (BusTransaction       *transaction,
 
 dbus_bool_t
 bus_activation_service_created (BusActivation  *activation,
-                               const char     *service_name,
+                                const char     *service_name,
                                 BusTransaction *transaction,
-                               DBusError      *error)
+                                DBusError      *error)
 {
   BusPendingActivation *pending_activation;
   DBusMessage *message;
@@ -907,40 +908,40 @@ bus_activation_service_created (BusActivation  *activation,
       DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
       
       if (dbus_connection_get_is_connected (entry->connection))
-       {
-         /* Only send activation replies to regular activation requests. */
-         if (!entry->auto_activation)
-           {
+        {
+          /* Only send activation replies to regular activation requests. */
+          if (!entry->auto_activation)
+            {
               dbus_uint32_t result;
               
-             message = dbus_message_new_method_return (entry->activation_message);
-             if (!message)
-               {
-                 BUS_SET_OOM (error);
-                 goto error;
-               }
+              message = dbus_message_new_method_return (entry->activation_message);
+              if (!message)
+                {
+                  BUS_SET_OOM (error);
+                  goto error;
+                }
 
               result = DBUS_START_REPLY_SUCCESS;
               
-             if (!dbus_message_append_args (message,
-                                            DBUS_TYPE_UINT32, &result,
-                                            DBUS_TYPE_INVALID))
-               {
-                 dbus_message_unref (message);
-                 BUS_SET_OOM (error);
-                 goto error;
-               }
-             
-             if (!bus_transaction_send_from_driver (transaction, entry->connection, message))
-               {
-                 dbus_message_unref (message);
-                 BUS_SET_OOM (error);
-                 goto error;
-               }
-             
-             dbus_message_unref (message);
-           }
-       }
+              if (!dbus_message_append_args (message,
+                                             DBUS_TYPE_UINT32, &result,
+                                             DBUS_TYPE_INVALID))
+                {
+                  dbus_message_unref (message);
+                  BUS_SET_OOM (error);
+                  goto error;
+                }
+              
+              if (!bus_transaction_send_from_driver (transaction, entry->connection, message))
+                {
+                  dbus_message_unref (message);
+                  BUS_SET_OOM (error);
+                  goto error;
+                }
+              
+              dbus_message_unref (message);
+            }
+        }
       
       link = next;
     }
@@ -953,9 +954,9 @@ bus_activation_service_created (BusActivation  *activation,
 
 dbus_bool_t
 bus_activation_send_pending_auto_activation_messages (BusActivation  *activation,
-                                                     BusService     *service,
-                                                     BusTransaction *transaction,
-                                                     DBusError      *error)
+                                                      BusService     *service,
+                                                      BusTransaction *transaction,
+                                                      DBusError      *error)
 {
   BusPendingActivation *pending_activation;
   DBusList *link;
@@ -964,7 +965,7 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
   
   /* Check if it's a pending activation */
   pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations,
-                                                      bus_service_get_name (service));
+                                                       bus_service_get_name (service));
 
   if (!pending_activation)
     return TRUE;
@@ -976,27 +977,27 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
       DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
 
       if (entry->auto_activation && dbus_connection_get_is_connected (entry->connection))
-       {
-         DBusConnection *addressed_recipient;
-         
-         addressed_recipient = bus_service_get_primary_owner (service);
-
-         /* Check the security policy, which has the side-effect of adding an
-          * expected pending reply.
-          */
+        {
+          DBusConnection *addressed_recipient;
+          
+          addressed_recipient = bus_service_get_primary_owner (service);
+
+          /* Check the security policy, which has the side-effect of adding an
+           * expected pending reply.
+           */
           if (!bus_context_check_security_policy (activation->context, transaction,
-                                                 entry->connection,
-                                                 addressed_recipient,
-                                                 addressed_recipient,
-                                                 entry->activation_message, error))
-           goto error;
-
-         if (!bus_transaction_send (transaction, addressed_recipient, entry->activation_message))
-           {
-             BUS_SET_OOM (error);
-             goto error;
-           }
-       }
+                                                  entry->connection,
+                                                  addressed_recipient,
+                                                  addressed_recipient,
+                                                  entry->activation_message, error))
+            goto error;
+
+          if (!bus_transaction_send (transaction, addressed_recipient, entry->activation_message))
+            {
+              BUS_SET_OOM (error);
+              goto error;
+            }
+        }
 
       link = next;
     }
@@ -1043,13 +1044,13 @@ try_send_activation_failure (BusPendingActivation *pending_activation,
       DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
       
       if (dbus_connection_get_is_connected (entry->connection))
-       {
+        {
           if (!bus_transaction_send_error_reply (transaction,
                                                  entry->connection,
                                                  how,
                                                  entry->activation_message))
             goto error;
-       }
+        }
       
       link = next;
     }
@@ -1116,14 +1117,14 @@ babysitter_watch_callback (DBusWatch     *watch,
 
       /* Destroy all pending activations with the same exec */
       _dbus_hash_iter_init (pending_activation->activation->pending_activations,
-                           &iter);
+                            &iter);
       while (_dbus_hash_iter_next (&iter))
-       {
-         BusPendingActivation *p = _dbus_hash_iter_get_value (&iter);
-        
-         if (p != pending_activation && strcmp (p->exec, pending_activation->exec) == 0)
-           pending_activation_failed (p, &error);
-       }
+        {
+          BusPendingActivation *p = _dbus_hash_iter_get_value (&iter);
+         
+          if (p != pending_activation && strcmp (p->exec, pending_activation->exec) == 0)
+            pending_activation_failed (p, &error);
+        }
       
       /* Destroys the pending activation */
       pending_activation_failed (pending_activation, &error);
@@ -1237,16 +1238,16 @@ update_service_cache (BusActivation *activation, DBusError *error)
 
       dbus_error_init (&tmp_error);
       if (!update_directory (activation, s_dir, &tmp_error))
-       {
-         if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
-           {
-             dbus_move_error (&tmp_error, error);
-             return FALSE;
-           }
-
-         dbus_error_free (&tmp_error);
-         continue;
-       }
+        {
+          if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+            {
+              dbus_move_error (&tmp_error, error);
+              return FALSE;
+            }
+
+          dbus_error_free (&tmp_error);
+          continue;
+        }
     }
   
   return TRUE;
@@ -1254,8 +1255,8 @@ update_service_cache (BusActivation *activation, DBusError *error)
 
 static BusActivationEntry *
 activation_find_entry (BusActivation *activation, 
-                      const char    *service_name,
-                      DBusError     *error)
+                       const char    *service_name,
+                       DBusError     *error)
 {
   BusActivationEntry *entry;
   
@@ -1263,17 +1264,17 @@ activation_find_entry (BusActivation *activation,
   if (!entry)
     { 
       if (!update_service_cache (activation, error)) 
-       return NULL;
+        return NULL;
 
       entry = _dbus_hash_table_lookup_string (activation->entries,
-                                             service_name);
+                                              service_name);
     }
   else 
     {
       BusActivationEntry *updated_entry;
 
       if (!check_service_file (activation, entry, &updated_entry, error)) 
-       return NULL;
+        return NULL;
 
       entry = updated_entry;
     }
@@ -1281,8 +1282,8 @@ activation_find_entry (BusActivation *activation,
   if (!entry) 
     {
       dbus_set_error (error, DBUS_ERROR_SERVICE_UNKNOWN,
-                     "The name %s was not provided by any .service files",
-                     service_name);
+                      "The name %s was not provided by any .service files",
+                      service_name);
       return NULL;
     }
 
@@ -1291,19 +1292,20 @@ activation_find_entry (BusActivation *activation,
 
 dbus_bool_t
 bus_activation_activate_service (BusActivation  *activation,
-                                DBusConnection *connection,
+                                 DBusConnection *connection,
                                  BusTransaction *transaction,
-                                dbus_bool_t     auto_activation,
-                                DBusMessage    *activation_message,
+                                 dbus_bool_t     auto_activation,
+                                 DBusMessage    *activation_message,
                                  const char     *service_name,
-                                DBusError      *error)
+                                 DBusError      *error)
 {
   BusActivationEntry *entry;
   BusPendingActivation *pending_activation;
   BusPendingActivationEntry *pending_activation_entry;
   DBusMessage *message;
   DBusString service_str;
-  char *argv[2];
+  char **argv;
+  int argc;
   dbus_bool_t retval;
   DBusHashIter iter;
   dbus_bool_t activated;
@@ -1316,8 +1318,8 @@ bus_activation_activate_service (BusActivation  *activation,
       bus_context_get_max_pending_activations (activation->context))
     {
       dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
-                     "The maximum number of pending activations has been reached, activation of %s failed",
-                     service_name);
+                      "The maximum number of pending activations has been reached, activation of %s failed",
+                      service_name);
       return FALSE;
     }
 
@@ -1370,7 +1372,7 @@ bus_activation_activate_service (BusActivation  *activation,
           return retval;
         }
     }
-
+  
   pending_activation_entry = dbus_new0 (BusPendingActivationEntry, 1);
   if (!pending_activation_entry)
     {
@@ -1391,13 +1393,13 @@ bus_activation_activate_service (BusActivation  *activation,
   if (pending_activation)
     {
       if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
-       {
+        {
           _dbus_verbose ("Failed to append a new entry to pending activation\n");
           
-         BUS_SET_OOM (error);
-         bus_pending_activation_entry_free (pending_activation_entry);
-         return FALSE;
-       }
+          BUS_SET_OOM (error);
+          bus_pending_activation_entry_free (pending_activation_entry);
+          return FALSE;
+        }
 
       pending_activation->n_entries += 1;
       pending_activation->activation->n_pending_activations += 1;
@@ -1406,37 +1408,37 @@ bus_activation_activate_service (BusActivation  *activation,
     {
       pending_activation = dbus_new0 (BusPendingActivation, 1);
       if (!pending_activation)
-       {
+        {
           _dbus_verbose ("Failed to create pending activation\n");
           
-         BUS_SET_OOM (error);
-         bus_pending_activation_entry_free (pending_activation_entry);   
-         return FALSE;
-       }
+          BUS_SET_OOM (error);
+          bus_pending_activation_entry_free (pending_activation_entry);          
+          return FALSE;
+        }
 
       pending_activation->activation = activation;
       pending_activation->refcount = 1;
       
       pending_activation->service_name = _dbus_strdup (service_name);
       if (!pending_activation->service_name)
-       {
+        {
           _dbus_verbose ("Failed to copy service name for pending activation\n");
           
-         BUS_SET_OOM (error);
-         bus_pending_activation_unref (pending_activation);
-         bus_pending_activation_entry_free (pending_activation_entry);   
-         return FALSE;
-       }
+          BUS_SET_OOM (error);
+          bus_pending_activation_unref (pending_activation);
+          bus_pending_activation_entry_free (pending_activation_entry);          
+          return FALSE;
+        }
 
       pending_activation->exec = _dbus_strdup (entry->exec);
       if (!pending_activation->exec)
-       {
-         _dbus_verbose ("Failed to copy service exec for pending activation\n");
-         BUS_SET_OOM (error);
-         bus_pending_activation_unref (pending_activation);
-         bus_pending_activation_entry_free (pending_activation_entry);
-         return FALSE;
-       }
+        {
+          _dbus_verbose ("Failed to copy service exec for pending activation\n");
+          BUS_SET_OOM (error);
+          bus_pending_activation_unref (pending_activation);
+          bus_pending_activation_entry_free (pending_activation_entry);
+          return FALSE;
+        }
 
       pending_activation->timeout =
         _dbus_timeout_new (bus_context_get_activation_timeout (activation->context),
@@ -1444,40 +1446,40 @@ bus_activation_activate_service (BusActivation  *activation,
                            pending_activation,
                            NULL);
       if (!pending_activation->timeout)
-       {
+        {
           _dbus_verbose ("Failed to create timeout for pending activation\n");
           
-         BUS_SET_OOM (error);
-         bus_pending_activation_unref (pending_activation);
-         bus_pending_activation_entry_free (pending_activation_entry);   
-         return FALSE;
-       }
+          BUS_SET_OOM (error);
+          bus_pending_activation_unref (pending_activation);
+          bus_pending_activation_entry_free (pending_activation_entry);
+          return FALSE;
+        }
 
       if (!_dbus_loop_add_timeout (bus_context_get_loop (activation->context),
                                    pending_activation->timeout,
                                    handle_timeout_callback,
                                    pending_activation,
                                    NULL))
-       {
+        {
           _dbus_verbose ("Failed to add timeout for pending activation\n");
           
-         BUS_SET_OOM (error);
-         bus_pending_activation_unref (pending_activation);
-         bus_pending_activation_entry_free (pending_activation_entry);   
-         return FALSE;
-       }
+          BUS_SET_OOM (error);
+          bus_pending_activation_unref (pending_activation);
+          bus_pending_activation_entry_free (pending_activation_entry);          
+          return FALSE;
+        }
 
       pending_activation->timeout_added = TRUE;
       
       if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
-       {
+        {
           _dbus_verbose ("Failed to add entry to just-created pending activation\n");
           
-         BUS_SET_OOM (error);
-         bus_pending_activation_unref (pending_activation);
-         bus_pending_activation_entry_free (pending_activation_entry);   
-         return FALSE;
-       }
+          BUS_SET_OOM (error);
+          bus_pending_activation_unref (pending_activation);
+          bus_pending_activation_entry_free (pending_activation_entry);          
+          return FALSE;
+        }
 
       pending_activation->n_entries += 1;
       pending_activation->activation->n_pending_activations += 1;
@@ -1485,26 +1487,26 @@ bus_activation_activate_service (BusActivation  *activation,
       activated = FALSE;
       _dbus_hash_iter_init (activation->pending_activations, &iter);
       while (_dbus_hash_iter_next (&iter))
-       {
-         BusPendingActivation *p = _dbus_hash_iter_get_value (&iter);
-         
-         if (strcmp (p->exec, entry->exec) == 0) 
-           {
-             activated = TRUE;
-             break;
-           }
-       }
+        {
+          BusPendingActivation *p = _dbus_hash_iter_get_value (&iter);
+          
+          if (strcmp (p->exec, entry->exec) == 0) 
+            {
+              activated = TRUE;
+              break;
+            }
+        }
      
       if (!_dbus_hash_table_insert_string (activation->pending_activations,
-                                          pending_activation->service_name,
+                                           pending_activation->service_name,
                                            pending_activation))
-       {
+        {
           _dbus_verbose ("Failed to put pending activation in hash table\n");
           
-         BUS_SET_OOM (error);
-         bus_pending_activation_unref (pending_activation);
-         return FALSE;
-       }
+          BUS_SET_OOM (error);
+          bus_pending_activation_unref (pending_activation);
+          return FALSE;
+        }
     }
   
   if (!add_cancel_pending_to_transaction (transaction, pending_activation))
@@ -1512,35 +1514,45 @@ bus_activation_activate_service (BusActivation  *activation,
       _dbus_verbose ("Failed to add pending activation cancel hook to transaction\n");
       BUS_SET_OOM (error);
       _dbus_hash_table_remove_string (activation->pending_activations,
-                                     pending_activation->service_name);
+                                      pending_activation->service_name);
+
       return FALSE;
     }
   
-  /* FIXME we need to support a full command line, not just a single
-   * argv[0]
-   */
-
   if (activated)
     return TRUE;
 
   /* Now try to spawn the process */
-  argv[0] = entry->exec;
-  argv[1] = NULL;
+  if (!_dbus_shell_parse_argv (entry->exec, &argc, &argv, error))
+    {
+      _dbus_verbose ("Failed to parse command line: %s\n", entry->exec);
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      
+      _dbus_hash_table_remove_string (activation->pending_activations,
+                                      pending_activation->service_name);
 
+      return FALSE;
+    }
+
+  _dbus_verbose ("Spawning %s ...\n", argv[0]);
   if (!_dbus_spawn_async_with_babysitter (&pending_activation->babysitter, argv,
-                                         child_setup, activation, 
-                                         error))
+                                          child_setup, activation, 
+                                          error))
     {
       _dbus_verbose ("Failed to spawn child\n");
       _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_free_string_array (argv);
+
       return FALSE;
     }
 
+  dbus_free_string_array (argv);
+
   _dbus_assert (pending_activation->babysitter != NULL);
   
   if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
                                              add_babysitter_watch,
-                                            remove_babysitter_watch,
+                                             remove_babysitter_watch,
                                              NULL,
                                              pending_activation,
                                              NULL))
@@ -1567,9 +1579,9 @@ bus_activation_activate_service (BusActivation  *activation,
 
 static dbus_bool_t
 test_create_service_file (DBusString *dir,
-                         const char *filename, 
-                         const char *name, 
-                         const char *exec)
+                          const char *filename, 
+                          const char *name, 
+                          const char *exec)
 {
   DBusString  file_name, full_path;
   FILE        *file;
@@ -1662,10 +1674,10 @@ test_remove_directory (DBusString *dir)
   while (_dbus_directory_get_next_file (iter, &filename, NULL)) 
     {
       if (!test_remove_service_file (dir, _dbus_string_get_const_data (&filename)))
-       {
-         ret_val = FALSE;
-         goto out;
-       }
+        {
+          ret_val = FALSE;
+          goto out;
+        }
     }
   _dbus_directory_close (iter);
 
@@ -1690,15 +1702,15 @@ init_service_reload_test (DBusString *dir)
   if (!_dbus_stat (dir, &stat_buf, NULL))
     {
       if (!_dbus_create_directory (dir, NULL))
-       return FALSE;
+        return FALSE;
     }
   else 
     {
       if (!test_remove_directory (dir))
-       return FALSE;
+        return FALSE;
 
       if (!_dbus_create_directory (dir, NULL))
-       return FALSE;
+        return FALSE;
     }
 
   /* Create one initial file */
@@ -1741,21 +1753,21 @@ check_func (void *data)
   if (entry == NULL)
     {
       if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) 
-       {
-         ret_val = TRUE;
-       }
+        {
+          ret_val = TRUE;
+        }
       else
-       {
-         if (d->expecting_find)
-           ret_val = FALSE;
-       }
+        {
+          if (d->expecting_find)
+            ret_val = FALSE;
+        }
       
       dbus_error_free (&error);
     }
   else 
     {
       if (!d->expecting_find)
-       ret_val = FALSE;
+        ret_val = FALSE;
     }
 
   return ret_val;
index 123a29e..02f6c69 100644 (file)
@@ -260,24 +260,24 @@ bus_dispatch (DBusConnection *connection,
 
       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\n");
-             goto out;
-           }
-         
-         goto out;
-       }
+          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\n");
+               ("Failed: %s\n", error.name);
+              goto out;
+            }
+          
+          goto out;
+        }
       else if (service == NULL)
         {
           dbus_set_error (&error,
@@ -287,7 +287,7 @@ bus_dispatch (DBusConnection *connection,
           goto out;
         }
       else
-        {          
+        {
           addressed_recipient = bus_service_get_primary_owner (service);
           _dbus_assert (addressed_recipient != NULL);
           
@@ -339,7 +339,6 @@ bus_dispatch (DBusConnection *connection,
            * the OOM error
            */
           _dbus_assert (transaction != NULL);
-          
           if (!bus_transaction_send_error_reply (transaction, connection,
                                                  &error, message))
             {
@@ -353,6 +352,7 @@ bus_dispatch (DBusConnection *connection,
                 }
             }
         }
+     
       
       dbus_error_free (&error);
     }
@@ -521,7 +521,7 @@ typedef struct
 
 static dbus_bool_t
 check_service_owner_changed_foreach (DBusConnection *connection,
-                                    void           *data)
+                                     void           *data)
 {
   CheckServiceOwnerChangedData *d = data;
   DBusMessage *message;
@@ -564,19 +564,19 @@ check_service_owner_changed_foreach (DBusConnection *connection,
                              DBUS_TYPE_INVALID);
 
       if (dbus_error_is_set (&error))
-       {
-         if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
-           {
+        {
+          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;
-           }
-       }
+            }
+          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]))
@@ -901,7 +901,7 @@ check_hello_message (BusContext     *context,
           goto out;
         }
       if (! dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
-                                   "NameAcquired"))
+                                    "NameAcquired"))
         {
           _dbus_warn ("Expecting %s, got smthg else\n",
                       "NameAcquired");
@@ -1144,7 +1144,7 @@ check_get_connection_unix_user (BusContext     *context,
       else
         {
           warn_unexpected (connection, message,
-                          "method_return for GetConnectionUnixUser");
+                           "method_return for GetConnectionUnixUser");
 
           goto out;
         }
@@ -1190,7 +1190,7 @@ check_get_connection_unix_user (BusContext     *context,
  */
 static dbus_bool_t
 check_get_connection_unix_process_id (BusContext     *context,
-                                     DBusConnection *connection)
+                                      DBusConnection *connection)
 {
   DBusMessage *message;
   dbus_uint32_t serial;
@@ -1281,7 +1281,7 @@ check_get_connection_unix_process_id (BusContext     *context,
       else
         {
           warn_unexpected (connection, message,
-                          "method_return for GetConnectionUnixProcessID");
+                           "method_return for GetConnectionUnixProcessID");
 
           goto out;
         }
@@ -1307,20 +1307,20 @@ check_get_connection_unix_process_id (BusContext     *context,
             }
         } 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 ())
-           {
+          /* 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;
-           }
-       }
+            }
+        }
     }
 
   if (!check_no_leftovers (context))
@@ -1753,7 +1753,7 @@ check_base_service_activated (BusContext     *context,
 
       if (!dbus_message_get_args (message, &error,
                                   DBUS_TYPE_STRING, &base_service,
-                                 DBUS_TYPE_STRING, &old_owner,
+                                  DBUS_TYPE_STRING, &old_owner,
                                   DBUS_TYPE_STRING, &base_service_from_bus,
                                   DBUS_TYPE_INVALID))
         {
@@ -1780,7 +1780,7 @@ check_base_service_activated (BusContext     *context,
         }
          
       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;
@@ -1856,7 +1856,7 @@ check_service_activated (BusContext     *context,
 
       if (!dbus_message_get_args (message, &error,
                                   DBUS_TYPE_STRING, &service_name,
-                                 DBUS_TYPE_STRING, &old_owner,
+                                   DBUS_TYPE_STRING, &old_owner,
                                   DBUS_TYPE_STRING, &base_service_from_bus,
                                   DBUS_TYPE_INVALID))
         {
@@ -1983,10 +1983,10 @@ check_service_activated (BusContext     *context,
 
 static dbus_bool_t
 check_service_auto_activated (BusContext     *context,
-                             DBusConnection *connection,
-                             const char     *activated_name,
-                             const char     *base_service_name,
-                             DBusMessage    *initial_message)
+                              DBusConnection *connection,
+                              const char     *activated_name,
+                              const char     *base_service_name,
+                              DBusMessage    *initial_message)
 {
   DBusMessage *message;
   dbus_bool_t retval;
@@ -2039,7 +2039,7 @@ check_service_auto_activated (BusContext     *context,
       socd.failed = FALSE;
       socd.skip_connection = connection; 
       bus_test_clients_foreach (check_service_owner_changed_foreach,
-                               &socd);
+                                &socd);
       
       if (socd.failed)
         goto out;
@@ -2513,13 +2513,13 @@ check_existent_service_no_auto_start (BusContext     *context,
       message = NULL;
 
       switch (message_kind)
-       {
-       case GOT_SOMETHING_ELSE:
+        {
+        case GOT_SOMETHING_ELSE:
           _dbus_warn ("Unexpected message after ActivateService "
                       "(should be an error or a service announcement");
-         goto out;
+          goto out;
 
-       case GOT_ERROR:
+        case GOT_ERROR:
           if (!check_got_error (context, connection,
                                 DBUS_ERROR_SPAWN_CHILD_EXITED,
                                 DBUS_ERROR_NO_MEMORY,
@@ -2529,50 +2529,50 @@ check_existent_service_no_auto_start (BusContext     *context,
            * 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");
+          /* 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);
+                /* and process everything again */
+                bus_test_run_everything (context);
               
-               if (!check_got_error (context, connection,
-                                     DBUS_ERROR_SPAWN_CHILD_EXITED,
-                                     NULL))
-                 goto out;
-             }
-           break;
-         }
-
-       case GOT_SERVICE_CREATED:
+                if (!check_got_error (context, connection,
+                                      DBUS_ERROR_SPAWN_CHILD_EXITED,
+                                      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");
+                          "should have been a NameOwnerChanged (creation)\n");
               goto out;
             }
           
@@ -2593,8 +2593,8 @@ check_existent_service_no_auto_start (BusContext     *context,
                                            EXISTENT_SERVICE_NAME, base_service))
             goto out;
 
-         break;
-       }
+          break;
+        }
     }
 
   retval = TRUE;
@@ -2887,7 +2887,287 @@ check_existent_service_auto_start (BusContext     *context,
 
       if (!check_base_service_activated (context, connection,
                                          message, &base_service))
-       goto out;
+        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)");
+          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_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 SHELL_FAIL_SERVICE_NAME "org.freedesktop.DBus.TestSuiteShellEchoServiceFail"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_shell_fail_service_auto_start (BusContext     *context,
+                                     DBusConnection *connection)
+{
+  DBusMessage *message;
+  dbus_uint32_t serial;
+  dbus_bool_t retval;
+
+  message = dbus_message_new_method_call (SHELL_FAIL_SERVICE_NAME,
+                                          "/org/freedesktop/TestSuite",
+                                          "org.freedesktop.TestSuite",
+                                          "Echo");
+  
+  if (message == NULL)
+    return TRUE;
+  
+  if (!dbus_connection_send (connection, message, &serial))
+    {
+      dbus_message_unref (message);
+      return TRUE;
+    }
+
+  dbus_message_unref (message);
+  message = NULL;
+
+  bus_test_run_everything (context);
+  block_connection_until_message_from_bus (context, connection, "reply to shell Echo on service which should fail to auto-start");
+  bus_test_run_everything (context);
+
+  if (!dbus_connection_get_is_connected (connection))
+    {
+      _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+      return TRUE;
+    }
+  
+  retval = FALSE;
+  
+  message = pop_message_waiting_for_memory (connection);
+  if (message == NULL)
+    {
+      _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+                  "Echo message (auto activation)", serial, connection);
+      goto out;
+    }
+
+  verbose_message_received (connection, message);
+
+  if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+    {
+      if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+        {
+          _dbus_warn ("Message has wrong sender %s\n",
+                      dbus_message_get_sender (message) ?
+                      dbus_message_get_sender (message) : "(none)");
+          goto out;
+        }
+      
+      if (dbus_message_is_error (message,
+                                 DBUS_ERROR_NO_MEMORY))
+        {
+          ; /* good, this is a valid response */
+        }
+      else if (dbus_message_is_error (message,
+                                      DBUS_ERROR_INVALID_ARGS))
+        {
+          _dbus_verbose("got invalid args\n");
+          ; /* good, this is expected also */
+        }
+      else
+        {
+          warn_unexpected (connection, message, "not this error");
+
+          goto out;
+        }
+    }
+  else
+    {
+      _dbus_warn ("Did not expect to successfully auto-start shell fail service\n");
+      goto out;
+    }
+
+  retval = TRUE;
+  
+ out:
+  if (message)
+    dbus_message_unref (message);
+  
+  return retval;
+}
+
+#define SHELL_SUCCESS_SERVICE_NAME "org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_shell_service_success_auto_start (BusContext     *context,
+                                        DBusConnection *connection)
+{
+  DBusMessage *message;
+  DBusMessage *base_service_message;
+  dbus_uint32_t serial;
+  dbus_bool_t retval;
+  const char *base_service;
+  const char *argv[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+  base_service_message = NULL;
+
+  message = dbus_message_new_method_call (SHELL_SUCCESS_SERVICE_NAME,
+                                          "/org/freedesktop/TestSuite",
+                                          "org.freedesktop.TestSuite",
+                                          "Echo");
+  
+  if (message == NULL)
+    return TRUE;
+
+  if (!dbus_connection_send (connection, message, &serial))
+    {
+      dbus_message_unref (message);
+      return TRUE;
+    }
+
+  dbus_message_unref (message);
+  message = NULL;
+
+  bus_test_run_everything (context);
+
+  /* now wait for the message bus to hear back from the activated
+   * service.
+   */
+  block_connection_until_message_from_bus (context, connection, "reply to Echo on shell success service");
+  bus_test_run_everything (context);
+
+  if (!dbus_connection_get_is_connected (connection))
+    {
+      _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+      return TRUE;
+    }
+
+  retval = FALSE;
+  
+  message = pop_message_waiting_for_memory (connection);
+  if (message == NULL)
+    {
+      _dbus_warn ("Did not receive any messages after auto start %d on %p\n",
+                  serial, connection);
+      goto out;
+    }
+
+  verbose_message_received (connection, message);
+  _dbus_verbose ("  (after sending %s)\n", "auto start");
+
+  /* we should get zero or two ServiceOwnerChanged signals */
+  if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL)
+    {
+      GotServiceInfo message_kind;
+
+      if (!check_base_service_activated (context, connection,
+                                         message, &base_service))
+        goto out;
 
       base_service_message = message;
       message = NULL;
@@ -2901,10 +3181,10 @@ check_existent_service_auto_start (BusContext     *context,
       message = dbus_connection_borrow_message (connection);
       if (message == NULL)
         {
-         _dbus_warn ("No message after auto activation "
-                     "(should be a service announcement)");
-         dbus_connection_return_message (connection, message);
-         message = NULL;
+          _dbus_warn ("No message after auto activation "
+                      "(should be a service announcement)");
+          dbus_connection_return_message (connection, message);
+          message = NULL;
           goto out;
         }
 
@@ -2914,51 +3194,51 @@ check_existent_service_auto_start (BusContext     *context,
       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;
+        {
+        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;
           
-           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_message_unref (message);
+          message = NULL;
+
+          break;
 
-           if (socd.failed)
-             goto out;
+        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;
-         }
+            break;
+          }
 
         case GOT_ERROR:
-       case GOT_SOMETHING_ELSE:
-         _dbus_warn ("Unexpected message after auto activation\n");
-         goto out;
-       }
+        case GOT_SOMETHING_ELSE:
+          _dbus_warn ("Unexpected message after auto activation\n");
+          goto out;
+        }
     }
 
   /* OK, now we've dealt with ServiceOwnerChanged signals, now should
@@ -2983,12 +3263,71 @@ check_existent_service_auto_start (BusContext     *context,
       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");
+      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)", 
+                  "-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)", 
+                   "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)", 
+                   "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)", 
+                   "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)", 
+                   "--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)", 
+                   "we put in", argv[6]);
+      goto out;
+    } 
+
   dbus_message_unref (message);
   message = NULL;
       
   if (!check_send_exit_to_service (context, connection,
-                                  EXISTENT_SERVICE_NAME,
-                                  base_service))
+                                   SHELL_SUCCESS_SERVICE_NAME,
+                                   base_service))
     goto out;
   
   retval = TRUE;
@@ -3174,10 +3513,13 @@ bus_dispatch_test (const DBusString *test_data_dir)
                          check_existent_service_no_auto_start);
   
   check2_try_iterations (context, foo, "nonexistent_service_auto_start",
-                        check_nonexistent_service_auto_start);
+                         check_nonexistent_service_auto_start);
   
   check2_try_iterations (context, foo, "segfault_service_auto_start",
-                        check_segfault_service_auto_start);
+                         check_segfault_service_auto_start);
+
+  check2_try_iterations (context, foo, "shell_fail_service_auto_start",
+                         check_shell_fail_service_auto_start);
 
 #if 0
   /* Note: need to resolve some issues with the testing code in order to run
@@ -3185,12 +3527,15 @@ bus_dispatch_test (const DBusString *test_data_dir)
    * when oom happens, without blocking the test).
    */
   check2_try_iterations (context, foo, "existent_service_auto_auto_start",
-                        check_existent_service_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);
index 0266d00..66fae85 100644 (file)
@@ -1190,6 +1190,7 @@ AC_SUBST(TEST_$1)
 
 TEST_PATH(SERVICE_DIR, data/valid-service-files)
 TEST_PATH(SERVICE_BINARY, test-service)
+TEST_PATH(SHELL_SERVICE_BINARY, test-shell-service)
 TEST_PATH(GLIB_SERVICE_BINARY, glib/test-service-glib)
 TEST_PATH(EXIT_BINARY, test-exit)
 TEST_PATH(SEGFAULT_BINARY, test-segfault)
@@ -1298,6 +1299,8 @@ test/data/valid-config-files/debug-allow-all-sha1.conf
 test/data/valid-service-files/debug-echo.service
 test/data/valid-service-files/debug-segfault.service
 test/data/valid-service-files/debug-glib.service
+test/data/valid-service-files/debug-shell-echo-success.service
+test/data/valid-service-files/debug-shell-echo-fail.service
 ])
 
 ### FIXME it's bizarre that have_qt and have_glib are used
index 879a34c..f4175c7 100644 (file)
@@ -136,6 +136,8 @@ DBUS_UTIL_SOURCES=                          \
        dbus-message-factory.c                  \
        dbus-message-factory.h                  \
        dbus-message-util.c                     \
+       dbus-shell.c                            \
+       dbus-shell.h                            \
        dbus-spawn.c                            \
        dbus-spawn.h                            \
        dbus-string-util.c                      \
diff --git a/dbus/dbus-shell.c b/dbus/dbus-shell.c
new file mode 100644 (file)
index 0000000..02d8cc8
--- /dev/null
@@ -0,0 +1,706 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-shell.c Shell command line utility functions.
+ *
+ * Copyright (C) 2002, 2003  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <string.h>
+#include "dbus-internals.h"
+#include "dbus-list.h"
+#include "dbus-memory.h"
+#include "dbus-protocol.h"
+#include "dbus-shell.h"
+#include "dbus-string.h"
+
+/* Single quotes preserve the literal string exactly. escape
+ * sequences are not allowed; not even \' - if you want a '
+ * in the quoted text, you have to do something like 'foo'\''bar'
+ *
+ * Double quotes allow $ ` " \ and newline to be escaped with backslash.
+ * Otherwise double quotes preserve things literally.
+ */
+
+static dbus_bool_t
+unquote_string_inplace (char* str, char** end)
+{
+  char* dest;
+  char* s;
+  char quote_char;
+  
+  dest = s = str;
+
+  quote_char = *s;
+  
+  if (!(*s == '"' || *s == '\''))
+    {
+      *end = str;
+      return FALSE;
+    }
+
+  /* Skip the initial quote mark */
+  ++s;
+
+  if (quote_char == '"')
+    {
+      while (*s)
+        {
+          _dbus_assert(s > dest); /* loop invariant */
+      
+          switch (*s)
+            {
+            case '"':
+              /* End of the string, return now */
+              *dest = '\0';
+              ++s;
+              *end = s;
+              return TRUE;
+
+            case '\\':
+              /* Possible escaped quote or \ */
+              ++s;
+              switch (*s)
+                {
+                case '"':
+                case '\\':
+                case '`':
+                case '$':
+                case '\n':
+                  *dest = *s;
+                  ++s;
+                  ++dest;
+                  break;
+
+                default:
+                  /* not an escaped char */
+                  *dest = '\\';
+                  ++dest;
+                  /* ++s already done. */
+                  break;
+                }
+              break;
+
+            default:
+              *dest = *s;
+              ++dest;
+              ++s;
+              break;
+            }
+
+          _dbus_assert(s > dest); /* loop invariant */
+        }
+    }
+  else
+    {
+      while (*s)
+        {
+          _dbus_assert(s > dest); /* loop invariant */
+          
+          if (*s == '\'')
+            {
+              /* End of the string, return now */
+              *dest = '\0';
+              ++s;
+              *end = s;
+              return TRUE;
+            }
+          else
+            {
+              *dest = *s;
+              ++dest;
+              ++s;
+            }
+
+          _dbus_assert(s > dest); /* loop invariant */
+        }
+    }
+  
+  /* If we reach here this means the close quote was never encountered */
+
+  *dest = '\0';
+  
+  *end = s;
+  return FALSE;
+}
+
+/**
+ * Quotes a string so that the shell (/bin/sh) will interpret the
+ * quoted string to mean @unquoted_string. If you pass a filename to
+ * the shell, for example, you should first quote it with this
+ * function.  The return value must be freed with dbus_free(). The
+ * quoting style used is undefined (single or double quotes may be
+ * used).
+ * 
+ * @unquoted_string: a literal string
+ **/
+char*
+_dbus_shell_quote (const char *unquoted_string)
+{
+  /* We always use single quotes, because the algorithm is cheesier.
+   * We could use double if we felt like it, that might be more
+   * human-readable.
+   */
+
+  const char *p;
+  char *ret;
+  DBusString dest;
+  
+  _dbus_string_init (&dest);
+
+  p = unquoted_string;
+
+  /* could speed this up a lot by appending chunks of text at a
+   * time.
+   */
+  while (*p)
+    {
+      /* Replace literal ' with a close ', a \', and a open ' */
+      if (*p == '\'')
+       {
+         if (!_dbus_string_append (&dest, "'\\''"))
+           {
+             _dbus_string_free (&dest);
+             return NULL;
+           }
+       }
+      else
+       {
+         if (!_dbus_string_append_byte (&dest, *p))
+           {
+             _dbus_string_free (&dest);
+             return NULL;
+           }
+       }
+
+      ++p;
+    }
+
+  /* close the quote */
+  if (_dbus_string_append_byte (&dest, '\''))
+    {
+      ret = _dbus_strdup (_dbus_string_get_data (&dest));
+      _dbus_string_free (&dest);
+
+      return ret;
+    }
+
+  _dbus_string_free (&dest);
+
+  return NULL;
+}
+
+/** 
+ * Unquotes a string as the shell (/bin/sh) would. Only handles
+ * quotes; if a string contains file globs, arithmetic operators,
+ * variables, backticks, redirections, or other special-to-the-shell
+ * features, the result will be different from the result a real shell
+ * would produce (the variables, backticks, etc. will be passed
+ * through literally instead of being expanded). This function is
+ * guaranteed to succeed if applied to the result of
+ * _dbus_shell_quote(). If it fails, it returns %NULL.
+ * The @quoted_string need not actually contain quoted or
+ * escaped text; _dbus_shell_unquote() simply goes through the string and
+ * unquotes/unescapes anything that the shell would. Both single and
+ * double quotes are handled, as are escapes including escaped
+ * newlines. The return value must be freed with dbus_free().
+ * 
+ * Shell quoting rules are a bit strange. Single quotes preserve the
+ * literal string exactly. escape sequences are not allowed; not even
+ * \' - if you want a ' in the quoted text, you have to do something
+ * like 'foo'\''bar'.  Double quotes allow $, `, ", \, and newline to
+ * be escaped with backslash. Otherwise double quotes preserve things
+ * literally.
+ *
+ * @quoted_string: shell-quoted string
+ **/
+char*
+_dbus_shell_unquote (const char *quoted_string)
+{
+  char *unquoted;
+  char *end;
+  char *start;
+  char *ret;
+  DBusString retval;
+
+  unquoted = _dbus_strdup (quoted_string);
+  if (unquoted == NULL)
+    return NULL;
+
+  start = unquoted;
+  end = unquoted;
+  if (!_dbus_string_init (&retval))
+    {
+      dbus_free (unquoted);
+      return NULL;
+    }
+
+  /* The loop allows cases such as
+   * "foo"blah blah'bar'woo foo"baz"la la la\'\''foo'
+   */
+  while (*start)
+    {
+      /* Append all non-quoted chars, honoring backslash escape
+       */
+      
+      while (*start && !(*start == '"' || *start == '\''))
+        {
+          if (*start == '\\')
+            {
+              /* all characters can get escaped by backslash,
+               * except newline, which is removed if it follows
+               * a backslash outside of quotes
+               */
+              
+              ++start;
+              if (*start)
+                {
+                  if (*start != '\n')
+                   {
+                     if (!_dbus_string_append_byte (&retval, *start))
+                       goto error;
+                   }
+                  ++start;
+                }
+            }
+          else
+            {
+              if (!_dbus_string_append_byte (&retval, *start))
+               goto error;
+              ++start;
+            }
+        }
+
+      if (*start)
+        {
+          if (!unquote_string_inplace (start, &end))
+           goto error;
+          else
+            {
+              if (!_dbus_string_append (&retval, start))
+               goto error;
+              start = end;
+            }
+        }
+    }
+
+  ret = _dbus_strdup (_dbus_string_get_data (&retval));
+  if (!ret)
+    goto error;
+
+  dbus_free (unquoted);
+  _dbus_string_free (&retval);
+  
+  return ret;
+  
+ error:
+  dbus_free (unquoted);
+  _dbus_string_free (&retval);
+  return NULL;
+}
+
+/* _dbus_shell_parse_argv() does a semi-arbitrary weird subset of the way
+ * the shell parses a command line. We don't do variable expansion,
+ * don't understand that operators are tokens, don't do tilde expansion,
+ * don't do command substitution, no arithmetic expansion, IFS gets ignored,
+ * don't do filename globs, don't remove redirection stuff, etc.
+ *
+ * READ THE UNIX98 SPEC on "Shell Command Language" before changing
+ * the behavior of this code.
+ *
+ * Steps to parsing the argv string:
+ *
+ *  - tokenize the string (but since we ignore operators,
+ *    our tokenization may diverge from what the shell would do)
+ *    note that tokenization ignores the internals of a quoted
+ *    word and it always splits on spaces, not on IFS even
+ *    if we used IFS. We also ignore "end of input indicator"
+ *    (I guess this is control-D?)
+ *
+ *    Tokenization steps, from UNIX98 with operator stuff removed,
+ *    are:
+ * 
+ *    1) "If the current character is backslash, single-quote or
+ *        double-quote (\, ' or ") and it is not quoted, it will affect
+ *        quoting for subsequent characters up to the end of the quoted
+ *        text. The rules for quoting are as described in Quoting
+ *        . During token recognition no substitutions will be actually
+ *        performed, and the result token will contain exactly the
+ *        characters that appear in the input (except for newline
+ *        character joining), unmodified, including any embedded or
+ *        enclosing quotes or substitution operators, between the quote
+ *        mark and the end of the quoted text. The token will not be
+ *        delimited by the end of the quoted field."
+ *
+ *    2) "If the current character is an unquoted newline character,
+ *        the current token will be delimited."
+ *
+ *    3) "If the current character is an unquoted blank character, any
+ *        token containing the previous character is delimited and the
+ *        current character will be discarded."
+ *
+ *    4) "If the previous character was part of a word, the current
+ *        character will be appended to that word."
+ *
+ *    5) "If the current character is a "#", it and all subsequent
+ *        characters up to, but excluding, the next newline character
+ *        will be discarded as a comment. The newline character that
+ *        ends the line is not considered part of the comment. The
+ *        "#" starts a comment only when it is at the beginning of a
+ *        token. Since the search for the end-of-comment does not
+ *        consider an escaped newline character specially, a comment
+ *        cannot be continued to the next line."
+ *
+ *    6) "The current character will be used as the start of a new word."
+ *
+ *
+ *  - for each token (word), perform portions of word expansion, namely
+ *    field splitting (using default whitespace IFS) and quote
+ *    removal.  Field splitting may increase the number of words.
+ *    Quote removal does not increase the number of words.
+ *
+ *   "If the complete expansion appropriate for a word results in an
+ *   empty field, that empty field will be deleted from the list of
+ *   fields that form the completely expanded command, unless the
+ *   original word contained single-quote or double-quote characters."
+ *    - UNIX98 spec
+ *
+ *
+ */
+
+static dbus_bool_t
+delimit_token (DBusString *token,
+               DBusList **retval,
+              DBusError *error)
+{
+  char *str;
+
+  str = _dbus_strdup (_dbus_string_get_data (token));
+  if (!str)
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  if (!_dbus_list_append (retval, str))
+    {
+      dbus_free (str);
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static DBusList*
+tokenize_command_line (const char *command_line, DBusError *error)
+{
+  char current_quote;
+  const char *p;
+  DBusString current_token;
+  DBusList *retval = NULL;
+  dbus_bool_t quoted;;
+
+  current_quote = '\0';
+  quoted = FALSE;
+  p = command_line;
+
+  if (!_dbus_string_init (&current_token))
+    {
+      _DBUS_SET_OOM (error);
+      return NULL;
+    }
+
+  while (*p)
+    {
+      if (current_quote == '\\')
+        {
+          if (*p == '\n')
+            {
+              /* we append nothing; backslash-newline become nothing */
+            }
+          else
+            {
+             if (!_dbus_string_append_byte (&current_token, '\\') || 
+                 !_dbus_string_append_byte (&current_token, *p))
+               {
+                 _DBUS_SET_OOM (error);
+                 goto error;
+               }
+            }
+
+          current_quote = '\0';
+        }
+      else if (current_quote == '#')
+        {
+          /* Discard up to and including next newline */
+          while (*p && *p != '\n')
+            ++p;
+
+          current_quote = '\0';
+          
+          if (*p == '\0')
+            break;
+        }
+      else if (current_quote)
+        {
+          if (*p == current_quote &&
+              /* check that it isn't an escaped double quote */
+              !(current_quote == '"' && quoted))
+            {
+              /* close the quote */
+              current_quote = '\0';
+            }
+
+          /* Everything inside quotes, and the close quote,
+           * gets appended literally.
+           */
+
+          if (!_dbus_string_append_byte (&current_token, *p))
+           {
+             _DBUS_SET_OOM (error);
+             goto error;
+           }
+        }
+      else
+        {
+          switch (*p)
+            {
+            case '\n':
+              if (!delimit_token (&current_token, &retval, error))
+               goto error;
+
+               _dbus_string_free (&current_token);
+
+               if (!_dbus_string_init (&current_token))
+                 {
+                   _DBUS_SET_OOM (error);
+                   goto init_error;
+                 }
+
+              break;
+
+            case ' ':
+            case '\t':
+              /* If the current token contains the previous char, delimit
+               * the current token. A nonzero length
+               * token should always contain the previous char.
+               */
+              if (_dbus_string_get_length (&current_token) > 0)
+                {
+                  if (!delimit_token (&current_token, &retval, error))
+                   goto error;
+
+                 _dbus_string_free (&current_token);
+
+                 if (!_dbus_string_init (&current_token))
+                   {
+                     _DBUS_SET_OOM (error);
+                     goto init_error;
+                   }
+
+                }
+              
+              /* discard all unquoted blanks (don't add them to a token) */
+              break;
+
+
+              /* single/double quotes are appended to the token,
+               * escapes are maybe appended next time through the loop,
+               * comment chars are never appended.
+               */
+              
+            case '\'':
+            case '"':
+              if (!_dbus_string_append_byte (&current_token, *p))
+               {
+                 _DBUS_SET_OOM (error);
+                 goto error;
+               }
+
+              /* FALL THRU */
+              
+            case '#':
+            case '\\':
+              current_quote = *p;
+              break;
+
+            default:
+              /* Combines rules 4) and 6) - if we have a token, append to it,
+               * otherwise create a new token.
+               */
+              if (!_dbus_string_append_byte (&current_token, *p))
+               {
+                 _DBUS_SET_OOM (error);
+                 goto error;
+               }
+              break;
+            }
+        }
+
+      /* We need to count consecutive backslashes mod 2, 
+       * to detect escaped doublequotes.
+       */
+      if (*p != '\\')
+       quoted = FALSE;
+      else
+       quoted = !quoted;
+
+      ++p;
+    }
+
+  if (!delimit_token (&current_token, &retval, error))
+    goto error;
+
+  if (current_quote)
+    {
+      dbus_set_error_const (error, DBUS_ERROR_INVALID_ARGS, "Unclosed quotes in command line");
+      goto error;
+    }
+
+  if (retval == NULL)
+    {
+      dbus_set_error_const (error, DBUS_ERROR_INVALID_ARGS, "No tokens found in command line");
+      goto error;
+    }
+  _dbus_string_free (&current_token);
+  return retval;
+
+ error:
+  _dbus_string_free (&current_token);
+
+ init_error:
+  if (retval)
+    {
+      _dbus_list_foreach (&retval, (DBusForeachFunction) dbus_free, NULL);
+      _dbus_list_clear (&retval);
+    }
+
+  return NULL;
+}
+
+/**
+ * _dbus_shell_parse_argv:
+ * 
+ * Parses a command line into an argument vector, in much the same way
+ * the shell would, but without many of the expansions the shell would
+ * perform (variable expansion, globs, operators, filename expansion,
+ * etc. are not supported). The results are defined to be the same as
+ * those you would get from a UNIX98 /bin/sh, as long as the input
+ * contains none of the unsupported shell expansions. If the input
+ * does contain such expansions, they are passed through
+ * literally. Free the returned vector with dbus_free_string_array().
+ * 
+ * @command_line: command line to parse
+ * @argcp: return location for number of args
+ * @argvp: return location for array of args
+ * @error: error information
+ **/
+dbus_bool_t
+_dbus_shell_parse_argv (const char *command_line,
+                       int        *argcp,
+                       char     ***argvp,
+                       DBusError  *error)
+{
+  /* Code based on poptParseArgvString() from libpopt */
+  int argc = 0;
+  char **argv = NULL;
+  DBusList *tokens = NULL;
+  int i;
+  DBusList *tmp_list;
+  
+  if (!command_line)
+    {
+      _dbus_verbose ("Command line is NULL\n");
+      return FALSE;
+    }
+
+  tokens = tokenize_command_line (command_line, error);
+  if (tokens == NULL)
+    {
+      _dbus_verbose ("No tokens for command line '%s'\n", command_line);
+      return FALSE;
+    }
+
+  /* Because we can't have introduced any new blank space into the
+   * tokens (we didn't do any new expansions), we don't need to
+   * perform field splitting. If we were going to honor IFS or do any
+   * expansions, we would have to do field splitting on each word
+   * here. Also, if we were going to do any expansion we would need to
+   * remove any zero-length words that didn't contain quotes
+   * originally; but since there's no expansion we know all words have
+   * nonzero length, unless they contain quotes.
+   * 
+   * So, we simply remove quotes, and don't do any field splitting or
+   * empty word removal, since we know there was no way to introduce
+   * such things.
+   */
+
+  argc = _dbus_list_get_length (&tokens);
+  argv = dbus_new (char *, argc + 1);
+  if (!argv)
+    {
+      _DBUS_SET_OOM (error);
+      goto error;
+    }
+
+  i = 0;
+  tmp_list = tokens;
+  while (tmp_list)
+    {
+      argv[i] = _dbus_shell_unquote (tmp_list->data);
+
+      if (!argv[i])
+        {
+          int j;
+         for (j = 0; j < i; j++)
+           dbus_free(argv[j]);
+
+          dbus_free (argv);
+         _DBUS_SET_OOM (error);
+         goto error;
+        }
+
+      tmp_list = _dbus_list_get_next_link (&tokens, tmp_list);
+      ++i;
+    }
+  argv[argc] = NULL;
+  
+  _dbus_list_foreach (&tokens, (DBusForeachFunction) dbus_free, NULL);
+  _dbus_list_clear (&tokens);
+  
+  if (argcp)
+    *argcp = argc;
+
+  if (argvp)
+    *argvp = argv;
+  else
+    dbus_free_string_array (argv);
+
+  return TRUE;
+
+ error:
+  _dbus_list_foreach (&tokens, (DBusForeachFunction) dbus_free, NULL);
+  _dbus_list_clear (&tokens);
+
+  return FALSE;
+
+}
diff --git a/dbus/dbus-shell.h b/dbus/dbus-shell.h
new file mode 100644 (file)
index 0000000..ceda45b
--- /dev/null
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-shell.h Shell command line utility functions.
+ *
+ * Copyright (C) 2002, 2003  Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+#ifndef DBUS_SHELL_H
+#define DBUS_SHELL_H
+
+DBUS_BEGIN_DECLS
+
+char*       _dbus_shell_quote      (const char *unquoted_string);
+char*       _dbus_shell_unquote    (const char *quoted_string);
+dbus_bool_t _dbus_shell_parse_argv (const char *command_line,
+                                    int        *argcp,
+                                    char       ***argvp,
+                                   DBusError  *error);
+
+DBUS_END_DECLS
+
+#endif /* DBUS_SHELL_H */
+
+
index 9c73bb7..cce7eea 100644 (file)
@@ -5,5 +5,6 @@ dbus_bindings.pxd
 *.lo
 *.la
 dbus_bindings.c
+dbus_glib_bindings.c
 *.pyc
 .libs
index 81a8367..7bedfd0 100644 (file)
@@ -31,12 +31,12 @@ CLEANFILES =                        \
        dbus_glib_bindings.c    
 
 
-dbus_bindings.pxd: dbus_bindings.pxd.in extract.py
-       -$(PYTHON) extract.py dbus_bindings.pxd.in -I$(top_builddir)  > dbus_bindings.pxd
+dbus_bindings.pxd: $(srcdir)/dbus_bindings.pxd.in $(srcdir)/extract.py
+       -$(PYTHON) $(srcdir)/extract.py $(srcdir)/dbus_bindings.pxd.in -I$(top_builddir)  > $@.tmp && mv $@.tmp $@
 
-dbus_bindings.c: dbus_bindings.pyx dbus_bindings.pxd 
-       -pyrexc dbus_bindings.pyx 
+dbus_bindings.c: $(srcdir)/dbus_bindings.pyx dbus_bindings.pxd 
+       -pyrexc $(srcdir)/dbus_bindings.pyx
+
+dbus_glib_bindings.c: $(srcdir)/dbus_glib_bindings.pyx dbus_bindings.pxd 
+       -pyrexc $(srcdir)/dbus_glib_bindings.pyx 
 
-dbus_glib_bindings.c: dbus_glib_bindings.pyx dbus_bindings.pxd 
-       -pyrexc dbus_glib_bindings.pyx 
-               
index 83e7bf0..20de1a2 100644 (file)
@@ -20,3 +20,5 @@ test-segfault
 test-service
 test-sleep-forever
 decode-gcov
+shell-test
+test-shell-service
index 4498b7e..27f5102 100644 (file)
@@ -11,9 +11,13 @@ INCLUDES=-I$(top_srcdir) $(DBUS_TEST_CFLAGS)
 
 if DBUS_BUILD_TESTS
 ## break-loader removed for now
-TEST_BINARIES=test-service spawn-test test-segfault test-exit test-sleep-forever
+TEST_BINARIES=test-service test-shell-service shell-test spawn-test test-segfault test-exit test-sleep-forever
+
+#enable stand alone make check test
+TESTS=shell-test
 else
 TEST_BINARIES=
+TESTS=
 endif
 
 if DBUS_GCOV_ENABLED
@@ -32,6 +36,14 @@ test_service_SOURCES=                                \
 ##break_loader_SOURCES=                                \
 ##     break-loader.c
 
+test_shell_service_SOURCES =                   \
+       test-shell-service.c                    \
+       test-utils.c                            \
+       test-utils.h
+
+shell_test_SOURCES=                             \
+        shell-test.c
+
 spawn_test_SOURCES=                            \
        spawn-test.c
 
@@ -51,6 +63,8 @@ TEST_LIBS=$(DBUS_TEST_LIBS) $(top_builddir)/dbus/libdbus-convenience.la
 
 test_service_LDADD=$(TEST_LIBS)
 ## break_loader_LDADD= $(TEST_LIBS)
+test_shell_service_LDADD=$(TEST_LIBS)
+shell_test_LDADD=$(TEST_LIBS)
 spawn_test_LDADD=$(TEST_LIBS)
 decode_gcov_LDADD=$(TEST_LIBS)
 
index c5bd07d..ca3fbc7 100644 (file)
@@ -1,4 +1,5 @@
 debug-echo.service
 debug-segfault.service
 debug-glib.service
-
+debug-shell-echo-fail.service
+debug-shell-echo-success.service
diff --git a/test/data/valid-service-files/debug-shell-echo-fail.service.in b/test/data/valid-service-files/debug-shell-echo-fail.service.in
new file mode 100644 (file)
index 0000000..971be60
--- /dev/null
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.DBus.TestSuiteShellEchoServiceFail
+Exec=@TEST_SHELL_SERVICE_BINARY@ "this should 'fail' because of an unterminated quote 
diff --git a/test/data/valid-service-files/debug-shell-echo-success.service.in b/test/data/valid-service-files/debug-shell-echo-success.service.in
new file mode 100644 (file)
index 0000000..49bf406
--- /dev/null
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess
+Exec=@TEST_SHELL_SERVICE_BINARY@ -test "that" 'we get' back --what "we put in"
diff --git a/test/shell-test.c b/test/shell-test.c
new file mode 100644 (file)
index 0000000..46a387d
--- /dev/null
@@ -0,0 +1,107 @@
+#include <stdio.h>
+#include <stdlib.h>
+#define DBUS_COMPILATION
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-memory.h>
+#include <dbus/dbus-shell.h>
+#include <dbus/dbus-string.h>
+
+static dbus_bool_t
+test_command_line (const char *arg1, ...)
+{
+  int i, original_argc, shell_argc;
+  char **shell_argv;
+  char **original_argv;
+  char *command_line, *tmp;
+  DBusString str;
+  DBusList *list = NULL, *node;
+  va_list var_args;
+  DBusError error;
+
+  va_start (var_args, arg1);
+  _dbus_list_append (&list, arg1);
+  do
+    {
+      tmp = va_arg (var_args, char *);
+      if (!tmp)
+        break;
+      _dbus_list_append (&list, tmp);
+    } while (tmp);
+  va_end (var_args);
+
+  original_argc = _dbus_list_get_length (&list);
+  original_argv = dbus_new (char *, original_argc);
+  _dbus_string_init (&str);
+  for (i = 0, node = _dbus_list_get_first_link (&list); i < original_argc && node;
+       i++, node = _dbus_list_get_next_link (&list, node))
+    {
+      original_argv[i] = node->data;
+      if (i > 0)
+        _dbus_string_append_byte (&str, ' ');
+      _dbus_string_append (&str, original_argv[i]);
+    }
+  
+  _dbus_list_clear (&list);
+  command_line = _dbus_string_get_data (&str);
+  printf ("\n\nTesting command line '%s'\n", command_line);
+
+  dbus_error_init (&error);
+  if (!_dbus_shell_parse_argv (command_line, &shell_argc, &shell_argv, &error))
+    {
+      fprintf (stderr, "Error parsing command line: %s\n", error.message ? error.message : "");
+      return FALSE;
+    }
+  else
+    {
+      if (shell_argc != original_argc)
+        {
+          printf ("Number of arguments returned (%d) don't match original (%d)\n",
+                  shell_argc, original_argc);
+          return FALSE;
+        } 
+      printf ("Number of arguments: %d\n", shell_argc);
+      for (i = 0; i < shell_argc; i++)
+        {
+          char *unquoted;
+          
+          unquoted = _dbus_shell_unquote (original_argv[i]);
+          if (strcmp (unquoted ? unquoted : "",
+                      shell_argv[i] ? shell_argv[i] : ""))
+            {
+              printf ("Position %d, returned argument (%s) does not match original (%s)\n",
+                      i, shell_argv[i], unquoted);
+              dbus_free (unquoted);
+              return FALSE;
+            }
+          dbus_free (unquoted);
+          if (shell_argv[i])
+            printf ("Argument %d = %s\n", i, shell_argv[i]);
+        }
+      
+      dbus_free_string_array (shell_argv);
+    }
+  
+  _dbus_string_free (&str);
+  
+  return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+  if (!test_command_line ("command", "-s", "--force-shutdown", "\"a string\"", "123", NULL)
+      || !test_command_line ("command", "-s", NULL)
+      || !test_command_line ("/opt/gnome/bin/service-start", NULL)
+      || !test_command_line ("grep", "-l", "-r", "-i", "'whatever'", "files*.c", NULL)
+      || !test_command_line ("/home/boston/johnp/devel-local/dbus/test/test-segfault", NULL)
+      || !test_command_line ("ls", "-l", "-a", "--colors", "/tmp", NULL)
+      || !test_command_line ("rsync-to-server", NULL)
+      || !test_command_line ("test-segfault", "--no-segfault", NULL)
+      || !test_command_line ("evolution", "mailto:pepe@cuco.com", NULL)
+      || !test_command_line ("run", "\"a \n multiline\"", NULL)
+      || test_command_line ("ls", "\"a wrong string'", NULL) /* invalid command line */ )
+    return -1;
+  
+  return 0;
+}
diff --git a/test/test-shell-service.c b/test/test-shell-service.c
new file mode 100644 (file)
index 0000000..79a4949
--- /dev/null
@@ -0,0 +1,198 @@
+
+#include "test-utils.h"
+
+static DBusLoop *loop;
+static dbus_bool_t already_quit = FALSE;
+static const char* echo_path = "/org/freedesktop/TestSuite";
+
+typedef struct
+{
+  int argc;
+  char **argv;
+} EchoData;
+
+static void
+quit (void)
+{
+  if (!already_quit)
+    {
+      _dbus_loop_quit (loop);
+      already_quit = TRUE;
+    }
+}
+
+static void
+die (const char *message)
+{
+  fprintf (stderr, "*** test-service: %s", message);
+  exit (1);
+}
+
+static DBusHandlerResult
+handle_echo (DBusConnection     *connection,
+             DBusMessage        *message)
+{
+  DBusError error;
+  DBusMessage *reply;
+  DBusMessageIter iter;
+  int i;
+  char *s;
+  EchoData *d;
+
+  _dbus_verbose ("sending reply to Echo method\n");
+
+  if (!dbus_connection_get_object_path_data (connection, echo_path, &d))
+      die ("No memory");
+
+
+  dbus_error_init (&error);
+
+  reply = dbus_message_new_method_return (message);
+  if (reply == NULL)
+    die ("No memory\n");
+  
+  dbus_message_iter_init_append (reply, &iter);
+  for (i = 0; i < d->argc; ++i)
+    if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &(d->argv[i])))
+      die ("No memory\n");
+
+  if (!dbus_connection_send (connection, reply, NULL))
+    die ("No memory\n");
+
+  fprintf (stderr, "Shell echo service echoed the command line\n");
+  
+  dbus_message_unref (reply);
+    
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void
+path_unregistered_func (DBusConnection  *connection,
+                        void            *user_data)
+{
+  /* connection was finalized */
+}
+
+static DBusHandlerResult
+path_message_func (DBusConnection  *connection,
+                   DBusMessage     *message,
+                   void            *user_data)
+{
+  if (dbus_message_is_method_call (message,
+                                   "org.freedesktop.TestSuite",
+                                   "Echo"))
+    return handle_echo (connection, message);
+  else if (dbus_message_is_method_call (message,
+                                        "org.freedesktop.TestSuite",
+                                        "Exit"))
+    {
+      dbus_connection_close (connection);
+      quit ();
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+  else
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable
+echo_vtable = {
+  path_unregistered_func,
+  path_message_func,
+  NULL,
+};
+
+static DBusHandlerResult
+filter_func (DBusConnection     *connection,
+             DBusMessage        *message,
+             void               *user_data)
+{
+  if (dbus_message_is_signal (message,
+                              DBUS_INTERFACE_LOCAL,
+                              "Disconnected"))
+    {
+      dbus_connection_close (connection);
+      quit ();
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+  else
+    {
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  DBusConnection *connection;
+  DBusError error;
+  EchoData echo_data;
+  int result;
+  
+  echo_data.argc = argc;
+  echo_data.argv = argv;
+  
+  dbus_error_init (&error);
+  connection = dbus_bus_get (DBUS_BUS_STARTER, &error);
+  if (connection == NULL)
+    {
+      fprintf (stderr, "*** Failed to open connection to activating message bus: %s\n",
+               error.message);
+      dbus_error_free (&error);
+      return 1;
+    }
+
+  loop = _dbus_loop_new ();
+  if (loop == NULL)
+    die ("No memory\n");
+  
+  if (!test_connection_setup (loop, connection))
+    die ("No memory\n");
+
+  if (!dbus_connection_add_filter (connection,
+                                   filter_func, NULL, NULL))
+    die ("No memory");
+
+  if (!dbus_connection_register_object_path (connection,
+                                             echo_path,
+                                             &echo_vtable,
+                                             (void*) &echo_data))
+    die ("No memory");
+
+  {
+    void *d;
+    if (!dbus_connection_get_object_path_data (connection, echo_path, &d))
+      die ("No memory");
+    if (d != (void*) &echo_data)
+      die ("dbus_connection_get_object_path_data() doesn't seem to work right\n");
+  }
+  
+  result = dbus_bus_request_name (connection, "org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess",
+                                  0, &error);
+  if (dbus_error_is_set (&error))
+    {
+      fprintf (stderr, "Error %s\n", error.message);
+      _dbus_verbose ("*** Failed to acquire service: %s\n",
+                     error.message);
+      dbus_error_free (&error);
+      exit (1);
+    }
+
+  _dbus_verbose ("*** Test service entering main loop\n");
+  _dbus_loop_run (loop);
+
+  test_connection_shutdown (loop, connection);
+
+  dbus_connection_remove_filter (connection, filter_func, NULL);
+  
+  dbus_connection_unref (connection);
+
+  _dbus_loop_unref (loop);
+  loop = NULL;
+  
+  dbus_shutdown ();
+
+  _dbus_verbose ("*** Test service exiting\n");
+  
+  return 0;
+}