mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-04-16 04:28:05 +02:00
gutils: Add functions for working with environment arrays
When spawning a child process, it is not safe to call setenv() before the fork() (because setenv() isn't thread-safe), but it's also not safe to call it after the fork() (because it's not async-signal-safe). So the only safe way to alter the environment for a child process from a threaded program is to pass a fully-formed envp array to exec*/g_spawn*/etc. So, add g_environ_getenv(), g_environ_setenv(), and g_environ_unsetenv(), which act like their namesakes, but work on arbitrary arrays rather than working directly on the environment. http://bugzilla.gnome.org/show_bug.cgi?id=659326
This commit is contained in:
parent
5ff803d91f
commit
409d93148f
@ -1665,6 +1665,9 @@ g_set_application_name
|
|||||||
g_get_prgname
|
g_get_prgname
|
||||||
g_set_prgname
|
g_set_prgname
|
||||||
g_get_environ
|
g_get_environ
|
||||||
|
g_environ_getenv
|
||||||
|
g_environ_setenv
|
||||||
|
g_environ_unsetenv
|
||||||
g_getenv
|
g_getenv
|
||||||
g_setenv
|
g_setenv
|
||||||
g_unsetenv
|
g_unsetenv
|
||||||
|
@ -1310,6 +1310,9 @@ g_setenv PRIVATE
|
|||||||
g_get_host_name
|
g_get_host_name
|
||||||
g_listenv
|
g_listenv
|
||||||
g_get_environ
|
g_get_environ
|
||||||
|
g_environ_getenv
|
||||||
|
g_environ_setenv
|
||||||
|
g_environ_unsetenv
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
g_find_program_in_path_utf8
|
g_find_program_in_path_utf8
|
||||||
g_get_current_dir_utf8
|
g_get_current_dir_utf8
|
||||||
|
@ -97,24 +97,36 @@ typedef enum
|
|||||||
* @user_data: user data to pass to the function.
|
* @user_data: user data to pass to the function.
|
||||||
*
|
*
|
||||||
* Specifies the type of the setup function passed to g_spawn_async(),
|
* Specifies the type of the setup function passed to g_spawn_async(),
|
||||||
* g_spawn_sync() and g_spawn_async_with_pipes(). On POSIX platforms it
|
* g_spawn_sync() and g_spawn_async_with_pipes(), which can, in very
|
||||||
* is called in the child after GLib has performed all the setup it plans
|
* limited ways, be used to affect the child's execution.
|
||||||
* to perform but before calling exec(). On POSIX actions taken in this
|
|
||||||
* function will thus only affect the child, not the parent.
|
|
||||||
*
|
*
|
||||||
* Note that POSIX allows only async-signal-safe functions (see signal(7))
|
* On POSIX platforms, the function is called in the child after GLib
|
||||||
* to be called in the child between fork() and exec(), which drastically
|
* has performed all the setup it plans to perform, but before calling
|
||||||
* limits the usefulness of child setup functions.
|
* exec(). Actions taken in this function will only affect the child,
|
||||||
|
* not the parent.
|
||||||
*
|
*
|
||||||
* Also note that modifying the environment from the child setup function
|
* On Windows, the function is called in the parent. Its usefulness on
|
||||||
* may not have the intended effect, since it will get overridden by
|
|
||||||
* a non-%NULL @env argument to the <literal>g_spawn...</literal> functions.
|
|
||||||
*
|
|
||||||
* On Windows the function is called in the parent. Its usefulness on
|
|
||||||
* Windows is thus questionable. In many cases executing the child setup
|
* Windows is thus questionable. In many cases executing the child setup
|
||||||
* function in the parent can have ill effects, and you should be very
|
* function in the parent can have ill effects, and you should be very
|
||||||
* careful when porting software to Windows that uses child setup
|
* careful when porting software to Windows that uses child setup
|
||||||
* functions.
|
* functions.
|
||||||
|
*
|
||||||
|
* However, even on POSIX, you are extremely limited in what you can
|
||||||
|
* safely do from a #GSpawnChildSetupFunc, because any mutexes that
|
||||||
|
* were held by other threads in the parent process at the time of the
|
||||||
|
* fork() will still be locked in the child process, and they will
|
||||||
|
* never be unlocked (since the threads that held them don't exist in
|
||||||
|
* the child). POSIX allows only async-signal-safe functions (see
|
||||||
|
* <citerefentry><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>)
|
||||||
|
* to be called in the child between fork() and exec(), which
|
||||||
|
* drastically limits the usefulness of child setup functions.
|
||||||
|
*
|
||||||
|
* In particular, it is not safe to call any function which may
|
||||||
|
* call malloc(), which includes POSIX functions such as setenv().
|
||||||
|
* If you need to set up the child environment differently from
|
||||||
|
* the parent, you should use g_get_environ(), g_environ_setenv(),
|
||||||
|
* and g_environ_unsetev(), and then pass the complete environment
|
||||||
|
* list to the <literal>g_spawn...</literal> function.
|
||||||
*/
|
*/
|
||||||
typedef void (* GSpawnChildSetupFunc) (gpointer user_data);
|
typedef void (* GSpawnChildSetupFunc) (gpointer user_data);
|
||||||
|
|
||||||
|
200
glib/gutils.c
200
glib/gutils.c
@ -1215,13 +1215,13 @@ _g_getenv_nomalloc (const gchar *variable,
|
|||||||
*
|
*
|
||||||
* Sets an environment variable. Both the variable's name and value
|
* Sets an environment variable. Both the variable's name and value
|
||||||
* should be in the GLib file name encoding. On UNIX, this means that
|
* should be in the GLib file name encoding. On UNIX, this means that
|
||||||
* they can be any sequence of bytes. On Windows, they should be in
|
* they can be arbitrary byte strings. On Windows, they should be in
|
||||||
* UTF-8.
|
* UTF-8.
|
||||||
*
|
*
|
||||||
* Note that on some systems, when variables are overwritten, the memory
|
* Note that on some systems, when variables are overwritten, the memory
|
||||||
* used for the previous variables and its value isn't reclaimed.
|
* used for the previous variables and its value isn't reclaimed.
|
||||||
*
|
*
|
||||||
* <warning><para>
|
* <warning>
|
||||||
* Environment variable handling in UNIX is not thread-safe, and your
|
* Environment variable handling in UNIX is not thread-safe, and your
|
||||||
* program may crash if one thread calls g_setenv() while another
|
* program may crash if one thread calls g_setenv() while another
|
||||||
* thread is calling getenv(). (And note that many functions, such as
|
* thread is calling getenv(). (And note that many functions, such as
|
||||||
@ -1229,7 +1229,12 @@ _g_getenv_nomalloc (const gchar *variable,
|
|||||||
* use at the very start of your program, before creating any other
|
* use at the very start of your program, before creating any other
|
||||||
* threads (or creating objects that create worker threads of their
|
* threads (or creating objects that create worker threads of their
|
||||||
* own).
|
* own).
|
||||||
* </para></warning>
|
*
|
||||||
|
* If you need to set up the environment for a child process, you can
|
||||||
|
* use g_get_environ() to get an environment array, modify that with
|
||||||
|
* g_environ_setenv() and g_environ_unsetenv(), and then pass that
|
||||||
|
* array directly to execvpe(), g_spawn_async(), or the like.
|
||||||
|
* </warning>
|
||||||
*
|
*
|
||||||
* Returns: %FALSE if the environment variable couldn't be set.
|
* Returns: %FALSE if the environment variable couldn't be set.
|
||||||
*
|
*
|
||||||
@ -1331,7 +1336,7 @@ extern char **environ;
|
|||||||
* Note that on some systems, when variables are overwritten, the memory
|
* Note that on some systems, when variables are overwritten, the memory
|
||||||
* used for the previous variables and its value isn't reclaimed.
|
* used for the previous variables and its value isn't reclaimed.
|
||||||
*
|
*
|
||||||
* <warning><para>
|
* <warning>
|
||||||
* Environment variable handling in UNIX is not thread-safe, and your
|
* Environment variable handling in UNIX is not thread-safe, and your
|
||||||
* program may crash if one thread calls g_unsetenv() while another
|
* program may crash if one thread calls g_unsetenv() while another
|
||||||
* thread is calling getenv(). (And note that many functions, such as
|
* thread is calling getenv(). (And note that many functions, such as
|
||||||
@ -1339,7 +1344,12 @@ extern char **environ;
|
|||||||
* use at the very start of your program, before creating any other
|
* use at the very start of your program, before creating any other
|
||||||
* threads (or creating objects that create worker threads of their
|
* threads (or creating objects that create worker threads of their
|
||||||
* own).
|
* own).
|
||||||
* </para></warning>
|
*
|
||||||
|
* If you need to set up the environment for a child process, you can
|
||||||
|
* use g_get_environ() to get an environment array, modify that with
|
||||||
|
* g_environ_setenv() and g_environ_unsetenv(), and then pass that
|
||||||
|
* array directly to execvpe(), g_spawn_async(), or the like.
|
||||||
|
* </warning>
|
||||||
*
|
*
|
||||||
* Since: 2.4
|
* Since: 2.4
|
||||||
**/
|
**/
|
||||||
@ -1354,31 +1364,13 @@ g_unsetenv (const gchar *variable)
|
|||||||
|
|
||||||
unsetenv (variable);
|
unsetenv (variable);
|
||||||
#else /* !HAVE_UNSETENV */
|
#else /* !HAVE_UNSETENV */
|
||||||
int len;
|
|
||||||
gchar **e, **f;
|
|
||||||
|
|
||||||
g_return_if_fail (variable != NULL);
|
g_return_if_fail (variable != NULL);
|
||||||
g_return_if_fail (strchr (variable, '=') == NULL);
|
g_return_if_fail (strchr (variable, '=') == NULL);
|
||||||
|
|
||||||
len = strlen (variable);
|
|
||||||
|
|
||||||
/* Mess directly with the environ array.
|
/* Mess directly with the environ array.
|
||||||
* This seems to be the only portable way to do this.
|
* This seems to be the only portable way to do this.
|
||||||
*
|
|
||||||
* Note that we remove *all* environment entries for
|
|
||||||
* the variable name, not just the first.
|
|
||||||
*/
|
*/
|
||||||
e = f = environ;
|
g_environ_unsetenv (environ, variable);
|
||||||
while (*e != NULL)
|
|
||||||
{
|
|
||||||
if (strncmp (*e, variable, len) != 0 || (*e)[len] != '=')
|
|
||||||
{
|
|
||||||
*f = *e;
|
|
||||||
f++;
|
|
||||||
}
|
|
||||||
e++;
|
|
||||||
}
|
|
||||||
*f = NULL;
|
|
||||||
#endif /* !HAVE_UNSETENV */
|
#endif /* !HAVE_UNSETENV */
|
||||||
|
|
||||||
#else /* G_OS_WIN32 */
|
#else /* G_OS_WIN32 */
|
||||||
@ -1498,7 +1490,8 @@ g_listenv (void)
|
|||||||
* The return value is freshly allocated and it should be freed with
|
* The return value is freshly allocated and it should be freed with
|
||||||
* g_strfreev() when it is no longer needed.
|
* g_strfreev() when it is no longer needed.
|
||||||
*
|
*
|
||||||
* Returns: (array zero-terminated=1) (transfer full): the list of environment variables
|
* Returns: (array zero-terminated=1) (transfer full): the list of
|
||||||
|
* environment variables
|
||||||
*
|
*
|
||||||
* Since: 2.28
|
* Since: 2.28
|
||||||
*/
|
*/
|
||||||
@ -1524,6 +1517,163 @@ g_get_environ (void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
g_environ_find (gchar **envp,
|
||||||
|
const gchar *variable)
|
||||||
|
{
|
||||||
|
gint len, i;
|
||||||
|
|
||||||
|
len = strlen (variable);
|
||||||
|
|
||||||
|
for (i = 0; envp[i]; i++)
|
||||||
|
{
|
||||||
|
if (strncmp (envp[i], variable, len) == 0 &&
|
||||||
|
envp[i][len] == '=')
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_environ_getenv:
|
||||||
|
* @envp: (array zero-terminated=1) (transfer none): an environment
|
||||||
|
* list (eg, as returned from g_get_environ())
|
||||||
|
* @variable: the environment variable to get, in the GLib file name
|
||||||
|
* encoding
|
||||||
|
*
|
||||||
|
* Returns the value of the environment variable @variable in the
|
||||||
|
* provided list @envp.
|
||||||
|
*
|
||||||
|
* The name and value are in the GLib file name encoding.
|
||||||
|
* On UNIX, this means the actual bytes which might or might not
|
||||||
|
* be in some consistent character set and encoding. On Windows,
|
||||||
|
* it is in UTF-8. On Windows, in case the environment variable's
|
||||||
|
* value contains references to other environment variables, they
|
||||||
|
* are expanded.
|
||||||
|
*
|
||||||
|
* Return value: the value of the environment variable, or %NULL if
|
||||||
|
* the environment variable is not set in @envp. The returned
|
||||||
|
* string is owned by @envp, and will be freed if @variable is
|
||||||
|
* set or unset again.
|
||||||
|
*
|
||||||
|
* Since: 2.32
|
||||||
|
*/
|
||||||
|
const gchar *
|
||||||
|
g_environ_getenv (gchar **envp,
|
||||||
|
const gchar *variable)
|
||||||
|
{
|
||||||
|
gint index;
|
||||||
|
|
||||||
|
g_return_val_if_fail (envp != NULL, NULL);
|
||||||
|
g_return_val_if_fail (variable != NULL, NULL);
|
||||||
|
|
||||||
|
index = g_environ_find (envp, variable);
|
||||||
|
if (index != -1)
|
||||||
|
return envp[index] + strlen (variable) + 1;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_environ_setenv:
|
||||||
|
* @envp: (array zero-terminated=1) (transfer full): an environment
|
||||||
|
* list (eg, as returned from g_get_environ())
|
||||||
|
* @variable: the environment variable to set, must not contain '='
|
||||||
|
* @value: the value for to set the variable to
|
||||||
|
* @overwrite: whether to change the variable if it already exists
|
||||||
|
*
|
||||||
|
* Sets the environment variable @variable in the provided list
|
||||||
|
* @envp to @value.
|
||||||
|
*
|
||||||
|
* Both the variable's name and value should be in the GLib
|
||||||
|
* file name encoding. On UNIX, this means that they can be
|
||||||
|
* arbitrary byte strings. On Windows, they should be in UTF-8.
|
||||||
|
*
|
||||||
|
* Return value: (array zero-terminated=1) (transfer full): the
|
||||||
|
* updated environment
|
||||||
|
*
|
||||||
|
* Since: 2.32
|
||||||
|
*/
|
||||||
|
gchar **
|
||||||
|
g_environ_setenv (gchar **envp,
|
||||||
|
const gchar *variable,
|
||||||
|
const gchar *value,
|
||||||
|
gboolean overwrite)
|
||||||
|
{
|
||||||
|
gint index;
|
||||||
|
|
||||||
|
g_return_val_if_fail (envp != NULL, NULL);
|
||||||
|
g_return_val_if_fail (variable != NULL, NULL);
|
||||||
|
g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
|
||||||
|
|
||||||
|
index = g_environ_find (envp, variable);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
if (overwrite)
|
||||||
|
{
|
||||||
|
g_free (envp[index]);
|
||||||
|
envp[index] = g_strdup_printf ("%s=%s", variable, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gint length;
|
||||||
|
|
||||||
|
length = g_strv_length (envp);
|
||||||
|
envp = g_renew (gchar *, envp, length + 2);
|
||||||
|
envp[length] = g_strdup_printf ("%s=%s", variable, value);
|
||||||
|
envp[length + 1] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return envp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_environ_unsetenv:
|
||||||
|
* @envp: (array zero-terminated=1) (transfer full): an environment
|
||||||
|
* list (eg, as returned from g_get_environ())
|
||||||
|
* @variable: the environment variable to remove, must not contain '='
|
||||||
|
*
|
||||||
|
* Removes the environment variable @variable from the provided
|
||||||
|
* environment @envp.
|
||||||
|
*
|
||||||
|
* Return value: (array zero-terminated=1) (transfer full): the
|
||||||
|
* updated environment
|
||||||
|
*
|
||||||
|
* Since: 2.32
|
||||||
|
*/
|
||||||
|
gchar **
|
||||||
|
g_environ_unsetenv (gchar **envp,
|
||||||
|
const gchar *variable)
|
||||||
|
{
|
||||||
|
gint len;
|
||||||
|
gchar **e, **f;
|
||||||
|
|
||||||
|
g_return_val_if_fail (envp != NULL, NULL);
|
||||||
|
g_return_val_if_fail (variable != NULL, NULL);
|
||||||
|
g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
|
||||||
|
|
||||||
|
len = strlen (variable);
|
||||||
|
|
||||||
|
/* Note that we remove *all* environment entries for
|
||||||
|
* the variable name, not just the first.
|
||||||
|
*/
|
||||||
|
e = f = envp;
|
||||||
|
while (*e != NULL)
|
||||||
|
{
|
||||||
|
if (strncmp (*e, variable, len) != 0 || (*e)[len] != '=')
|
||||||
|
{
|
||||||
|
*f = *e;
|
||||||
|
f++;
|
||||||
|
}
|
||||||
|
e++;
|
||||||
|
}
|
||||||
|
*f = NULL;
|
||||||
|
|
||||||
|
return envp;
|
||||||
|
}
|
||||||
|
|
||||||
G_LOCK_DEFINE_STATIC (g_utils_global);
|
G_LOCK_DEFINE_STATIC (g_utils_global);
|
||||||
|
|
||||||
static gchar *g_tmp_dir = NULL;
|
static gchar *g_tmp_dir = NULL;
|
||||||
|
@ -271,7 +271,16 @@ gboolean g_setenv (const gchar *variable,
|
|||||||
gboolean overwrite);
|
gboolean overwrite);
|
||||||
void g_unsetenv (const gchar *variable);
|
void g_unsetenv (const gchar *variable);
|
||||||
gchar ** g_listenv (void);
|
gchar ** g_listenv (void);
|
||||||
|
|
||||||
gchar ** g_get_environ (void);
|
gchar ** g_get_environ (void);
|
||||||
|
const gchar * g_environ_getenv (gchar **envp,
|
||||||
|
const gchar *variable);
|
||||||
|
gchar ** g_environ_setenv (gchar **envp,
|
||||||
|
const gchar *variable,
|
||||||
|
const gchar *value,
|
||||||
|
gboolean overwrite) G_GNUC_WARN_UNUSED_RESULT;
|
||||||
|
gchar ** g_environ_unsetenv (gchar **envp,
|
||||||
|
const gchar *variable) G_GNUC_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
/* private */
|
/* private */
|
||||||
const gchar* _g_getenv_nomalloc (const gchar *variable,
|
const gchar* _g_getenv_nomalloc (const gchar *variable,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user