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; +} |