* @user_data: user data to pass to the function.
*
* 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
- * is called in the child after GLib has performed all the setup it plans
- * to perform but before calling exec(). On POSIX actions taken in this
- * function will thus only affect the child, not the parent.
+ * g_spawn_sync() and g_spawn_async_with_pipes(), which can, in very
+ * limited ways, be used to affect the child's execution.
*
- * Note that POSIX allows only async-signal-safe functions (see signal(7))
- * to be called in the child between fork() and exec(), which drastically
- * limits the usefulness of child setup functions.
+ * On POSIX platforms, the function is called in the child after GLib
+ * has performed all the setup it plans to perform, but before calling
+ * 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
- * 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
+ * On Windows, the function is called in the parent. Its usefulness on
* Windows is thus questionable. In many cases executing the child setup
* function in the parent can have ill effects, and you should be very
* careful when porting software to Windows that uses child setup
* 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);
*
* Sets an environment variable. Both the variable's name and value
* 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.
*
- * 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.
*
- * <warning><para>
+ * <warning>
* Environment variable handling in UNIX is not thread-safe, and your
* program may crash if one thread calls g_setenv() while another
* thread is calling getenv(). (And note that many functions, such as
* use at the very start of your program, before creating any other
* threads (or creating objects that create worker threads of their
* 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.
*
* Note that on some systems, when variables are overwritten, the memory
* 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
* program may crash if one thread calls g_unsetenv() while another
* thread is calling getenv(). (And note that many functions, such as
* use at the very start of your program, before creating any other
* threads (or creating objects that create worker threads of their
* 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
**/
unsetenv (variable);
#else /* !HAVE_UNSETENV */
- int len;
- gchar **e, **f;
-
g_return_if_fail (variable != NULL);
g_return_if_fail (strchr (variable, '=') == NULL);
- len = strlen (variable);
-
/* Mess directly with the environ array.
* 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;
- while (*e != NULL)
- {
- if (strncmp (*e, variable, len) != 0 || (*e)[len] != '=')
- {
- *f = *e;
- f++;
- }
- e++;
- }
- *f = NULL;
+ g_environ_unsetenv (environ, variable);
#endif /* !HAVE_UNSETENV */
#else /* G_OS_WIN32 */
/**
* g_get_environ:
- *
+ *
* Gets the list of environment variables for the current process. The
* list is %NULL terminated and each item in the list is of the form
* 'NAME=VALUE'.
* The return value is freshly allocated and it should be freed with
* 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
*/
#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);
static gchar *g_tmp_dir = NULL;