diff options
Diffstat (limited to 'glib/tests/spawn-path-search-helper.c')
-rw-r--r-- | glib/tests/spawn-path-search-helper.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/glib/tests/spawn-path-search-helper.c b/glib/tests/spawn-path-search-helper.c new file mode 100644 index 000000000..378c203c7 --- /dev/null +++ b/glib/tests/spawn-path-search-helper.c @@ -0,0 +1,171 @@ +/* + * Copyright 2021 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <stdio.h> + +#include <glib.h> + +#ifdef G_OS_UNIX +#include <sys/types.h> +#include <sys/wait.h> +#endif + +static void +child_setup (gpointer user_data) +{ +} + +typedef struct +{ + int wait_status; + gboolean done; +} ChildStatus; + +static ChildStatus child_status = { -1, FALSE }; + +static void +child_watch_cb (GPid pid, + gint status, + gpointer user_data) +{ + child_status.wait_status = status; + child_status.done = TRUE; +} + +int +main (int argc, + char **argv) +{ + gboolean search_path = FALSE; + gboolean search_path_from_envp = FALSE; + gboolean slow_path = FALSE; + gboolean unset_path_in_envp = FALSE; + gchar *chdir_child = NULL; + gchar *set_path_in_envp = NULL; + gchar **envp = NULL; + GOptionEntry entries[] = + { + { "chdir-child", '\0', + G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &chdir_child, + "Run PROGRAM in this working directory", NULL }, + { "search-path", '\0', + G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path, + "Search PATH for PROGRAM", NULL }, + { "search-path-from-envp", '\0', + G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path_from_envp, + "Search PATH from specified environment", NULL }, + { "set-path-in-envp", '\0', + G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &set_path_in_envp, + "Set PATH in specified environment to this value", "PATH", }, + { "unset-path-in-envp", '\0', + G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &unset_path_in_envp, + "Unset PATH in specified environment", NULL }, + { "slow-path", '\0', + G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &slow_path, + "Use a child-setup function to avoid the posix_spawn fast path", NULL }, + { NULL } + }; + GError *error = NULL; + int ret = 1; + GSpawnFlags spawn_flags = G_SPAWN_DO_NOT_REAP_CHILD; + GPid pid; + GOptionContext *context = NULL; + + context = g_option_context_new ("PROGRAM [ARGS...]"); + g_option_context_add_main_entries (context, entries, NULL); + + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + ret = 2; + goto out; + } + + if (argc < 2) + { + g_set_error (&error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "Usage: %s [OPTIONS] PROGRAM [ARGS...]", argv[0]); + ret = 2; + goto out; + } + + envp = g_get_environ (); + + if (set_path_in_envp != NULL && unset_path_in_envp) + { + g_set_error (&error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "Cannot both set PATH and unset it"); + ret = 2; + goto out; + } + + if (set_path_in_envp != NULL) + envp = g_environ_setenv (envp, "PATH", set_path_in_envp, TRUE); + + if (unset_path_in_envp) + envp = g_environ_unsetenv (envp, "PATH"); + + if (search_path) + spawn_flags |= G_SPAWN_SEARCH_PATH; + + if (search_path_from_envp) + spawn_flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP; + + if (!g_spawn_async_with_pipes (chdir_child, + &argv[1], + envp, + spawn_flags, + slow_path ? child_setup : NULL, + NULL, /* user_data */ + &pid, + NULL, /* stdin */ + NULL, /* stdout */ + NULL, /* stderr */ + &error)) + { + ret = 1; + goto out; + } + + g_child_watch_add (pid, child_watch_cb, NULL); + + while (!child_status.done) + g_main_context_iteration (NULL, TRUE); + + g_spawn_close_pid (pid); + +#ifdef G_OS_UNIX + if (WIFEXITED (child_status.wait_status)) + ret = WEXITSTATUS (child_status.wait_status); + else + ret = 1; +#else + ret = child_status.wait_status; +#endif + +out: + if (error != NULL) + fprintf (stderr, "%s\n", error->message); + + g_free (set_path_in_envp); + g_free (chdir_child); + g_clear_error (&error); + g_strfreev (envp); + g_option_context_free (context); + return ret; +} |