sh-quote, execute, spawn-pipe, etc.: Make better use of 'const'.
[gnulib.git] / tests / test-execute-main.c
blobc58a62dcae6e63c9ab12952b96b9a1a971e491b7
1 /* Test of execute.
2 Copyright (C) 2020 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 <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
31 #if defined _WIN32 && ! defined __CYGWIN__
32 /* Get _isatty, _getcwd. */
33 # include <io.h>
34 #endif
36 #include "read-file.h"
37 #include "macros.h"
39 /* The name of the "always silent" device. */
40 #if defined _WIN32 && ! defined __CYGWIN__
41 /* Native Windows API. */
42 # define DEV_NULL "NUL"
43 #else
44 /* Unix API. */
45 # define DEV_NULL "/dev/null"
46 #endif
48 #define BASE "test-execute"
50 int
51 main (int argc, char *argv[])
53 if (argc != 3)
55 fprintf (stderr, "%s: need 2 arguments\n", argv[0]);
56 return 2;
58 char *prog_path = argv[1];
59 const char *progname = "test-execute-child";
60 int test = atoi (argv[2]);
61 switch (test)
63 case 0:
65 /* Check an invocation without arguments. Check the exit code. */
66 const char *prog_argv[2] = { prog_path, NULL };
67 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
68 false, false, false, false, true, false, NULL);
69 ASSERT (ret == 40);
71 break;
72 case 1:
74 /* Check an invocation of a non-existent program. */
75 const char *prog_argv[3] = { "./non-existent", NULL };
76 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
77 false, false, false, false, true, false, NULL);
78 ASSERT (ret == 127);
80 break;
81 case 2:
83 /* Check argument passing. */
84 const char *prog_argv[13] =
86 prog_path,
87 "2",
88 "abc def",
89 "abc\"def\"ghi",
90 "xyz\"",
91 "abc\\def\\ghi",
92 "xyz\\",
93 "???",
94 "***",
95 "",
96 "foo",
97 "",
98 NULL
100 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
101 false, false, false, false, true, false, NULL);
102 ASSERT (ret == 0);
104 break;
105 case 3:
106 #if !(defined _WIN32 && !defined __CYGWIN__)
108 /* Check SIGPIPE handling with ignore_sigpipe = false. */
109 const char *prog_argv[3] = { prog_path, "3", NULL };
110 int termsig = 0xDEADBEEF;
111 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
112 false, false, false, false, true, false, &termsig);
113 ASSERT (ret == 127);
114 ASSERT (termsig == SIGPIPE);
116 #endif
117 break;
118 case 4:
119 #if !(defined _WIN32 && !defined __CYGWIN__)
121 /* Check SIGPIPE handling with ignore_sigpipe = true. */
122 const char *prog_argv[3] = { prog_path, "4", NULL };
123 int termsig = 0xDEADBEEF;
124 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
125 true, false, false, false, true, false, &termsig);
126 ASSERT (ret == 0);
127 ASSERT (termsig == SIGPIPE);
129 #endif
130 break;
131 case 5:
133 /* Check other signal. */
134 const char *prog_argv[3] = { prog_path, "5", NULL };
135 int termsig = 0xDEADBEEF;
136 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
137 false, false, false, false, true, false, &termsig);
138 #if defined _WIN32 && !defined __CYGWIN__
139 ASSERT (ret == 3);
140 ASSERT (termsig == 0);
141 #else
142 ASSERT (ret == 127);
143 ASSERT (termsig == SIGINT);
144 #endif
146 break;
147 case 6:
149 /* Check stdin is inherited. */
150 FILE *fp = fopen (BASE ".tmp", "w");
151 fputs ("Foo", fp);
152 ASSERT (fclose (fp) == 0);
154 fp = freopen (BASE ".tmp", "r", stdin);
155 ASSERT (fp != NULL);
157 const char *prog_argv[3] = { prog_path, "6", NULL };
158 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
159 false, false, false, false, true, false, NULL);
160 ASSERT (ret == 0);
162 ASSERT (fclose (stdin) == 0);
163 ASSERT (remove (BASE ".tmp") == 0);
165 break;
166 case 7:
168 /* Check null_stdin = true. */
169 FILE *fp = fopen (BASE ".tmp", "w");
170 fputs ("Foo", fp);
171 ASSERT (fclose (fp) == 0);
173 fp = freopen (BASE ".tmp", "r", stdin);
174 ASSERT (fp != NULL);
176 const char *prog_argv[3] = { prog_path, "7", NULL };
177 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
178 false, true, false, false, true, false, NULL);
179 ASSERT (ret == 0);
181 ASSERT (fclose (stdin) == 0);
182 ASSERT (remove (BASE ".tmp") == 0);
184 break;
185 case 8:
187 /* Check stdout is inherited, part 1 (regular file). */
188 FILE *fp = freopen (BASE ".tmp", "w", stdout);
189 ASSERT (fp != NULL);
191 const char *prog_argv[3] = { prog_path, "8", NULL };
192 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
193 false, false, false, false, true, false, NULL);
194 ASSERT (ret == 0);
196 ASSERT (fclose (stdout) == 0);
198 size_t length;
199 char *contents = read_file (BASE ".tmp", 0, &length);
200 ASSERT (length == 3 && memcmp (contents, "bar", 3) == 0);
202 ASSERT (remove (BASE ".tmp") == 0);
204 break;
205 case 9:
207 /* Check stdout is inherited, part 2 (device). */
208 FILE *fp = freopen (DEV_NULL, "w", stdout);
209 ASSERT (fp != NULL);
211 const char *prog_argv[3] = { prog_path, "9", NULL };
212 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
213 false, false, false, false, true, false, NULL);
214 ASSERT (ret == 0);
216 break;
217 case 10:
219 /* Check null_stdout = true. */
220 FILE *fp = freopen (BASE ".tmp", "w", stdout);
221 ASSERT (fp != NULL);
223 const char *prog_argv[3] = { prog_path, "10", NULL };
224 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
225 false, false, true, false, true, false, NULL);
226 ASSERT (ret == 0);
228 ASSERT (fclose (stdout) == 0);
230 size_t length;
231 (void) read_file (BASE ".tmp", 0, &length);
232 ASSERT (length == 0);
234 ASSERT (remove (BASE ".tmp") == 0);
236 break;
237 case 11:
239 /* Check stderr is inherited, part 1 (regular file). */
240 FILE *fp = freopen (BASE ".tmp", "w", stderr);
241 ASSERT (fp != NULL);
243 const char *prog_argv[3] = { prog_path, "11", NULL };
244 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
245 false, false, false, false, true, false, NULL);
246 ASSERT (ret == 0);
248 ASSERT (fclose (stderr) == 0);
250 size_t length;
251 char *contents = read_file (BASE ".tmp", 0, &length);
252 ASSERT (length == 3 && memcmp (contents, "bar", 3) == 0);
254 ASSERT (remove (BASE ".tmp") == 0);
256 break;
257 case 12:
259 /* Check stderr is inherited, part 2 (device). */
260 FILE *fp = freopen (DEV_NULL, "w", stderr);
261 ASSERT (fp != NULL);
263 const char *prog_argv[3] = { prog_path, "12", NULL };
264 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
265 false, false, false, false, true, false, NULL);
266 ASSERT (ret == 0);
268 break;
269 case 13:
271 /* Check null_stderr = true. */
272 FILE *fp = freopen (BASE ".tmp", "w", stderr);
273 ASSERT (fp != NULL);
275 const char *prog_argv[3] = { prog_path, "13", NULL };
276 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
277 false, false, false, true, true, false, NULL);
278 ASSERT (ret == 0);
280 ASSERT (fclose (stderr) == 0);
282 size_t length;
283 (void) read_file (BASE ".tmp", 0, &length);
284 ASSERT (length == 0);
286 ASSERT (remove (BASE ".tmp") == 0);
288 break;
289 case 14:
291 /* Check file descriptors >= 3 can be inherited. */
292 ASSERT (dup2 (STDOUT_FILENO, 10) >= 0);
293 const char *prog_argv[3] = { prog_path, "14", NULL };
294 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
295 true, false, false, false, true, false, NULL);
296 ASSERT (ret == 0);
298 break;
299 case 15:
301 /* Check file descriptors >= 3 can be inherited. */
302 ASSERT (fcntl (STDOUT_FILENO, F_DUPFD, 10) >= 0);
303 const char *prog_argv[3] = { prog_path, "15", NULL };
304 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
305 true, false, false, false, true, false, NULL);
306 ASSERT (ret == 0);
308 break;
309 case 16:
311 /* Check file descriptors >= 3 with O_CLOEXEC bit are not inherited. */
312 ASSERT (fcntl (STDOUT_FILENO, F_DUPFD_CLOEXEC, 10) >= 0);
313 const char *prog_argv[3] = { prog_path, "16", NULL };
314 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
315 true, false, false, false, true, false, NULL);
316 ASSERT (ret == 0);
318 break;
319 case 17:
321 /* Check that file descriptors >= 3, open for reading, can be inherited,
322 including the file position. */
323 FILE *fp = fopen (BASE ".tmp", "w");
324 fputs ("Foobar", fp);
325 ASSERT (fclose (fp) == 0);
327 int fd = open (BASE ".tmp", O_RDONLY);
328 ASSERT (fd >= 0 && fd < 10);
330 ASSERT (dup2 (fd, 10) >= 0);
331 close (fd);
332 fd = 10;
334 char buf[2];
335 ASSERT (read (fd, buf, sizeof (buf)) == sizeof (buf));
336 /* The file position is now 2. */
338 const char *prog_argv[3] = { prog_path, "17", NULL };
339 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
340 false, false, false, false, true, false, NULL);
341 ASSERT (ret == 0);
343 close (fd);
344 ASSERT (remove (BASE ".tmp") == 0);
346 break;
347 case 18:
349 /* Check that file descriptors >= 3, open for writing, can be inherited,
350 including the file position. */
351 remove (BASE ".tmp");
352 int fd = open (BASE ".tmp", O_RDWR | O_CREAT | O_TRUNC, 0600);
353 ASSERT (fd >= 0 && fd < 10);
355 ASSERT (dup2 (fd, 10) >= 0);
356 close (fd);
357 fd = 10;
359 ASSERT (write (fd, "Foo", 3) == 3);
360 /* The file position is now 3. */
362 const char *prog_argv[3] = { prog_path, "18", NULL };
363 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
364 false, false, false, false, true, false, NULL);
365 ASSERT (ret == 0);
367 close (fd);
369 size_t length;
370 char *contents = read_file (BASE ".tmp", 0, &length);
371 ASSERT (length == 6 && memcmp (contents, "Foobar", 6) == 0);
373 ASSERT (remove (BASE ".tmp") == 0);
375 break;
376 case 19:
378 /* Check that file descriptors >= 3, when inherited, preserve their
379 isatty() property, part 1 (regular file). */
380 FILE *fp = fopen (BASE ".tmp", "w");
381 fputs ("Foo", fp);
382 ASSERT (fclose (fp) == 0);
384 int fd_in = open (BASE ".tmp", O_RDONLY);
385 ASSERT (fd_in >= 0 && fd_in < 10);
387 int fd_out = open (BASE ".tmp", O_WRONLY | O_APPEND);
388 ASSERT (fd_out >= 0 && fd_out < 10);
390 ASSERT (dup2 (fd_in, 10) >= 0);
391 close (fd_in);
392 fd_in = 10;
394 ASSERT (dup2 (fd_out, 11) >= 0);
395 close (fd_out);
396 fd_out = 11;
398 const char *prog_argv[3] = { prog_path, "19", NULL };
399 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
400 false, false, false, false, true, false, NULL);
401 #if defined _WIN32 && ! defined __CYGWIN__
402 ASSERT (ret == 4 + 2 * (_isatty (10) != 0) + (_isatty (11) != 0));
403 #else
404 ASSERT (ret == 4 + 2 * (isatty (10) != 0) + (isatty (11) != 0));
405 #endif
407 close (fd_in);
408 close (fd_out);
409 ASSERT (remove (BASE ".tmp") == 0);
411 break;
412 case 20:
414 /* Check that file descriptors >= 3, when inherited, preserve their
415 isatty() property, part 2 (character devices). */
416 ASSERT (dup2 (STDIN_FILENO, 10) >= 0);
417 int fd_in = 10;
419 ASSERT (dup2 (STDOUT_FILENO, 11) >= 0);
420 int fd_out = 11;
422 const char *prog_argv[3] = { prog_path, "20", NULL };
423 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
424 false, false, false, false, true, false, NULL);
425 #if defined _WIN32 && ! defined __CYGWIN__
426 ASSERT (ret == 4 + 2 * (_isatty (10) != 0) + (_isatty (11) != 0));
427 #else
428 ASSERT (ret == 4 + 2 * (isatty (10) != 0) + (isatty (11) != 0));
429 #endif
431 close (fd_in);
432 close (fd_out);
434 break;
435 case 21:
437 /* Check execution in a different directory. */
438 rmdir (BASE ".sub");
439 ASSERT (mkdir (BASE ".sub", 0700) == 0);
441 char cwd[1024];
442 #if defined _WIN32 && ! defined __CYGWIN__
443 ASSERT (_getcwd (cwd, sizeof (cwd)) != NULL);
444 #else
445 ASSERT (getcwd (cwd, sizeof (cwd)) != NULL);
446 #endif
448 const char *prog_argv[4] = { prog_path, "21", cwd, NULL };
449 int ret = execute (progname, prog_argv[0], prog_argv, BASE ".sub",
450 false, false, false, false, true, false, NULL);
451 ASSERT (ret == 0);
453 ASSERT (rmdir (BASE ".sub") == 0);
455 break;
456 default:
457 ASSERT (false);
459 return 0;