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 | |
26 | static void |
27 | test_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 | |
78 | static void |
79 | test_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 | |
131 | static void |
132 | test_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 | |
186 | static void |
187 | test_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 | |
242 | static void |
243 | test_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 | |
306 | static void |
307 | test_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 | |
369 | static void |
370 | test_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 | |
436 | int |
437 | main (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 | |