2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3, or (at your option)
9 This program 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
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
30 #if defined _WIN32 && ! defined __CYGWIN__
31 /* Get _isatty, _getcwd. */
35 #include "read-file.h"
38 /* The name of the "always silent" device. */
39 #if defined _WIN32 && ! defined __CYGWIN__
40 /* Native Windows API. */
41 # define DEV_NULL "NUL"
44 # define DEV_NULL "/dev/null"
47 #define BASE "test-execute"
50 main (int argc
, char *argv
[])
54 fprintf (stderr
, "%s: need 2 arguments\n", argv
[0]);
57 char *prog_path
= argv
[1];
58 const char *progname
= "test-execute-child";
59 int test
= atoi (argv
[2]);
61 /* When this test is executed through 'make' (GNU make 4.4) and
62 build-aux/test-driver, i.e. through
63 make check TESTS=test-execute.sh
65 rm -f test-execute.sh.log; make test-execute.sh.log
66 the signal handler for SIGPIPE is set to SIG_IGN. This is a bug in
67 GNU make 4.4: <https://savannah.gnu.org/bugs/index.php?63307>.
68 It causes the tests 3 and 4 to fail. Work around it by resetting
69 the signal handler for SIGPIPE to the default. */
71 signal (SIGPIPE
, SIG_DFL
);
79 /* Close file descriptors that have been inherited from the parent
80 process and that would cause failures in test-execute-child.c.
81 Such file descriptors have been seen:
82 - with GNU make, when invoked as 'make -j N' with j > 1,
83 - in some versions of the KDE desktop environment,
85 - in MacPorts with the "trace mode" enabled,
89 if (close_range (3, 20 - 1, 0) < 0)
93 for (fd
= 3; fd
< 20; fd
++)
105 /* Check an invocation without arguments. Check the exit code. */
106 const char *prog_argv
[2] = { prog_path
, NULL
};
107 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
108 false, false, false, false, true, false, NULL
);
114 /* Check an invocation of a nonexistent program. */
115 const char *prog_argv
[3] = { "./nonexistent", NULL
};
116 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
117 false, false, false, false, true, false, NULL
);
123 /* Check argument passing. */
124 const char *prog_argv
[13] =
140 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
141 false, false, false, false, true, false, NULL
);
146 #if !(defined _WIN32 && !defined __CYGWIN__)
148 /* Check SIGPIPE handling with ignore_sigpipe = false. */
149 const char *prog_argv
[3] = { prog_path
, "3", NULL
};
150 int termsig
= 0x7DEADBEE;
151 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
152 false, false, false, false, true, false, &termsig
);
154 ASSERT (termsig
== SIGPIPE
);
159 #if !(defined _WIN32 && !defined __CYGWIN__)
161 /* Check SIGPIPE handling with ignore_sigpipe = true. */
162 const char *prog_argv
[3] = { prog_path
, "4", NULL
};
163 int termsig
= 0x7DEADBEE;
164 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
165 true, false, false, false, true, false, &termsig
);
167 ASSERT (termsig
== SIGPIPE
);
173 /* Check other signal. */
174 const char *prog_argv
[3] = { prog_path
, "5", NULL
};
175 int termsig
= 0x7DEADBEE;
176 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
177 false, false, false, false, true, false, &termsig
);
179 #if defined _WIN32 && !defined __CYGWIN__
180 ASSERT (termsig
== SIGTERM
); /* dummy, from WTERMSIG in <sys/wait.h> */
182 ASSERT (termsig
== SIGINT
);
188 /* Check stdin is inherited. */
189 FILE *fp
= fopen (BASE
".tmp", "w");
191 ASSERT (fclose (fp
) == 0);
193 fp
= freopen (BASE
".tmp", "r", stdin
);
196 const char *prog_argv
[3] = { prog_path
, "6", NULL
};
197 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
198 false, false, false, false, true, false, NULL
);
201 ASSERT (fclose (stdin
) == 0);
202 ASSERT (remove (BASE
".tmp") == 0);
207 /* Check null_stdin = true. */
208 FILE *fp
= fopen (BASE
".tmp", "w");
210 ASSERT (fclose (fp
) == 0);
212 fp
= freopen (BASE
".tmp", "r", stdin
);
215 const char *prog_argv
[3] = { prog_path
, "7", NULL
};
216 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
217 false, true, false, false, true, false, NULL
);
220 ASSERT (fclose (stdin
) == 0);
221 ASSERT (remove (BASE
".tmp") == 0);
226 /* Check stdout is inherited, part 1 (regular file). */
227 FILE *fp
= freopen (BASE
".tmp", "w", stdout
);
230 const char *prog_argv
[3] = { prog_path
, "8", NULL
};
231 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
232 false, false, false, false, true, false, NULL
);
235 ASSERT (fclose (stdout
) == 0);
238 char *contents
= read_file (BASE
".tmp", 0, &length
);
239 ASSERT (length
== 3 && memcmp (contents
, "bar", 3) == 0);
241 ASSERT (remove (BASE
".tmp") == 0);
246 /* Check stdout is inherited, part 2 (device). */
247 FILE *fp
= freopen (DEV_NULL
, "w", stdout
);
250 const char *prog_argv
[3] = { prog_path
, "9", NULL
};
251 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
252 false, false, false, false, true, false, NULL
);
258 /* Check null_stdout = true. */
259 FILE *fp
= freopen (BASE
".tmp", "w", stdout
);
262 const char *prog_argv
[3] = { prog_path
, "10", NULL
};
263 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
264 false, false, true, false, true, false, NULL
);
267 ASSERT (fclose (stdout
) == 0);
270 (void) read_file (BASE
".tmp", 0, &length
);
271 ASSERT (length
== 0);
273 ASSERT (remove (BASE
".tmp") == 0);
278 /* Check stderr is inherited, part 1 (regular file). */
279 FILE *fp
= freopen (BASE
".tmp", "w", stderr
);
282 const char *prog_argv
[3] = { prog_path
, "11", NULL
};
283 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
284 false, false, false, false, true, false, NULL
);
287 ASSERT (fclose (stderr
) == 0);
290 char *contents
= read_file (BASE
".tmp", 0, &length
);
291 ASSERT (length
== 3 && memcmp (contents
, "bar", 3) == 0);
293 ASSERT (remove (BASE
".tmp") == 0);
298 /* Check stderr is inherited, part 2 (device). */
299 FILE *fp
= freopen (DEV_NULL
, "w", stderr
);
302 const char *prog_argv
[3] = { prog_path
, "12", NULL
};
303 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
304 false, false, false, false, true, false, NULL
);
310 /* Check null_stderr = true. */
311 FILE *fp
= freopen (BASE
".tmp", "w", stderr
);
314 const char *prog_argv
[3] = { prog_path
, "13", NULL
};
315 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
316 false, false, false, true, true, false, NULL
);
319 ASSERT (fclose (stderr
) == 0);
322 (void) read_file (BASE
".tmp", 0, &length
);
323 ASSERT (length
== 0);
325 ASSERT (remove (BASE
".tmp") == 0);
330 /* Check file descriptors >= 3 can be inherited. */
331 ASSERT (dup2 (STDOUT_FILENO
, 10) >= 0);
332 const char *prog_argv
[3] = { prog_path
, "14", NULL
};
333 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
334 true, false, false, false, true, false, NULL
);
340 /* Check file descriptors >= 3 can be inherited. */
341 ASSERT (fcntl (STDOUT_FILENO
, F_DUPFD
, 10) >= 0);
342 const char *prog_argv
[3] = { prog_path
, "15", NULL
};
343 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
344 true, false, false, false, true, false, NULL
);
350 /* Check file descriptors >= 3 with O_CLOEXEC bit are not inherited. */
351 ASSERT (fcntl (STDOUT_FILENO
, F_DUPFD_CLOEXEC
, 10) >= 0);
352 const char *prog_argv
[3] = { prog_path
, "16", NULL
};
353 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
354 true, false, false, false, true, false, NULL
);
360 /* Check that file descriptors >= 3, open for reading, can be inherited,
361 including the file position. */
362 FILE *fp
= fopen (BASE
".tmp", "w");
363 fputs ("Foobar", fp
);
364 ASSERT (fclose (fp
) == 0);
366 int fd
= open (BASE
".tmp", O_RDONLY
);
367 ASSERT (fd
>= 0 && fd
< 15);
369 ASSERT (dup2 (fd
, 15) >= 0);
374 ASSERT (read (fd
, buf
, sizeof (buf
)) == sizeof (buf
));
375 /* The file position is now 2. */
377 const char *prog_argv
[3] = { prog_path
, "17", NULL
};
378 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
379 false, false, false, false, true, false, NULL
);
383 ASSERT (remove (BASE
".tmp") == 0);
388 /* Check that file descriptors >= 3, open for writing, can be inherited,
389 including the file position. */
390 remove (BASE
".tmp");
391 int fd
= open (BASE
".tmp", O_RDWR
| O_CREAT
| O_TRUNC
, 0600);
392 ASSERT (fd
>= 0 && fd
< 15);
394 ASSERT (dup2 (fd
, 15) >= 0);
398 ASSERT (write (fd
, "Foo", 3) == 3);
399 /* The file position is now 3. */
401 const char *prog_argv
[3] = { prog_path
, "18", NULL
};
402 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
403 false, false, false, false, true, false, NULL
);
409 char *contents
= read_file (BASE
".tmp", 0, &length
);
410 ASSERT (length
== 6 && memcmp (contents
, "Foobar", 6) == 0);
412 ASSERT (remove (BASE
".tmp") == 0);
417 /* Check that file descriptors >= 3, when inherited, preserve their
418 isatty() property, part 1 (regular file). */
419 FILE *fp
= fopen (BASE
".tmp", "w");
421 ASSERT (fclose (fp
) == 0);
423 int fd_in
= open (BASE
".tmp", O_RDONLY
);
424 ASSERT (fd_in
>= 0 && fd_in
< 15);
426 int fd_out
= open (BASE
".tmp", O_WRONLY
| O_APPEND
);
427 ASSERT (fd_out
>= 0 && fd_out
< 15);
429 ASSERT (dup2 (fd_in
, 15) >= 0);
433 ASSERT (dup2 (fd_out
, 16) >= 0);
437 const char *prog_argv
[3] = { prog_path
, "19", NULL
};
438 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
439 false, false, false, false, true, false, NULL
);
440 #if defined _WIN32 && ! defined __CYGWIN__
441 ASSERT (ret
== 4 + 2 * (_isatty (15) != 0) + (_isatty (16) != 0));
443 ASSERT (ret
== 4 + 2 * (isatty (15) != 0) + (isatty (16) != 0));
448 ASSERT (remove (BASE
".tmp") == 0);
453 /* Check that file descriptors >= 3, when inherited, preserve their
454 isatty() property, part 2 (character devices). */
455 ASSERT (dup2 (STDIN_FILENO
, 15) >= 0);
458 ASSERT (dup2 (STDOUT_FILENO
, 16) >= 0);
461 const char *prog_argv
[3] = { prog_path
, "20", NULL
};
462 int ret
= execute (progname
, prog_argv
[0], prog_argv
, NULL
,
463 false, false, false, false, true, false, NULL
);
464 #if defined _WIN32 && ! defined __CYGWIN__
465 ASSERT (ret
== 4 + 2 * (_isatty (15) != 0) + (_isatty (16) != 0));
467 ASSERT (ret
== 4 + 2 * (isatty (15) != 0) + (isatty (16) != 0));
476 /* Check execution in a different directory. */
478 ASSERT (mkdir (BASE
".sub", 0700) == 0);
481 #if defined _WIN32 && ! defined __CYGWIN__
482 ASSERT (_getcwd (cwd
, sizeof (cwd
)) != NULL
);
484 ASSERT (getcwd (cwd
, sizeof (cwd
)) != NULL
);
487 const char *prog_argv
[4] = { prog_path
, "21", cwd
, NULL
};
488 int ret
= execute (progname
, prog_argv
[0], prog_argv
, BASE
".sub",
489 false, false, false, false, true, false, NULL
);
492 ASSERT (rmdir (BASE
".sub") == 0);
498 return test_exit_status
;