summaryrefslogtreecommitdiff
path: root/glib/tests/spawn-path-search-helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'glib/tests/spawn-path-search-helper.c')
-rw-r--r--glib/tests/spawn-path-search-helper.c171
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;
+}