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