1/*
2 * Copyright 2021 Collabora Ltd.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19#include <errno.h>
20#include <stdio.h>
21
22#include <glib.h>
23
24#ifdef G_OS_UNIX
25#include <sys/types.h>
26#include <sys/wait.h>
27#endif
28
29static void
30child_setup (gpointer user_data)
31{
32}
33
34typedef struct
35{
36 int wait_status;
37 gboolean done;
38} ChildStatus;
39
40static ChildStatus child_status = { -1, FALSE };
41
42static void
43child_watch_cb (GPid pid,
44 gint status,
45 gpointer user_data)
46{
47 child_status.wait_status = status;
48 child_status.done = TRUE;
49}
50
51int
52main (int argc,
53 char **argv)
54{
55 gboolean search_path = FALSE;
56 gboolean search_path_from_envp = FALSE;
57 gboolean slow_path = FALSE;
58 gboolean unset_path_in_envp = FALSE;
59 gchar *chdir_child = NULL;
60 gchar *set_path_in_envp = NULL;
61 gchar **envp = NULL;
62 GOptionEntry entries[] =
63 {
64 { "chdir-child", '\0',
65 G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &chdir_child,
66 "Run PROGRAM in this working directory", NULL },
67 { "search-path", '\0',
68 G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path,
69 "Search PATH for PROGRAM", NULL },
70 { "search-path-from-envp", '\0',
71 G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path_from_envp,
72 "Search PATH from specified environment", NULL },
73 { "set-path-in-envp", '\0',
74 G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &set_path_in_envp,
75 "Set PATH in specified environment to this value", "PATH", },
76 { "unset-path-in-envp", '\0',
77 G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &unset_path_in_envp,
78 "Unset PATH in specified environment", NULL },
79 { "slow-path", '\0',
80 G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &slow_path,
81 "Use a child-setup function to avoid the posix_spawn fast path", NULL },
82 { NULL }
83 };
84 GError *error = NULL;
85 int ret = 1;
86 GSpawnFlags spawn_flags = G_SPAWN_DO_NOT_REAP_CHILD;
87 GPid pid;
88 GOptionContext *context = NULL;
89
90 context = g_option_context_new (parameter_string: "PROGRAM [ARGS...]");
91 g_option_context_add_main_entries (context, entries, NULL);
92
93 if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error))
94 {
95 ret = 2;
96 goto out;
97 }
98
99 if (argc < 2)
100 {
101 g_set_error (err: &error, G_OPTION_ERROR, code: G_OPTION_ERROR_FAILED,
102 format: "Usage: %s [OPTIONS] PROGRAM [ARGS...]", argv[0]);
103 ret = 2;
104 goto out;
105 }
106
107 envp = g_get_environ ();
108
109 if (set_path_in_envp != NULL && unset_path_in_envp)
110 {
111 g_set_error (err: &error, G_OPTION_ERROR, code: G_OPTION_ERROR_FAILED,
112 format: "Cannot both set PATH and unset it");
113 ret = 2;
114 goto out;
115 }
116
117 if (set_path_in_envp != NULL)
118 envp = g_environ_setenv (envp, variable: "PATH", value: set_path_in_envp, TRUE);
119
120 if (unset_path_in_envp)
121 envp = g_environ_unsetenv (envp, variable: "PATH");
122
123 if (search_path)
124 spawn_flags |= G_SPAWN_SEARCH_PATH;
125
126 if (search_path_from_envp)
127 spawn_flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP;
128
129 if (!g_spawn_async_with_pipes (working_directory: chdir_child,
130 argv: &argv[1],
131 envp,
132 flags: spawn_flags,
133 child_setup: slow_path ? child_setup : NULL,
134 NULL, /* user_data */
135 child_pid: &pid,
136 NULL, /* stdin */
137 NULL, /* stdout */
138 NULL, /* stderr */
139 error: &error))
140 {
141 ret = 1;
142 goto out;
143 }
144
145 g_child_watch_add (pid, function: child_watch_cb, NULL);
146
147 while (!child_status.done)
148 g_main_context_iteration (NULL, TRUE);
149
150 g_spawn_close_pid (pid);
151
152#ifdef G_OS_UNIX
153 if (WIFEXITED (child_status.wait_status))
154 ret = WEXITSTATUS (child_status.wait_status);
155 else
156 ret = 1;
157#else
158 ret = child_status.wait_status;
159#endif
160
161out:
162 if (error != NULL)
163 fprintf (stderr, format: "%s\n", error->message);
164
165 g_free (mem: set_path_in_envp);
166 g_free (mem: chdir_child);
167 g_clear_error (err: &error);
168 g_strfreev (str_array: envp);
169 g_option_context_free (context);
170 return ret;
171}
172

source code of gtk/subprojects/glib/glib/tests/spawn-path-search-helper.c