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 <glib.h>
20
21#ifdef G_OS_UNIX
22#include <sys/types.h>
23#include <sys/wait.h>
24#endif
25
26static void
27test_do_not_search (void)
28{
29 GPtrArray *argv = g_ptr_array_new_with_free_func (element_free_func: g_free);
30 gchar *here = g_test_build_filename (file_type: G_TEST_BUILT, first_path: ".", NULL);
31 gchar *subdir = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "path-test-subdir", NULL);
32 gchar **envp = g_get_environ ();
33 gchar *out = NULL;
34 gchar *err = NULL;
35 GError *error = NULL;
36 int wait_status = -1;
37
38 g_test_summary (summary: "Without G_SPAWN_SEARCH_PATH, spawn-test-helper "
39 "means ./spawn-test-helper.");
40
41 envp = g_environ_setenv (envp, variable: "PATH", value: subdir, TRUE);
42
43 g_ptr_array_add (array: argv,
44 data: g_test_build_filename (file_type: G_TEST_BUILT, first_path: "spawn-path-search-helper", NULL));
45 g_ptr_array_add (array: argv, data: g_strdup (str: "--"));
46 g_ptr_array_add (array: argv, data: g_strdup (str: "spawn-test-helper"));
47 g_ptr_array_add (array: argv, NULL);
48
49 g_spawn_sync (working_directory: here,
50 argv: (char **) argv->pdata,
51 envp,
52 flags: G_SPAWN_DEFAULT,
53 NULL, /* child setup */
54 NULL, /* user data */
55 standard_output: &out,
56 standard_error: &err,
57 exit_status: &wait_status,
58 error: &error);
59 g_assert_no_error (error);
60
61 g_test_message (format: "%s", out);
62 g_test_message (format: "%s", err);
63 g_assert_nonnull (strstr (err, "this is spawn-test-helper from glib/tests"));
64
65#ifdef G_OS_UNIX
66 g_assert_true (WIFEXITED (wait_status));
67 g_assert_cmpint (WEXITSTATUS (wait_status), ==, 0);
68#endif
69
70 g_strfreev (str_array: envp);
71 g_free (mem: here);
72 g_free (mem: subdir);
73 g_free (mem: out);
74 g_free (mem: err);
75 g_ptr_array_unref (array: argv);
76}
77
78static void
79test_search_path (void)
80{
81 GPtrArray *argv = g_ptr_array_new_with_free_func (element_free_func: g_free);
82 gchar *here = g_test_build_filename (file_type: G_TEST_BUILT, first_path: ".", NULL);
83 gchar *subdir = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "path-test-subdir", NULL);
84 gchar **envp = g_get_environ ();
85 gchar *out = NULL;
86 gchar *err = NULL;
87 GError *error = NULL;
88 int wait_status = -1;
89
90 g_test_summary (summary: "With G_SPAWN_SEARCH_PATH, spawn-test-helper "
91 "means $PATH/spawn-test-helper.");
92
93 envp = g_environ_setenv (envp, variable: "PATH", value: subdir, TRUE);
94
95 g_ptr_array_add (array: argv,
96 data: g_test_build_filename (file_type: G_TEST_BUILT, first_path: "spawn-path-search-helper", NULL));
97 g_ptr_array_add (array: argv, data: g_strdup (str: "--search-path"));
98 g_ptr_array_add (array: argv, data: g_strdup (str: "--"));
99 g_ptr_array_add (array: argv, data: g_strdup (str: "spawn-test-helper"));
100 g_ptr_array_add (array: argv, NULL);
101
102 g_spawn_sync (working_directory: here,
103 argv: (char **) argv->pdata,
104 envp,
105 flags: G_SPAWN_DEFAULT,
106 NULL, /* child setup */
107 NULL, /* user data */
108 standard_output: &out,
109 standard_error: &err,
110 exit_status: &wait_status,
111 error: &error);
112 g_assert_no_error (error);
113
114 g_test_message (format: "%s", out);
115 g_test_message (format: "%s", err);
116 g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
117
118#ifdef G_OS_UNIX
119 g_assert_true (WIFEXITED (wait_status));
120 g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
121#endif
122
123 g_strfreev (str_array: envp);
124 g_free (mem: here);
125 g_free (mem: subdir);
126 g_free (mem: out);
127 g_free (mem: err);
128 g_ptr_array_unref (array: argv);
129}
130
131static void
132test_search_path_from_envp (void)
133{
134 GPtrArray *argv = g_ptr_array_new_with_free_func (element_free_func: g_free);
135 gchar *here = g_test_build_filename (file_type: G_TEST_BUILT, first_path: ".", NULL);
136 gchar *subdir = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "path-test-subdir", NULL);
137 gchar **envp = g_get_environ ();
138 gchar *out = NULL;
139 gchar *err = NULL;
140 GError *error = NULL;
141 int wait_status = -1;
142
143 g_test_summary (summary: "With G_SPAWN_SEARCH_PATH_FROM_ENVP, spawn-test-helper "
144 "means $PATH/spawn-test-helper with $PATH from envp.");
145
146 envp = g_environ_setenv (envp, variable: "PATH", value: here, TRUE);
147
148 g_ptr_array_add (array: argv,
149 data: g_test_build_filename (file_type: G_TEST_BUILT, first_path: "spawn-path-search-helper", NULL));
150 g_ptr_array_add (array: argv, data: g_strdup (str: "--search-path-from-envp"));
151 g_ptr_array_add (array: argv, data: g_strdup (str: "--set-path-in-envp"));
152 g_ptr_array_add (array: argv, data: g_strdup (str: subdir));
153 g_ptr_array_add (array: argv, data: g_strdup (str: "--"));
154 g_ptr_array_add (array: argv, data: g_strdup (str: "spawn-test-helper"));
155 g_ptr_array_add (array: argv, NULL);
156
157 g_spawn_sync (working_directory: here,
158 argv: (char **) argv->pdata,
159 envp,
160 flags: G_SPAWN_DEFAULT,
161 NULL, /* child setup */
162 NULL, /* user data */
163 standard_output: &out,
164 standard_error: &err,
165 exit_status: &wait_status,
166 error: &error);
167 g_assert_no_error (error);
168
169 g_test_message (format: "%s", out);
170 g_test_message (format: "%s", err);
171 g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
172
173#ifdef G_OS_UNIX
174 g_assert_true (WIFEXITED (wait_status));
175 g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
176#endif
177
178 g_strfreev (str_array: envp);
179 g_free (mem: here);
180 g_free (mem: subdir);
181 g_free (mem: out);
182 g_free (mem: err);
183 g_ptr_array_unref (array: argv);
184}
185
186static void
187test_search_path_ambiguous (void)
188{
189 GPtrArray *argv = g_ptr_array_new_with_free_func (element_free_func: g_free);
190 gchar *here = g_test_build_filename (file_type: G_TEST_BUILT, first_path: ".", NULL);
191 gchar *subdir = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "path-test-subdir", NULL);
192 gchar **envp = g_get_environ ();
193 gchar *out = NULL;
194 gchar *err = NULL;
195 GError *error = NULL;
196 int wait_status = -1;
197
198 g_test_summary (summary: "With G_SPAWN_SEARCH_PATH and G_SPAWN_SEARCH_PATH_FROM_ENVP, "
199 "the latter wins.");
200
201 envp = g_environ_setenv (envp, variable: "PATH", value: here, TRUE);
202
203 g_ptr_array_add (array: argv,
204 data: g_test_build_filename (file_type: G_TEST_BUILT, first_path: "spawn-path-search-helper", NULL));
205 g_ptr_array_add (array: argv, data: g_strdup (str: "--search-path"));
206 g_ptr_array_add (array: argv, data: g_strdup (str: "--search-path-from-envp"));
207 g_ptr_array_add (array: argv, data: g_strdup (str: "--set-path-in-envp"));
208 g_ptr_array_add (array: argv, data: g_strdup (str: subdir));
209 g_ptr_array_add (array: argv, data: g_strdup (str: "--"));
210 g_ptr_array_add (array: argv, data: g_strdup (str: "spawn-test-helper"));
211 g_ptr_array_add (array: argv, NULL);
212
213 g_spawn_sync (working_directory: here,
214 argv: (char **) argv->pdata,
215 envp,
216 flags: G_SPAWN_DEFAULT,
217 NULL, /* child setup */
218 NULL, /* user data */
219 standard_output: &out,
220 standard_error: &err,
221 exit_status: &wait_status,
222 error: &error);
223 g_assert_no_error (error);
224
225 g_test_message (format: "%s", out);
226 g_test_message (format: "%s", err);
227 g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
228
229#ifdef G_OS_UNIX
230 g_assert_true (WIFEXITED (wait_status));
231 g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
232#endif
233
234 g_strfreev (str_array: envp);
235 g_free (mem: here);
236 g_free (mem: subdir);
237 g_free (mem: out);
238 g_free (mem: err);
239 g_ptr_array_unref (array: argv);
240}
241
242static void
243test_search_path_fallback_in_environ (void)
244{
245 GPtrArray *argv = g_ptr_array_new_with_free_func (element_free_func: g_free);
246 gchar *here = g_test_build_filename (file_type: G_TEST_BUILT, first_path: ".", NULL);
247 gchar *subdir = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "path-test-subdir", NULL);
248 gchar **envp = g_get_environ ();
249 gchar *out = NULL;
250 gchar *err = NULL;
251 GError *error = NULL;
252 int wait_status = -1;
253
254 g_test_summary (summary: "With G_SPAWN_SEARCH_PATH but no PATH, a fallback is used.");
255 /* We can't make a meaningful assertion about what the fallback *is*,
256 * but we can assert that it *includes* the current working directory. */
257
258 if (g_file_test (filename: "/usr/bin/spawn-test-helper", test: G_FILE_TEST_IS_EXECUTABLE) ||
259 g_file_test (filename: "/bin/spawn-test-helper", test: G_FILE_TEST_IS_EXECUTABLE))
260 {
261 g_test_skip (msg: "Not testing fallback with unknown spawn-test-helper "
262 "executable in /usr/bin:/bin");
263 return;
264 }
265
266 envp = g_environ_unsetenv (envp, variable: "PATH");
267
268 g_ptr_array_add (array: argv,
269 data: g_test_build_filename (file_type: G_TEST_BUILT, first_path: "spawn-path-search-helper", NULL));
270 g_ptr_array_add (array: argv, data: g_strdup (str: "--search-path"));
271 g_ptr_array_add (array: argv, data: g_strdup (str: "--set-path-in-envp"));
272 g_ptr_array_add (array: argv, data: g_strdup (str: subdir));
273 g_ptr_array_add (array: argv, data: g_strdup (str: "--"));
274 g_ptr_array_add (array: argv, data: g_strdup (str: "spawn-test-helper"));
275 g_ptr_array_add (array: argv, NULL);
276
277 g_spawn_sync (working_directory: here,
278 argv: (char **) argv->pdata,
279 envp,
280 flags: G_SPAWN_DEFAULT,
281 NULL, /* child setup */
282 NULL, /* user data */
283 standard_output: &out,
284 standard_error: &err,
285 exit_status: &wait_status,
286 error: &error);
287 g_assert_no_error (error);
288
289 g_test_message (format: "%s", out);
290 g_test_message (format: "%s", err);
291 g_assert_nonnull (strstr (err, "this is spawn-test-helper from glib/tests"));
292
293#ifdef G_OS_UNIX
294 g_assert_true (WIFEXITED (wait_status));
295 g_assert_cmpint (WEXITSTATUS (wait_status), ==, 0);
296#endif
297
298 g_strfreev (str_array: envp);
299 g_free (mem: here);
300 g_free (mem: subdir);
301 g_free (mem: out);
302 g_free (mem: err);
303 g_ptr_array_unref (array: argv);
304}
305
306static void
307test_search_path_fallback_in_envp (void)
308{
309 GPtrArray *argv = g_ptr_array_new_with_free_func (element_free_func: g_free);
310 gchar *here = g_test_build_filename (file_type: G_TEST_BUILT, first_path: ".", NULL);
311 gchar *subdir = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "path-test-subdir", NULL);
312 gchar **envp = g_get_environ ();
313 gchar *out = NULL;
314 gchar *err = NULL;
315 GError *error = NULL;
316 int wait_status = -1;
317
318 g_test_summary (summary: "With G_SPAWN_SEARCH_PATH_FROM_ENVP but no PATH, a fallback is used.");
319 /* We can't make a meaningful assertion about what the fallback *is*,
320 * but we can assert that it *includes* the current working directory. */
321
322 if (g_file_test (filename: "/usr/bin/spawn-test-helper", test: G_FILE_TEST_IS_EXECUTABLE) ||
323 g_file_test (filename: "/bin/spawn-test-helper", test: G_FILE_TEST_IS_EXECUTABLE))
324 {
325 g_test_skip (msg: "Not testing fallback with unknown spawn-test-helper "
326 "executable in /usr/bin:/bin");
327 return;
328 }
329
330 envp = g_environ_setenv (envp, variable: "PATH", value: subdir, TRUE);
331
332 g_ptr_array_add (array: argv,
333 data: g_test_build_filename (file_type: G_TEST_BUILT, first_path: "spawn-path-search-helper", NULL));
334 g_ptr_array_add (array: argv, data: g_strdup (str: "--search-path-from-envp"));
335 g_ptr_array_add (array: argv, data: g_strdup (str: "--unset-path-in-envp"));
336 g_ptr_array_add (array: argv, data: g_strdup (str: "--"));
337 g_ptr_array_add (array: argv, data: g_strdup (str: "spawn-test-helper"));
338 g_ptr_array_add (array: argv, NULL);
339
340 g_spawn_sync (working_directory: here,
341 argv: (char **) argv->pdata,
342 envp,
343 flags: G_SPAWN_DEFAULT,
344 NULL, /* child setup */
345 NULL, /* user data */
346 standard_output: &out,
347 standard_error: &err,
348 exit_status: &wait_status,
349 error: &error);
350 g_assert_no_error (error);
351
352 g_test_message (format: "%s", out);
353 g_test_message (format: "%s", err);
354 g_assert_nonnull (strstr (err, "this is spawn-test-helper from glib/tests"));
355
356#ifdef G_OS_UNIX
357 g_assert_true (WIFEXITED (wait_status));
358 g_assert_cmpint (WEXITSTATUS (wait_status), ==, 0);
359#endif
360
361 g_strfreev (str_array: envp);
362 g_free (mem: here);
363 g_free (mem: subdir);
364 g_free (mem: out);
365 g_free (mem: err);
366 g_ptr_array_unref (array: argv);
367}
368
369static void
370test_search_path_heap_allocation (void)
371{
372 GPtrArray *argv = g_ptr_array_new_with_free_func (element_free_func: g_free);
373 /* Must be longer than the arbitrary 4000 byte limit for stack allocation
374 * in gspawn.c */
375 char placeholder[4096];
376 gchar *here = g_test_build_filename (file_type: G_TEST_BUILT, first_path: ".", NULL);
377 gchar *subdir = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "path-test-subdir", NULL);
378 gchar *long_dir = NULL;
379 gchar *long_path = NULL;
380 gchar **envp = g_get_environ ();
381 gchar *out = NULL;
382 gchar *err = NULL;
383 GError *error = NULL;
384 int wait_status = -1;
385 gsize i;
386
387 memset (s: placeholder, c: '_', n: sizeof (placeholder));
388 /* Force search_path_buffer to be heap-allocated */
389 long_dir = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "path-test-subdir", placeholder, NULL);
390 long_path = g_strjoin (G_SEARCHPATH_SEPARATOR_S, subdir, long_dir, NULL);
391 envp = g_environ_setenv (envp, variable: "PATH", value: long_path, TRUE);
392
393 g_ptr_array_add (array: argv,
394 data: g_test_build_filename (file_type: G_TEST_BUILT, first_path: "spawn-path-search-helper", NULL));
395 g_ptr_array_add (array: argv, data: g_strdup (str: "--search-path"));
396 g_ptr_array_add (array: argv, data: g_strdup (str: "--"));
397 g_ptr_array_add (array: argv, data: g_strdup (str: "spawn-test-helper"));
398
399 /* Add enough arguments to make argv longer than the arbitrary 4000 byte
400 * limit for stack allocation in gspawn.c.
401 * This assumes sizeof (char *) >= 4. */
402 for (i = 0; i < 1001; i++)
403 g_ptr_array_add (array: argv, data: g_strdup (str: "_"));
404
405 g_ptr_array_add (array: argv, NULL);
406
407 g_spawn_sync (working_directory: here,
408 argv: (char **) argv->pdata,
409 envp,
410 flags: G_SPAWN_DEFAULT,
411 NULL, /* child setup */
412 NULL, /* user data */
413 standard_output: &out,
414 standard_error: &err,
415 exit_status: &wait_status,
416 error: &error);
417 g_assert_no_error (error);
418
419 g_test_message (format: "%s", out);
420 g_test_message (format: "%s", err);
421 g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
422
423#ifdef G_OS_UNIX
424 g_assert_true (WIFEXITED (wait_status));
425 g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
426#endif
427
428 g_strfreev (str_array: envp);
429 g_free (mem: here);
430 g_free (mem: subdir);
431 g_free (mem: out);
432 g_free (mem: err);
433 g_ptr_array_unref (array: argv);
434}
435
436int
437main (int argc,
438 char **argv)
439{
440 g_test_init (argc: &argc, argv: &argv, NULL);
441
442 g_test_add_func (testpath: "/spawn/do-not-search", test_func: test_do_not_search);
443 g_test_add_func (testpath: "/spawn/search-path", test_func: test_search_path);
444 g_test_add_func (testpath: "/spawn/search-path-from-envp", test_func: test_search_path_from_envp);
445 g_test_add_func (testpath: "/spawn/search-path-ambiguous", test_func: test_search_path_ambiguous);
446 g_test_add_func (testpath: "/spawn/search-path-heap-allocation",
447 test_func: test_search_path_heap_allocation);
448 g_test_add_func (testpath: "/spawn/search-path-fallback-in-environ",
449 test_func: test_search_path_fallback_in_environ);
450 g_test_add_func (testpath: "/spawn/search-path-fallback-in-envp",
451 test_func: test_search_path_fallback_in_envp);
452
453 return g_test_run ();
454}
455

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