Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2011 Red Hat, Inc.
3 : : *
4 : : * SPDX-License-Identifier: LicenseRef-old-glib-tests
5 : : *
6 : : * This work is provided "as is"; redistribution and modification
7 : : * in whole or in part, in any medium, physical or electronic is
8 : : * permitted without restriction.
9 : : *
10 : : * This work is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 : : *
14 : : * In no event shall the authors or contributors be liable for any
15 : : * direct, indirect, incidental, special, exemplary, or consequential
16 : : * damages (including, but not limited to, procurement of substitute
17 : : * goods or services; loss of use, data, or profits; or business
18 : : * interruption) however caused and on any theory of liability, whether
19 : : * in contract, strict liability, or tort (including negligence or
20 : : * otherwise) arising in any way out of the use of this software, even
21 : : * if advised of the possibility of such damage.
22 : : *
23 : : * Author: Colin Walters <walters@verbum.org>
24 : : */
25 : :
26 : : #include "config.h"
27 : :
28 : : #include <glib.h>
29 : : #include <locale.h>
30 : : #include <string.h>
31 : : #include <fcntl.h>
32 : : #include <glib/gstdio.h>
33 : :
34 : : #ifdef G_OS_UNIX
35 : : #include <glib-unix.h>
36 : : #include <sys/types.h>
37 : : #include <sys/socket.h>
38 : : #include <sys/stat.h>
39 : : #include <unistd.h>
40 : : #endif
41 : :
42 : : #ifdef G_OS_WIN32
43 : : #include <winsock2.h>
44 : : #include <io.h>
45 : : #define LINEEND "\r\n"
46 : : #else
47 : : #define LINEEND "\n"
48 : : #endif
49 : :
50 : : /* MinGW builds are likely done using a BASH-style shell, so run the
51 : : * normal script there, as on non-Windows builds, as it is more likely
52 : : * that one will run 'make check' in such shells to test the code
53 : : */
54 : : #if defined (G_OS_WIN32) && defined (_MSC_VER)
55 : : #define SCRIPT_EXT ".bat"
56 : : #else
57 : : #define SCRIPT_EXT
58 : : #endif
59 : :
60 : : static char *echo_prog_path;
61 : : static char *echo_script_path;
62 : :
63 : : typedef struct {
64 : : GMainLoop *loop;
65 : : gboolean child_exited;
66 : : gboolean stdout_done;
67 : : GString *stdout_buf;
68 : : } SpawnAsyncMultithreadedData;
69 : :
70 : : static gboolean
71 : 5 : on_child_exited (GPid pid,
72 : : gint status,
73 : : gpointer datap)
74 : : {
75 : 5 : SpawnAsyncMultithreadedData *data = datap;
76 : :
77 : 5 : data->child_exited = TRUE;
78 [ + - + + ]: 5 : if (data->child_exited && data->stdout_done)
79 : 3 : g_main_loop_quit (data->loop);
80 : :
81 : 5 : return G_SOURCE_REMOVE;
82 : : }
83 : :
84 : : static gboolean
85 : 6 : on_child_stdout (GIOChannel *channel,
86 : : GIOCondition condition,
87 : : gpointer datap)
88 : : {
89 : : char buf[1024];
90 : 6 : GError *error = NULL;
91 : : gsize bytes_read;
92 : 6 : SpawnAsyncMultithreadedData *data = datap;
93 : :
94 [ + + ]: 6 : if (condition & G_IO_IN)
95 : : {
96 : : GIOStatus status;
97 : 3 : status = g_io_channel_read_chars (channel, buf, sizeof (buf), &bytes_read, &error);
98 : 3 : g_assert_no_error (error);
99 [ - + ]: 3 : g_string_append_len (data->stdout_buf, buf, (gssize) bytes_read);
100 [ - + ]: 3 : if (status == G_IO_STATUS_EOF)
101 : 0 : data->stdout_done = TRUE;
102 : : }
103 [ + + ]: 6 : if (condition & G_IO_HUP)
104 : 3 : data->stdout_done = TRUE;
105 [ - + ]: 6 : if (condition & G_IO_ERR)
106 : 0 : g_error ("Error reading from child stdin");
107 : :
108 [ + + + - ]: 6 : if (data->child_exited && data->stdout_done)
109 : 2 : g_main_loop_quit (data->loop);
110 : :
111 : 6 : return !data->stdout_done;
112 : : }
113 : :
114 : : static void
115 : 1 : test_spawn_async (void)
116 : : {
117 : 1 : int tnum = 1;
118 : 1 : GError *error = NULL;
119 : : GPtrArray *argv;
120 : : char *arg;
121 : : GPid pid;
122 : : GMainContext *context;
123 : : GMainLoop *loop;
124 : : GIOChannel *channel;
125 : : GSource *source;
126 : : int child_stdout_fd;
127 : : SpawnAsyncMultithreadedData data;
128 : :
129 : 1 : context = g_main_context_new ();
130 : 1 : loop = g_main_loop_new (context, TRUE);
131 : :
132 : 1 : arg = g_strdup_printf ("thread %d", tnum);
133 : :
134 : 1 : argv = g_ptr_array_new ();
135 : 1 : g_ptr_array_add (argv, echo_prog_path);
136 : 1 : g_ptr_array_add (argv, arg);
137 : 1 : g_ptr_array_add (argv, NULL);
138 : :
139 : 1 : g_spawn_async_with_pipes (NULL, (char**)argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL,
140 : : &child_stdout_fd, NULL, &error);
141 : 1 : g_assert_no_error (error);
142 : 1 : g_ptr_array_free (argv, TRUE);
143 : :
144 : 1 : data.loop = loop;
145 : 1 : data.stdout_done = FALSE;
146 : 1 : data.child_exited = FALSE;
147 : 1 : data.stdout_buf = g_string_new (0);
148 : :
149 : 1 : source = g_child_watch_source_new (pid);
150 : 1 : g_source_set_callback (source, (GSourceFunc)on_child_exited, &data, NULL);
151 : 1 : g_source_attach (source, context);
152 : 1 : g_source_unref (source);
153 : :
154 : 1 : channel = g_io_channel_unix_new (child_stdout_fd);
155 : 1 : source = g_io_create_watch (channel, G_IO_IN | G_IO_HUP | G_IO_ERR);
156 : 1 : g_source_set_callback (source, (GSourceFunc)on_child_stdout, &data, NULL);
157 : 1 : g_source_attach (source, context);
158 : 1 : g_source_unref (source);
159 : :
160 : 1 : g_main_loop_run (loop);
161 : :
162 : 1 : g_assert (data.child_exited);
163 : 1 : g_assert (data.stdout_done);
164 : 1 : g_assert_cmpstr (data.stdout_buf->str, ==, arg);
165 : 1 : g_string_free (data.stdout_buf, TRUE);
166 : :
167 : 1 : g_io_channel_unref (channel);
168 : 1 : g_main_context_unref (context);
169 : 1 : g_main_loop_unref (loop);
170 : :
171 : 1 : g_free (arg);
172 : 1 : }
173 : :
174 : : /* Windows close() causes failure through the Invalid Parameter Handler
175 : : * Routine if the file descriptor does not exist.
176 : : */
177 : : static void
178 : 22 : safe_close (int fd)
179 : : {
180 [ + + ]: 22 : if (fd >= 0)
181 : 8 : close (fd);
182 : 22 : }
183 : :
184 : : /* Test g_spawn_async_with_fds() with a variety of different inputs */
185 : : static void
186 : 1 : test_spawn_async_with_fds (void)
187 : : {
188 : 1 : int tnum = 1;
189 : : GPtrArray *argv;
190 : : char *arg;
191 : : gsize i;
192 : :
193 : : /* Each test has 3 variable parameters: stdin, stdout, stderr */
194 : : enum fd_type {
195 : : NO_FD, /* pass fd -1 (unset) */
196 : : FD_NEGATIVE, /* pass fd of negative value (equivalent to unset) */
197 : : PIPE, /* pass fd of new/unique pipe */
198 : : STDOUT_PIPE, /* pass the same pipe as stdout */
199 : 1 : } tests[][3] = {
200 : : { NO_FD, NO_FD, NO_FD }, /* Test with no fds passed */
201 : : { NO_FD, FD_NEGATIVE, NO_FD }, /* Test another negative fd value */
202 : : { PIPE, PIPE, PIPE }, /* Test with unique fds passed */
203 : : { NO_FD, PIPE, STDOUT_PIPE }, /* Test the same fd for stdout + stderr */
204 : : };
205 : :
206 : 1 : arg = g_strdup_printf ("# thread %d\n", tnum);
207 : :
208 : 1 : argv = g_ptr_array_new ();
209 : 1 : g_ptr_array_add (argv, echo_prog_path);
210 : 1 : g_ptr_array_add (argv, arg);
211 : 1 : g_ptr_array_add (argv, NULL);
212 : :
213 [ + + ]: 5 : for (i = 0; i < G_N_ELEMENTS (tests); i++)
214 : : {
215 : 4 : GError *error = NULL;
216 : : GPid pid;
217 : : GMainContext *context;
218 : : GMainLoop *loop;
219 : 4 : GIOChannel *channel = NULL;
220 : : GSource *source;
221 : : SpawnAsyncMultithreadedData data;
222 : 4 : enum fd_type *fd_info = tests[i];
223 : : gint test_pipe[3][2];
224 : : int j;
225 : :
226 [ + + ]: 16 : for (j = 0; j < 3; j++)
227 : : {
228 [ + + + + : 12 : switch (fd_info[j])
- ]
229 : : {
230 : 6 : case NO_FD:
231 : 6 : test_pipe[j][0] = -1;
232 : 6 : test_pipe[j][1] = -1;
233 : 6 : break;
234 : 1 : case FD_NEGATIVE:
235 : 1 : test_pipe[j][0] = -5;
236 : 1 : test_pipe[j][1] = -5;
237 : 1 : break;
238 : 4 : case PIPE:
239 : : #ifdef G_OS_UNIX
240 : 4 : g_unix_open_pipe (test_pipe[j], O_CLOEXEC, &error);
241 : 4 : g_assert_no_error (error);
242 : : #else
243 : : g_assert_cmpint (_pipe (test_pipe[j], 4096, _O_BINARY), >=, 0);
244 : : #endif
245 : 4 : break;
246 : 1 : case STDOUT_PIPE:
247 : 1 : g_assert_cmpint (j, ==, 2); /* only works for stderr */
248 : 1 : test_pipe[j][0] = test_pipe[1][0];
249 : 1 : test_pipe[j][1] = test_pipe[1][1];
250 : 1 : break;
251 : 0 : default:
252 : : g_assert_not_reached ();
253 : : }
254 : : }
255 : :
256 : 4 : context = g_main_context_new ();
257 : 4 : loop = g_main_loop_new (context, TRUE);
258 : :
259 : 4 : g_spawn_async_with_fds (NULL, (char**)argv->pdata, NULL,
260 : : G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid,
261 : : test_pipe[0][0], test_pipe[1][1], test_pipe[2][1],
262 : : &error);
263 : 4 : g_assert_no_error (error);
264 : 4 : safe_close (test_pipe[0][0]);
265 : 4 : safe_close (test_pipe[1][1]);
266 [ + + ]: 4 : if (fd_info[2] != STDOUT_PIPE)
267 : 3 : safe_close (test_pipe[2][1]);
268 : :
269 : 4 : data.loop = loop;
270 : 4 : data.stdout_done = FALSE;
271 : 4 : data.child_exited = FALSE;
272 : 4 : data.stdout_buf = g_string_new (0);
273 : :
274 : 4 : source = g_child_watch_source_new (pid);
275 : 4 : g_source_set_callback (source, (GSourceFunc)on_child_exited, &data, NULL);
276 : 4 : g_source_attach (source, context);
277 : 4 : g_source_unref (source);
278 : :
279 [ + + ]: 4 : if (test_pipe[1][0] >= 0)
280 : : {
281 : 2 : channel = g_io_channel_unix_new (test_pipe[1][0]);
282 : 2 : source = g_io_create_watch (channel, G_IO_IN | G_IO_HUP | G_IO_ERR);
283 : 2 : g_source_set_callback (source, (GSourceFunc)on_child_stdout,
284 : : &data, NULL);
285 : 2 : g_source_attach (source, context);
286 : 2 : g_source_unref (source);
287 : : }
288 : : else
289 : : {
290 : : /* Don't check stdout data if we didn't pass a fd */
291 : 2 : data.stdout_done = TRUE;
292 : : }
293 : :
294 : 4 : g_main_loop_run (loop);
295 : :
296 : 4 : g_assert_true (data.child_exited);
297 : :
298 [ + + ]: 4 : if (test_pipe[1][0] >= 0)
299 : : {
300 : 2 : gchar *tmp = g_strdup_printf ("# thread %d" LINEEND, tnum);
301 : : /* Check for echo on stdout */
302 : 2 : g_assert_true (data.stdout_done);
303 : 2 : g_assert_cmpstr (data.stdout_buf->str, ==, tmp);
304 : 2 : g_io_channel_unref (channel);
305 : 2 : g_free (tmp);
306 : : }
307 : 4 : g_string_free (data.stdout_buf, TRUE);
308 : :
309 : 4 : g_main_context_unref (context);
310 : 4 : g_main_loop_unref (loop);
311 : 4 : safe_close (test_pipe[0][1]);
312 : 4 : safe_close (test_pipe[1][0]);
313 [ + + ]: 4 : if (fd_info[2] != STDOUT_PIPE)
314 : 3 : safe_close (test_pipe[2][0]);
315 : : }
316 : :
317 : 1 : g_ptr_array_free (argv, TRUE);
318 : 1 : g_free (arg);
319 : 1 : }
320 : :
321 : : static void
322 : 1 : test_spawn_async_with_invalid_fds (void)
323 : : {
324 : 1 : const gchar *argv[] = { echo_prog_path, "thread 0", NULL };
325 : : gint source_fds[1000];
326 : 1 : GError *local_error = NULL;
327 : : gboolean retval;
328 : : gsize i;
329 : :
330 : : /* Create an identity mapping from [0, …, 999]. This is very likely going to
331 : : * conflict with the internal FDs, as it covers a lot of the FD space
332 : : * (including stdin, stdout and stderr, though we don’t care about them in
333 : : * this test).
334 : : *
335 : : * Skip the test if we somehow avoid a collision. */
336 [ + + ]: 1001 : for (i = 0; i < G_N_ELEMENTS (source_fds); i++)
337 : 1000 : source_fds[i] = i;
338 : :
339 : 1 : retval = g_spawn_async_with_pipes_and_fds (NULL, argv, NULL, G_SPAWN_DEFAULT,
340 : : NULL, NULL, -1, -1, -1,
341 : : source_fds, source_fds, G_N_ELEMENTS (source_fds),
342 : : NULL, NULL, NULL, NULL,
343 : : &local_error);
344 [ - + ]: 1 : if (retval)
345 : : {
346 : 0 : g_test_skip ("Skipping internal FDs check as test didn’t manage to trigger a collision");
347 : 0 : return;
348 : : }
349 : 1 : g_assert_false (retval);
350 : 1 : g_assert_error (local_error, G_SPAWN_ERROR, G_SPAWN_ERROR_INVAL);
351 : 1 : g_error_free (local_error);
352 : : }
353 : :
354 : : static void
355 : 1 : test_spawn_sync (void)
356 : : {
357 : 1 : int tnum = 1;
358 : 1 : GError *error = NULL;
359 : 1 : char *arg = g_strdup_printf ("thread %d", tnum);
360 : : /* Include arguments with special symbols to test that they are correctly passed to child.
361 : : * This is tested on all platforms, but the most prone to failure is win32,
362 : : * where args are specially escaped during spawning.
363 : : */
364 : 1 : const char * const argv[] = {
365 : : echo_prog_path,
366 : : arg,
367 : : "doublequotes\\\"after\\\\\"\"backslashes", /* this would be special escaped on win32 */
368 : : "\\\"\"doublequotes spaced after backslashes\\\\\"", /* this would be special escaped on win32 */
369 : : "even$$dollars",
370 : : "even%%percents",
371 : : "even\"\"doublequotes",
372 : : "even''singlequotes",
373 : : "even\\\\backslashes",
374 : : "even//slashes",
375 : : "$odd spaced$dollars$",
376 : : "%odd spaced%spercents%",
377 : : "\"odd spaced\"doublequotes\"",
378 : : "'odd spaced'singlequotes'",
379 : : "\\odd spaced\\backslashes\\", /* this wasn't handled correctly on win32 in glib <=2.58 */
380 : : "/odd spaced/slashes/",
381 : : NULL
382 : : };
383 : 1 : char *joined_args_str = g_strjoinv ("", (char**)argv + 1);
384 : : char *stdout_str;
385 : : int estatus;
386 : :
387 : 1 : g_spawn_sync (NULL, (char**)argv, NULL, 0, NULL, NULL, &stdout_str, NULL, &estatus, &error);
388 : 1 : g_assert_no_error (error);
389 : 1 : g_assert_cmpstr (joined_args_str, ==, stdout_str);
390 : 1 : g_free (arg);
391 : 1 : g_free (stdout_str);
392 : 1 : g_free (joined_args_str);
393 : 1 : }
394 : :
395 : : static void
396 : 1 : init_networking (void)
397 : : {
398 : : #ifdef G_OS_WIN32
399 : : WSADATA wsadata;
400 : :
401 : : if (WSAStartup (MAKEWORD (2, 0), &wsadata) != 0)
402 : : g_error ("Windows Sockets could not be initialized");
403 : : #endif
404 : 1 : }
405 : :
406 : : static void
407 : 2 : test_spawn_stderr_socket (void)
408 : : {
409 : 2 : GError *error = NULL;
410 : : GPtrArray *argv;
411 : : int estatus;
412 : : int fd;
413 : :
414 : 2 : g_test_summary ("Test calling g_spawn_sync() with its stderr FD set to a socket");
415 : :
416 [ + + ]: 2 : if (g_test_subprocess ())
417 : : {
418 : 1 : init_networking ();
419 : 1 : fd = socket (AF_INET, SOCK_STREAM, 0);
420 : 1 : g_assert_cmpint (fd, >=, 0);
421 : : #ifdef G_OS_WIN32
422 : : fd = _open_osfhandle (fd, 0);
423 : : g_assert_cmpint (fd, >=, 0);
424 : : #endif
425 : : /* Set the socket as FD 2, stderr */
426 : 1 : estatus = dup2 (fd, 2);
427 : 1 : g_assert_cmpint (estatus, >=, 0);
428 : :
429 : 1 : argv = g_ptr_array_new ();
430 : 1 : g_ptr_array_add (argv, echo_script_path);
431 : 1 : g_ptr_array_add (argv, NULL);
432 : :
433 : 1 : g_spawn_sync (NULL, (char**) argv->pdata, NULL, 0, NULL, NULL, NULL, NULL, NULL, &error);
434 : 1 : g_assert_no_error (error);
435 : 1 : g_ptr_array_free (argv, TRUE);
436 : 1 : g_close (fd, &error);
437 : 1 : g_assert_no_error (error);
438 : 1 : return;
439 : : }
440 : :
441 : 1 : g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
442 : 1 : g_test_trap_assert_passed ();
443 : : }
444 : :
445 : : /* Like test_spawn_sync but uses spawn flags that trigger the optimized
446 : : * posix_spawn codepath.
447 : : */
448 : : static void
449 : 1 : test_posix_spawn (void)
450 : : {
451 : 1 : int tnum = 1;
452 : 1 : GError *error = NULL;
453 : : GPtrArray *argv;
454 : : char *arg;
455 : : char *stdout_str;
456 : : int estatus;
457 : 1 : GSpawnFlags flags = G_SPAWN_CLOEXEC_PIPES | G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
458 : :
459 : 1 : arg = g_strdup_printf ("thread %d", tnum);
460 : :
461 : 1 : argv = g_ptr_array_new ();
462 : 1 : g_ptr_array_add (argv, echo_prog_path);
463 : 1 : g_ptr_array_add (argv, arg);
464 : 1 : g_ptr_array_add (argv, NULL);
465 : :
466 : 1 : g_spawn_sync (NULL, (char**)argv->pdata, NULL, flags, NULL, NULL, &stdout_str, NULL, &estatus, &error);
467 : 1 : g_assert_no_error (error);
468 : 1 : g_assert_cmpstr (arg, ==, stdout_str);
469 : 1 : g_free (arg);
470 : 1 : g_free (stdout_str);
471 : 1 : g_ptr_array_free (argv, TRUE);
472 : 1 : }
473 : :
474 : : static void
475 : 1 : test_spawn_script (void)
476 : : {
477 : 1 : GError *error = NULL;
478 : : GPtrArray *argv;
479 : : char *stdout_str;
480 : : int estatus;
481 : :
482 : 1 : argv = g_ptr_array_new ();
483 : 1 : g_ptr_array_add (argv, echo_script_path);
484 : 1 : g_ptr_array_add (argv, NULL);
485 : :
486 : 1 : g_spawn_sync (NULL, (char**)argv->pdata, NULL, 0, NULL, NULL, &stdout_str, NULL, &estatus, &error);
487 : 1 : g_assert_no_error (error);
488 : 1 : g_assert_cmpstr ("echo" LINEEND, ==, stdout_str);
489 : 1 : g_free (stdout_str);
490 : 1 : g_ptr_array_free (argv, TRUE);
491 : 1 : }
492 : :
493 : : /* Test that spawning a non-existent executable returns %G_SPAWN_ERROR_NOENT. */
494 : : static void
495 : 1 : test_spawn_nonexistent (void)
496 : : {
497 : 1 : GError *error = NULL;
498 : 1 : GPtrArray *argv = NULL;
499 : 1 : gchar *stdout_str = NULL;
500 : 1 : gint wait_status = -1;
501 : :
502 : 1 : argv = g_ptr_array_new ();
503 : 1 : g_ptr_array_add (argv, "this does not exist");
504 : 1 : g_ptr_array_add (argv, NULL);
505 : :
506 : 1 : g_spawn_sync (NULL, (char**) argv->pdata, NULL, 0, NULL, NULL, &stdout_str,
507 : : NULL, &wait_status, &error);
508 : 1 : g_assert_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT);
509 : 1 : g_assert_null (stdout_str);
510 : 1 : g_assert_cmpint (wait_status, ==, -1);
511 : :
512 : 1 : g_ptr_array_free (argv, TRUE);
513 : :
514 : 1 : g_clear_error (&error);
515 : 1 : }
516 : :
517 : : /* Test that FD assignments in a spawned process don’t overwrite and break the
518 : : * child_err_report_fd which is used to report error information back from the
519 : : * intermediate child process to the parent.
520 : : *
521 : : * https://gitlab.gnome.org/GNOME/glib/-/issues/2097 */
522 : : static void
523 : 1 : test_spawn_fd_assignment_clash (void)
524 : : {
525 : : int tmp_fd;
526 : : guint i;
527 : : #define N_FDS 10
528 : : gint source_fds[N_FDS];
529 : : gint target_fds[N_FDS];
530 : 1 : const gchar *argv[] = { "/nonexistent", NULL };
531 : : gboolean retval;
532 : 1 : GError *local_error = NULL;
533 : : struct stat statbuf;
534 : :
535 : : /* Open a temporary file and duplicate its FD several times so we have several
536 : : * FDs to remap in the child process. */
537 : 1 : tmp_fd = g_file_open_tmp ("glib-spawn-test-XXXXXX", NULL, NULL);
538 : 1 : g_assert_cmpint (tmp_fd, >=, 0);
539 : :
540 [ + + ]: 10 : for (i = 0; i < (N_FDS - 1); ++i)
541 : : {
542 : : int source;
543 : : #ifdef F_DUPFD_CLOEXEC
544 : 9 : source = fcntl (tmp_fd, F_DUPFD_CLOEXEC, 3);
545 : : #else
546 : : source = dup (tmp_fd);
547 : : #endif
548 : 9 : g_assert_cmpint (source, >=, 0);
549 : 9 : source_fds[i] = source;
550 : 9 : target_fds[i] = source + N_FDS;
551 : : }
552 : :
553 : 1 : source_fds[i] = tmp_fd;
554 : 1 : target_fds[i] = tmp_fd + N_FDS;
555 : :
556 : : /* Print out the FD map. */
557 : 1 : g_test_message ("FD map:");
558 [ + + ]: 11 : for (i = 0; i < N_FDS; i++)
559 : 10 : g_test_message (" • %d → %d", source_fds[i], target_fds[i]);
560 : :
561 : : /* Spawn the subprocess. This should fail because the executable doesn’t
562 : : * exist. */
563 : 1 : retval = g_spawn_async_with_pipes_and_fds (NULL, argv, NULL, G_SPAWN_DEFAULT,
564 : : NULL, NULL, -1, -1, -1,
565 : : source_fds, target_fds, N_FDS,
566 : : NULL, NULL, NULL, NULL,
567 : : &local_error);
568 : 1 : g_assert_error (local_error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT);
569 : 1 : g_assert_false (retval);
570 : :
571 : 1 : g_clear_error (&local_error);
572 : :
573 : : /* Check nothing was written to the temporary file, as would happen if the FD
574 : : * mapping was messed up to conflict with the child process error reporting FD.
575 : : * See https://gitlab.gnome.org/GNOME/glib/-/issues/2097 */
576 : 1 : g_assert_no_errno (fstat (tmp_fd, &statbuf));
577 : 1 : g_assert_cmpuint (statbuf.st_size, ==, 0);
578 : :
579 : : /* Clean up. */
580 [ + + ]: 11 : for (i = 0; i < N_FDS; i++)
581 : 10 : g_close (source_fds[i], NULL);
582 : 1 : }
583 : :
584 : : int
585 : 2 : main (int argc,
586 : : char *argv[])
587 : : {
588 : : char *dirname;
589 : : int ret;
590 : :
591 : 2 : setlocale (LC_ALL, "");
592 : :
593 : 2 : g_test_init (&argc, &argv, NULL);
594 : :
595 : 2 : dirname = g_path_get_dirname (argv[0]);
596 : 2 : echo_prog_path = g_build_filename (dirname, "test-spawn-echo" EXEEXT, NULL);
597 : 2 : echo_script_path = g_build_filename (dirname, "echo-script" SCRIPT_EXT, NULL);
598 [ + - ]: 2 : if (!g_file_test (echo_script_path, G_FILE_TEST_EXISTS))
599 : : {
600 : 2 : g_free (echo_script_path);
601 : 2 : echo_script_path = g_test_build_filename (G_TEST_DIST, "echo-script" SCRIPT_EXT, NULL);
602 : : }
603 : 2 : g_free (dirname);
604 : :
605 : 2 : g_assert (g_file_test (echo_prog_path, G_FILE_TEST_EXISTS));
606 : 2 : g_assert (g_file_test (echo_script_path, G_FILE_TEST_EXISTS));
607 : :
608 : 2 : g_test_add_func ("/gthread/spawn-single-sync", test_spawn_sync);
609 : 2 : g_test_add_func ("/gthread/spawn-stderr-socket", test_spawn_stderr_socket);
610 : 2 : g_test_add_func ("/gthread/spawn-single-async", test_spawn_async);
611 : 2 : g_test_add_func ("/gthread/spawn-single-async-with-fds", test_spawn_async_with_fds);
612 : 2 : g_test_add_func ("/gthread/spawn-async-with-invalid-fds", test_spawn_async_with_invalid_fds);
613 : 2 : g_test_add_func ("/gthread/spawn-script", test_spawn_script);
614 : 2 : g_test_add_func ("/gthread/spawn/nonexistent", test_spawn_nonexistent);
615 : 2 : g_test_add_func ("/gthread/spawn-posix-spawn", test_posix_spawn);
616 : 2 : g_test_add_func ("/gthread/spawn/fd-assignment-clash", test_spawn_fd_assignment_clash);
617 : :
618 : 2 : ret = g_test_run();
619 : :
620 : 2 : g_free (echo_script_path);
621 : 2 : g_free (echo_prog_path);
622 : :
623 : 2 : return ret;
624 : : }
|