From 417c41f6c1ca122e1ce72a920bfc8c3ee841bf3c Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Thu, 10 Jul 2008 12:45:36 -0400 Subject: Add new _dbus_get_environment call It's a wrapper around the environ external variable. It will be important in the future when we allow bus clients to modify the environment of future activated clients. Presently, we just always use the bus daemon environment wholesale. --- dbus/dbus-sysdeps.c | 42 ++++++++++++++++++++++++++++++++++++++++++ dbus/dbus-sysdeps.h | 1 + 2 files changed, 43 insertions(+) diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index 1a736e42..d740f875 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -200,6 +200,48 @@ _dbus_clearenv (void) return rc; } +/** + * Gets a #NULL-terminated list of key=value pairs from the + * environment. Use dbus_free_string_array to free it. + * + * @returns the environment or #NULL on OOM + */ +char ** +_dbus_get_environment (void) +{ + int i, length; + extern char **environ; + char **environment; + + _dbus_assert (environ != NULL); + + for (length = 0; environ[length] != NULL; length++); + + /* Add one for NULL */ + length++; + + environment = dbus_new0 (char *, length); + + if (environment == NULL) + return NULL; + + for (i = 0; environ[i] != NULL; i++) + { + environment[i] = _dbus_strdup (environ[i]); + + if (environment[i] == NULL) + break; + } + + if (environ[i] != NULL) + { + dbus_free_string_array (environment); + environment = NULL; + } + + return environment; +} + /* * init a pipe instance. * diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 5ff13884..80236f05 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -101,6 +101,7 @@ const char* _dbus_getenv (const char *varname); dbus_bool_t _dbus_setenv (const char *varname, const char *value); dbus_bool_t _dbus_clearenv (void); +char ** _dbus_get_environment (void); /** A process ID */ typedef unsigned long dbus_pid_t; -- cgit From 0e3ec9cec0f6740acd39d6e6983f419e20461282 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 11 Jul 2008 11:32:30 -0400 Subject: Add new function _dbus_string_split_on_byte It allows you to turn a string like KEY=VALUE into two strings key and value. --- dbus/dbus-string-util.c | 25 +++++++++++++++++++++++++ dbus/dbus-string.c | 42 ++++++++++++++++++++++++++++++++++++++++++ dbus/dbus-string.h | 3 +++ 3 files changed, 70 insertions(+) diff --git a/dbus/dbus-string-util.c b/dbus/dbus-string-util.c index 492c5289..aed94878 100644 --- a/dbus/dbus-string-util.c +++ b/dbus/dbus-string-util.c @@ -846,6 +846,31 @@ _dbus_string_test (void) _dbus_string_free (&str); } + + { + const char two_strings[] = "one\ttwo"; + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_init (&other)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_append (&str, two_strings)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_split_on_byte (&str, '\t', &other)) + _dbus_assert_not_reached ("no memory or delimiter not found"); + + if (strcmp (_dbus_string_get_data (&str), "one") != 0) + _dbus_assert_not_reached ("left side after split on tab is wrong"); + + if (strcmp (_dbus_string_get_data (&other), "two") != 0) + _dbus_assert_not_reached ("right side after split on tab is wrong"); + + _dbus_string_free (&str); + _dbus_string_free (&other); + } return TRUE; } diff --git a/dbus/dbus-string.c b/dbus/dbus-string.c index cb108a8d..6b9b2bfe 100644 --- a/dbus/dbus-string.c +++ b/dbus/dbus-string.c @@ -1677,6 +1677,48 @@ _dbus_string_replace_len (const DBusString *source, return TRUE; } +/** + * Looks for the first occurance of a byte, deletes that byte, + * and moves everything after the byte to the beginning of a + * separate string. Both strings must be initialized, valid + * strings. + * + * @param source the source string + * @param byte the byte to remove and split the string at + * @param tail the split off string + * @returns #FALSE if not enough memory or if byte could not be found + * + */ +dbus_bool_t +_dbus_string_split_on_byte (DBusString *source, + unsigned char byte, + DBusString *tail) +{ + int byte_position; + char byte_string[2] = ""; + int head_length; + int tail_length; + + byte_string[0] = (char) byte; + + if (!_dbus_string_find (source, 0, byte_string, &byte_position)) + return FALSE; + + head_length = byte_position; + tail_length = _dbus_string_get_length (source) - head_length - 1; + + if (!_dbus_string_move_len (source, byte_position + 1, tail_length, + tail, 0)) + return FALSE; + + /* remove the trailing delimiter byte from the head now. + */ + if (!_dbus_string_set_length (source, head_length)) + return FALSE; + + return TRUE; +} + /* Unicode macros and utf8_validate() from GLib Owen Taylor, Havoc * Pennington, and Tom Tromey are the authors and authorized relicense. */ diff --git a/dbus/dbus-string.h b/dbus/dbus-string.h index d88d67ed..374f0a86 100644 --- a/dbus/dbus-string.h +++ b/dbus/dbus-string.h @@ -201,6 +201,9 @@ dbus_bool_t _dbus_string_replace_len (const DBusString *source, DBusString *dest, int replace_at, int replace_len); +dbus_bool_t _dbus_string_split_on_byte (DBusString *source, + unsigned char byte, + DBusString *tail); void _dbus_string_get_unichar (const DBusString *str, int start, dbus_unichar_t *ch_return, -- cgit From 8ec160419231d68c1f6a1e03def8353e02b442a9 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Thu, 10 Jul 2008 13:12:01 -0400 Subject: When spawning processes, don't ignore the passed in environment Previously, we'd always call execv() and unconditionally use the environment of the parent. Now we call execve() with the passed in environment. For compatibility, we detect if the passed in environment is NULL and for that case, use the environment from the parent instead. --- dbus/dbus-spawn.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/dbus/dbus-spawn.c b/dbus/dbus-spawn.c index d74b3265..35ccba6c 100644 --- a/dbus/dbus-spawn.c +++ b/dbus/dbus-spawn.c @@ -880,6 +880,7 @@ write_status_and_exit (int fd, int status) static void do_exec (int child_err_report_fd, char **argv, + char **envp, DBusSpawnChildSetupFunc child_setup, void *user_data) { @@ -910,8 +911,17 @@ do_exec (int child_err_report_fd, _dbus_warn ("Fd %d did not have the close-on-exec flag set!\n", i); } #endif + + if (envp == NULL) + { + extern char **environ; + + _dbus_assert (environ != NULL); + + envp = environ; + } - execv (argv[0], argv); + execve (argv[0], argv, envp); /* Exec failed */ write_err_and_exit (child_err_report_fd, @@ -1190,6 +1200,7 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, { do_exec (child_err_report_pipe[WRITE_END], argv, + env, child_setup, user_data); _dbus_assert_not_reached ("Got to code after exec() - should have exited on error"); } @@ -1218,6 +1229,8 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, else _dbus_babysitter_unref (sitter); + dbus_free_string_array (env); + _DBUS_ASSERT_ERROR_IS_CLEAR (error); return TRUE; -- cgit From 91306ef938873fce8f2ae2d4a6b3282d0379c65a Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Thu, 10 Jul 2008 14:35:38 -0400 Subject: Store what environment to activate with on activation object We now keep the environment in a hash table member of the activation object and provide a method bus_activation_set_environment_variable to modify the hash table. This hash table is seeded initially with the environment of the bus daemon itself. --- bus/activation.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++----- bus/activation.h | 5 ++ 2 files changed, 217 insertions(+), 18 deletions(-) diff --git a/bus/activation.c b/bus/activation.c index d087d6bd..b895d9fd 100644 --- a/bus/activation.c +++ b/bus/activation.c @@ -51,6 +51,7 @@ struct BusActivation * activations per se */ DBusHashTable *directories; + DBusHashTable *environment; }; typedef struct @@ -671,6 +672,69 @@ update_directory (BusActivation *activation, return retval; } +static dbus_bool_t +populate_environment (BusActivation *activation) +{ + DBusString key; + DBusString value; + int i; + char **environment; + dbus_bool_t retval; + + environment = _dbus_get_environment (); + + if (environment == NULL) + return FALSE; + + if (!_dbus_string_init (&key)) + { + dbus_free_string_array (environment); + return FALSE; + } + + if (!_dbus_string_init (&value)) + { + _dbus_string_free (&key); + dbus_free_string_array (environment); + return FALSE; + } + + for (i = 0; environment[i] != NULL; i++) + { + if (!_dbus_string_append (&key, environment[i])) + break; + + if (_dbus_string_split_on_byte (&key, '=', &value)) + { + char *hash_key, *hash_value; + + if (!_dbus_string_steal_data (&key, &hash_key)) + break; + + if (!_dbus_string_steal_data (&value, &hash_value)) + break; + + if (!_dbus_hash_table_insert_string (activation->environment, + hash_key, hash_value)) + break; + } + _dbus_string_set_length (&key, 0); + _dbus_string_set_length (&value, 0); + } + + if (environment[i] != NULL) + goto out; + + retval = TRUE; +out: + + _dbus_string_free (&key); + _dbus_string_free (&value); + dbus_free_string_array (environment); + + return retval; +} + BusActivation* bus_activation_new (BusContext *context, const DBusString *address, @@ -779,6 +843,22 @@ bus_activation_new (BusContext *context, link = _dbus_list_get_next_link (directories, link); } + activation->environment = _dbus_hash_table_new (DBUS_HASH_STRING, + (DBusFreeFunction) dbus_free, + (DBusFreeFunction) dbus_free); + + if (activation->environment == NULL) + { + BUS_SET_OOM (error); + goto failed; + } + + if (!populate_environment (activation)) + { + BUS_SET_OOM (error); + goto failed; + } + return activation; failed: @@ -813,41 +893,51 @@ bus_activation_unref (BusActivation *activation) _dbus_hash_table_unref (activation->pending_activations); if (activation->directories) _dbus_hash_table_unref (activation->directories); - + if (activation->environment) + _dbus_hash_table_unref (activation->environment); + dbus_free (activation); } -static void -child_setup (void *data) +static dbus_bool_t +add_bus_environment (BusActivation *activation, + DBusError *error) { - BusActivation *activation = data; const char *type; - /* If no memory, we simply have the child exit, so it won't try - * to connect to the wrong thing. - */ - if (!_dbus_setenv ("DBUS_STARTER_ADDRESS", activation->server_address)) - _dbus_exit (1); + if (!bus_activation_set_environment_variable (activation, + "DBUS_STARTER_ADDRESS", + activation->server_address, + error)) + return FALSE; type = bus_context_get_type (activation->context); if (type != NULL) { - if (!_dbus_setenv ("DBUS_STARTER_BUS_TYPE", type)) - _dbus_exit (1); + if (!bus_activation_set_environment_variable (activation, + "DBUS_STARTER_BUS_TYPE", type, + error)) + return FALSE; if (strcmp (type, "session") == 0) { - if (!_dbus_setenv ("DBUS_SESSION_BUS_ADDRESS", - activation->server_address)) - _dbus_exit (1); + if (!bus_activation_set_environment_variable (activation, + "DBUS_SESSION_BUS_ADDRESS", + activation->server_address, + error)) + return FALSE; } else if (strcmp (type, "system") == 0) { - if (!_dbus_setenv ("DBUS_SYSTEM_BUS_ADDRESS", - activation->server_address)) - _dbus_exit (1); + if (!bus_activation_set_environment_variable (activation, + "DBUS_SYSTEM_BUS_ADDRESS", + activation->server_address, + error)) + return FALSE; } } + + return TRUE; } typedef struct @@ -1389,6 +1479,92 @@ activation_find_entry (BusActivation *activation, return entry; } +static char ** +bus_activation_get_environment (BusActivation *activation) +{ + char **environment; + int i, length; + DBusString entry; + DBusHashIter iter; + + length = _dbus_hash_table_get_n_entries (activation->environment); + + environment = dbus_new0 (char *, length + 1); + + if (environment == NULL) + return NULL; + + i = 0; + _dbus_hash_iter_init (activation->environment, &iter); + + if (!_dbus_string_init (&entry)) + return NULL; + + while (_dbus_hash_iter_next (&iter)) + { + const char *key, *value; + + key = (const char *) _dbus_hash_iter_get_string_key (&iter); + value = (const char *) _dbus_hash_iter_get_value (&iter); + + if (!_dbus_string_append_printf (&entry, "%s=%s", key, value)) + break; + + if (!_dbus_string_steal_data (&entry, environment + i)) + break; + i++; + } + + _dbus_string_free (&entry); + + if (i != length) + { + dbus_free (environment); + environment = NULL; + } + + return environment; +} + +dbus_bool_t +bus_activation_set_environment_variable (BusActivation *activation, + const char *key, + const char *value, + DBusError *error) +{ + char *hash_key; + char *hash_value; + dbus_bool_t retval; + + retval = FALSE; + hash_key = NULL; + hash_value = NULL; + hash_key = _dbus_strdup (key); + + if (hash_key == NULL) + goto out; + + hash_value = _dbus_strdup (value); + + if (hash_value == NULL) + goto out; + + if (!_dbus_hash_table_insert_string (activation->environment, + hash_key, hash_value)) + goto out; + + retval = TRUE; +out: + if (retval == FALSE) + { + dbus_free (hash_key); + dbus_free (hash_value); + BUS_SET_OOM (error); + } + + return retval; +} + dbus_bool_t bus_activation_activate_service (BusActivation *activation, DBusConnection *connection, @@ -1688,20 +1864,38 @@ bus_activation_activate_service (BusActivation *activation, } _dbus_string_free (&command); + if (!add_bus_environment (activation, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + dbus_free_string_array (argv); + return FALSE; + } + + envp = bus_activation_get_environment (activation); + + if (envp == NULL) + { + BUS_SET_OOM (error); + dbus_free_string_array (argv); + return FALSE; + } + _dbus_verbose ("Spawning %s ...\n", argv[0]); if (!_dbus_spawn_async_with_babysitter (&pending_activation->babysitter, argv, envp, - child_setup, activation, + NULL, activation, error)) { _dbus_verbose ("Failed to spawn child\n"); _DBUS_ASSERT_ERROR_IS_SET (error); dbus_free_string_array (argv); + dbus_free_string_array (envp); return FALSE; } dbus_free_string_array (argv); + envp = NULL; _dbus_assert (pending_activation->babysitter != NULL); diff --git a/bus/activation.h b/bus/activation.h index 79084185..56e22836 100644 --- a/bus/activation.h +++ b/bus/activation.h @@ -34,6 +34,11 @@ BusActivation* bus_activation_new (BusContext *context, DBusError *error); BusActivation* bus_activation_ref (BusActivation *activation); void bus_activation_unref (BusActivation *activation); + +dbus_bool_t bus_activation_set_environment_variable (BusActivation *activation, + const char *key, + const char *value, + DBusError *error); dbus_bool_t bus_activation_activate_service (BusActivation *activation, DBusConnection *connection, BusTransaction *transaction, -- cgit From 37853b6dd04fa32a6f948438d2fbdcd08bd473e4 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Thu, 10 Jul 2008 13:19:44 -0400 Subject: Add new UpdateActivationEnvironment bus message It adjusts the environment of activated bus clients. This is important for session managers that get started after the session bus daemon and want to influence the environment of desktop services that are started by the bus. --- bus/driver.c | 131 +++++++++++++++++++++++++++++++++++++++++++++ bus/system.conf.in | 4 ++ doc/dbus-specification.xml | 34 ++++++++++++ 3 files changed, 169 insertions(+) diff --git a/bus/driver.c b/bus/driver.c index bdf5afbe..05ecd56c 100644 --- a/bus/driver.c +++ b/bus/driver.c @@ -810,6 +810,133 @@ send_ack_reply (DBusConnection *connection, return TRUE; } +static dbus_bool_t +bus_driver_handle_update_activation_environment (DBusConnection *connection, + BusTransaction *transaction, + DBusMessage *message, + DBusError *error) +{ + dbus_bool_t retval; + BusActivation *activation; + DBusMessageIter iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + int msg_type; + int array_type; + int key_type; + DBusList *keys, *key_link; + DBusList *values, *value_link; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + activation = bus_connection_get_activation (connection); + + dbus_message_iter_init (message, &iter); + + /* The message signature has already been checked for us, + * so let's just assert it's right. + */ + msg_type = dbus_message_iter_get_arg_type (&iter); + + _dbus_assert (msg_type == DBUS_TYPE_ARRAY); + + dbus_message_iter_recurse (&iter, &dict_iter); + + retval = FALSE; + + /* Then loop through the sent dictionary, add the location of + * the environment keys and values to lists. The result will + * be in reverse order, so we don't have to constantly search + * for the end of the list in a loop. + */ + keys = NULL; + values = NULL; + while ((array_type = dbus_message_iter_get_arg_type (&dict_iter)) == DBUS_TYPE_DICT_ENTRY) + { + dbus_message_iter_recurse (&dict_iter, &dict_entry_iter); + + while ((key_type = dbus_message_iter_get_arg_type (&dict_entry_iter)) == DBUS_TYPE_STRING) + { + char *key; + char *value; + int value_type; + + dbus_message_iter_get_basic (&dict_entry_iter, &key); + dbus_message_iter_next (&dict_entry_iter); + + value_type = dbus_message_iter_get_arg_type (&dict_entry_iter); + + if (value_type != DBUS_TYPE_STRING) + break; + + dbus_message_iter_get_basic (&dict_entry_iter, &value); + + if (!_dbus_list_append (&keys, key)) + { + BUS_SET_OOM (error); + break; + } + + if (!_dbus_list_append (&values, value)) + { + BUS_SET_OOM (error); + break; + } + + dbus_message_iter_next (&dict_entry_iter); + } + + if (key_type != DBUS_TYPE_INVALID) + break; + + dbus_message_iter_next (&dict_iter); + } + + if (array_type != DBUS_TYPE_INVALID) + goto out; + + _dbus_assert (_dbus_list_get_length (&keys) == _dbus_list_get_length (&values)); + + key_link = keys; + value_link = values; + while (key_link != NULL) + { + const char *key; + const char *value; + + key = key_link->data; + value = value_link->data; + + if (!bus_activation_set_environment_variable (activation, + key, value, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_verbose ("bus_activation_set_environment_variable() failed\n"); + break; + } + key_link = _dbus_list_get_next_link (&keys, key_link); + value_link = _dbus_list_get_next_link (&values, value_link); + } + + /* FIXME: We can fail early having set only some of the environment variables, + * (because of OOM failure). It's sort of hard to fix and it doesn't really + * matter, so we're punting for now. + */ + if (key_link != NULL) + goto out; + + if (!send_ack_reply (connection, transaction, + message, error)) + goto out; + + retval = TRUE; + + out: + _dbus_list_clear (&keys); + _dbus_list_clear (&values); + return retval; +} + static dbus_bool_t bus_driver_handle_add_match (DBusConnection *connection, BusTransaction *transaction, @@ -1542,6 +1669,10 @@ struct DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_UINT32_AS_STRING, DBUS_TYPE_UINT32_AS_STRING, bus_driver_handle_activate_service }, + { "UpdateActivationEnvironment", + DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + "", + bus_driver_handle_update_activation_environment }, { "NameHasOwner", DBUS_TYPE_STRING_AS_STRING, DBUS_TYPE_BOOLEAN_AS_STRING, diff --git a/bus/system.conf.in b/bus/system.conf.in index bb468bb9..6a71926e 100644 --- a/bus/system.conf.in +++ b/bus/system.conf.in @@ -53,6 +53,10 @@ + +