diff options
| -rw-r--r-- | ChangeLog | 34 | ||||
| -rw-r--r-- | bus/activation.c | 654 | ||||
| -rw-r--r-- | bus/dispatch.c | 641 | ||||
| -rw-r--r-- | configure.in | 3 | ||||
| -rw-r--r-- | dbus/Makefile.am | 2 | ||||
| -rw-r--r-- | dbus/dbus-shell.c | 706 | ||||
| -rw-r--r-- | dbus/dbus-shell.h | 42 | ||||
| -rw-r--r-- | python/.cvsignore | 1 | ||||
| -rw-r--r-- | python/Makefile.am | 14 | ||||
| -rw-r--r-- | test/.cvsignore | 2 | ||||
| -rw-r--r-- | test/Makefile.am | 16 | ||||
| -rw-r--r-- | test/data/valid-service-files/.cvsignore | 3 | ||||
| -rw-r--r-- | test/data/valid-service-files/debug-shell-echo-fail.service.in | 3 | ||||
| -rw-r--r-- | test/data/valid-service-files/debug-shell-echo-success.service.in | 3 | ||||
| -rw-r--r-- | test/shell-test.c | 107 | ||||
| -rw-r--r-- | test/test-shell-service.c | 198 | 
16 files changed, 1951 insertions, 478 deletions
@@ -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 diff --git a/bus/activation.c b/bus/activation.c index 10839b9f..2faa42b4 100644 --- a/bus/activation.c +++ b/bus/activation.c @@ -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; diff --git a/bus/dispatch.c b/bus/dispatch.c index 123a29ed..02f6c690 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -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); diff --git a/configure.in b/configure.in index 0266d006..66fae85b 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/dbus/Makefile.am b/dbus/Makefile.am index 879a34c7..f4175c79 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -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 index 00000000..02d8cc81 --- /dev/null +++ b/dbus/dbus-shell.c @@ -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 (¤t_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 (¤t_token, '\\') ||  +	          !_dbus_string_append_byte (¤t_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 (¤t_token, *p)) +	    { +	      _DBUS_SET_OOM (error); +	      goto error; +	    } +        } +      else +        { +          switch (*p) +            { +            case '\n': +              if (!delimit_token (¤t_token, &retval, error)) +		goto error; + +		_dbus_string_free (¤t_token); + +		if (!_dbus_string_init (¤t_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 (¤t_token) > 0) +                { +                  if (!delimit_token (¤t_token, &retval, error)) +		    goto error; + +		  _dbus_string_free (¤t_token); + +		  if (!_dbus_string_init (¤t_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 (¤t_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 (¤t_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 (¤t_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 (¤t_token); +  +  return retval; + + error: +  _dbus_string_free (¤t_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 index 00000000..ceda45bf --- /dev/null +++ b/dbus/dbus-shell.h @@ -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 */ + + diff --git a/python/.cvsignore b/python/.cvsignore index 9c73bb7a..cce7eeaf 100644 --- a/python/.cvsignore +++ b/python/.cvsignore @@ -5,5 +5,6 @@ dbus_bindings.pxd  *.lo  *.la  dbus_bindings.c +dbus_glib_bindings.c  *.pyc  .libs diff --git a/python/Makefile.am b/python/Makefile.am index 81a8367a..7bedfd0d 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -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  -		 diff --git a/test/.cvsignore b/test/.cvsignore index 83e7bf0c..20de1a28 100644 --- a/test/.cvsignore +++ b/test/.cvsignore @@ -20,3 +20,5 @@ test-segfault  test-service  test-sleep-forever  decode-gcov +shell-test +test-shell-service diff --git a/test/Makefile.am b/test/Makefile.am index 4498b7e3..27f51023 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -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) diff --git a/test/data/valid-service-files/.cvsignore b/test/data/valid-service-files/.cvsignore index c5bd07da..ca3fbc75 100644 --- a/test/data/valid-service-files/.cvsignore +++ b/test/data/valid-service-files/.cvsignore @@ -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 index 00000000..971be60c --- /dev/null +++ b/test/data/valid-service-files/debug-shell-echo-fail.service.in @@ -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 index 00000000..49bf406e --- /dev/null +++ b/test/data/valid-service-files/debug-shell-echo-success.service.in @@ -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 index 00000000..46a387d7 --- /dev/null +++ b/test/shell-test.c @@ -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 index 00000000..79a4949a --- /dev/null +++ b/test/test-shell-service.c @@ -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; +}  | 
