usleep tests: Avoid failure due to known Cygwin 3.5.3 bug.
[gnulib.git] / tests / test-execute-main.c
blob4703941beae7410ff084257e3acef5d2cd3ccd3b
1 /* Test of execute.
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)
7 any later version.
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/>. */
17 #include <config.h>
19 #include "execute.h"
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
30 #if defined _WIN32 && ! defined __CYGWIN__
31 /* Get _isatty, _getcwd. */
32 # include <io.h>
33 #endif
35 #include "read-file.h"
36 #include "macros.h"
38 /* The name of the "always silent" device. */
39 #if defined _WIN32 && ! defined __CYGWIN__
40 /* Native Windows API. */
41 # define DEV_NULL "NUL"
42 #else
43 /* Unix API. */
44 # define DEV_NULL "/dev/null"
45 #endif
47 #define BASE "test-execute"
49 int
50 main (int argc, char *argv[])
52 if (argc != 3)
54 fprintf (stderr, "%s: need 2 arguments\n", argv[0]);
55 return 2;
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. */
70 #ifdef SIGPIPE
71 signal (SIGPIPE, SIG_DFL);
72 #endif
74 switch (test)
76 case 14:
77 case 15:
78 case 16:
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,
84 - on NetBSD,
85 - in MacPorts with the "trace mode" enabled,
86 - on macOS 14.
88 #if HAVE_CLOSE_RANGE
89 if (close_range (3, 20 - 1, 0) < 0)
90 #endif
92 int fd;
93 for (fd = 3; fd < 20; fd++)
94 close (fd);
96 break;
97 default:
98 break;
101 switch (test)
103 case 0:
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);
109 ASSERT (ret == 40);
111 break;
112 case 1:
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);
118 ASSERT (ret == 127);
120 break;
121 case 2:
123 /* Check argument passing. */
124 const char *prog_argv[13] =
126 prog_path,
127 "2",
128 "abc def",
129 "abc\"def\"ghi",
130 "xyz\"",
131 "abc\\def\\ghi",
132 "xyz\\",
133 "???",
134 "***",
136 "foo",
138 NULL
140 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
141 false, false, false, false, true, false, NULL);
142 ASSERT (ret == 0);
144 break;
145 case 3:
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);
153 ASSERT (ret == 127);
154 ASSERT (termsig == SIGPIPE);
156 #endif
157 break;
158 case 4:
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);
166 ASSERT (ret == 0);
167 ASSERT (termsig == SIGPIPE);
169 #endif
170 break;
171 case 5:
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);
178 ASSERT (ret == 127);
179 #if defined _WIN32 && !defined __CYGWIN__
180 ASSERT (termsig == SIGTERM); /* dummy, from WTERMSIG in <sys/wait.h> */
181 #else
182 ASSERT (termsig == SIGINT);
183 #endif
185 break;
186 case 6:
188 /* Check stdin is inherited. */
189 FILE *fp = fopen (BASE ".tmp", "w");
190 fputs ("Foo", fp);
191 ASSERT (fclose (fp) == 0);
193 fp = freopen (BASE ".tmp", "r", stdin);
194 ASSERT (fp != NULL);
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);
199 ASSERT (ret == 0);
201 ASSERT (fclose (stdin) == 0);
202 ASSERT (remove (BASE ".tmp") == 0);
204 break;
205 case 7:
207 /* Check null_stdin = true. */
208 FILE *fp = fopen (BASE ".tmp", "w");
209 fputs ("Foo", fp);
210 ASSERT (fclose (fp) == 0);
212 fp = freopen (BASE ".tmp", "r", stdin);
213 ASSERT (fp != NULL);
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);
218 ASSERT (ret == 0);
220 ASSERT (fclose (stdin) == 0);
221 ASSERT (remove (BASE ".tmp") == 0);
223 break;
224 case 8:
226 /* Check stdout is inherited, part 1 (regular file). */
227 FILE *fp = freopen (BASE ".tmp", "w", stdout);
228 ASSERT (fp != NULL);
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);
233 ASSERT (ret == 0);
235 ASSERT (fclose (stdout) == 0);
237 size_t length;
238 char *contents = read_file (BASE ".tmp", 0, &length);
239 ASSERT (length == 3 && memcmp (contents, "bar", 3) == 0);
241 ASSERT (remove (BASE ".tmp") == 0);
243 break;
244 case 9:
246 /* Check stdout is inherited, part 2 (device). */
247 FILE *fp = freopen (DEV_NULL, "w", stdout);
248 ASSERT (fp != NULL);
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);
253 ASSERT (ret == 0);
255 break;
256 case 10:
258 /* Check null_stdout = true. */
259 FILE *fp = freopen (BASE ".tmp", "w", stdout);
260 ASSERT (fp != NULL);
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);
265 ASSERT (ret == 0);
267 ASSERT (fclose (stdout) == 0);
269 size_t length;
270 (void) read_file (BASE ".tmp", 0, &length);
271 ASSERT (length == 0);
273 ASSERT (remove (BASE ".tmp") == 0);
275 break;
276 case 11:
278 /* Check stderr is inherited, part 1 (regular file). */
279 FILE *fp = freopen (BASE ".tmp", "w", stderr);
280 ASSERT (fp != NULL);
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);
285 ASSERT (ret == 0);
287 ASSERT (fclose (stderr) == 0);
289 size_t length;
290 char *contents = read_file (BASE ".tmp", 0, &length);
291 ASSERT (length == 3 && memcmp (contents, "bar", 3) == 0);
293 ASSERT (remove (BASE ".tmp") == 0);
295 break;
296 case 12:
298 /* Check stderr is inherited, part 2 (device). */
299 FILE *fp = freopen (DEV_NULL, "w", stderr);
300 ASSERT (fp != NULL);
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);
305 ASSERT (ret == 0);
307 break;
308 case 13:
310 /* Check null_stderr = true. */
311 FILE *fp = freopen (BASE ".tmp", "w", stderr);
312 ASSERT (fp != NULL);
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);
317 ASSERT (ret == 0);
319 ASSERT (fclose (stderr) == 0);
321 size_t length;
322 (void) read_file (BASE ".tmp", 0, &length);
323 ASSERT (length == 0);
325 ASSERT (remove (BASE ".tmp") == 0);
327 break;
328 case 14:
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);
335 ASSERT (ret == 0);
337 break;
338 case 15:
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);
345 ASSERT (ret == 0);
347 break;
348 case 16:
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);
355 ASSERT (ret == 0);
357 break;
358 case 17:
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);
370 close (fd);
371 fd = 15;
373 char buf[2];
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);
380 ASSERT (ret == 0);
382 close (fd);
383 ASSERT (remove (BASE ".tmp") == 0);
385 break;
386 case 18:
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);
395 close (fd);
396 fd = 15;
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);
404 ASSERT (ret == 0);
406 close (fd);
408 size_t length;
409 char *contents = read_file (BASE ".tmp", 0, &length);
410 ASSERT (length == 6 && memcmp (contents, "Foobar", 6) == 0);
412 ASSERT (remove (BASE ".tmp") == 0);
414 break;
415 case 19:
417 /* Check that file descriptors >= 3, when inherited, preserve their
418 isatty() property, part 1 (regular file). */
419 FILE *fp = fopen (BASE ".tmp", "w");
420 fputs ("Foo", fp);
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);
430 close (fd_in);
431 fd_in = 15;
433 ASSERT (dup2 (fd_out, 16) >= 0);
434 close (fd_out);
435 fd_out = 16;
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));
442 #else
443 ASSERT (ret == 4 + 2 * (isatty (15) != 0) + (isatty (16) != 0));
444 #endif
446 close (fd_in);
447 close (fd_out);
448 ASSERT (remove (BASE ".tmp") == 0);
450 break;
451 case 20:
453 /* Check that file descriptors >= 3, when inherited, preserve their
454 isatty() property, part 2 (character devices). */
455 ASSERT (dup2 (STDIN_FILENO, 15) >= 0);
456 int fd_in = 15;
458 ASSERT (dup2 (STDOUT_FILENO, 16) >= 0);
459 int fd_out = 16;
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));
466 #else
467 ASSERT (ret == 4 + 2 * (isatty (15) != 0) + (isatty (16) != 0));
468 #endif
470 close (fd_in);
471 close (fd_out);
473 break;
474 case 21:
476 /* Check execution in a different directory. */
477 rmdir (BASE ".sub");
478 ASSERT (mkdir (BASE ".sub", 0700) == 0);
480 char cwd[1024];
481 #if defined _WIN32 && ! defined __CYGWIN__
482 ASSERT (_getcwd (cwd, sizeof (cwd)) != NULL);
483 #else
484 ASSERT (getcwd (cwd, sizeof (cwd)) != NULL);
485 #endif
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);
490 ASSERT (ret == 0);
492 ASSERT (rmdir (BASE ".sub") == 0);
494 break;
495 default:
496 ASSERT (false);
498 return test_exit_status;