1 /* Test of execute.
2 Copyright (C) 2020-2021 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "execute.h"
20
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>
30
31 #if defined _WIN32 && ! defined __CYGWIN__
32 /* Get _isatty, _getcwd. */
33 # include <io.h>
34 #endif
35
36 #include "read-file.h"
37 #include "macros.h"
38
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
47
48 #define BASE "test-execute"
49
50 int
51 main (int argc, char *argv[])
52 {
53 if (argc != 3)
54 {
55 fprintf (stderr, "%s: need 2 arguments\n", argv[0]);
56 return 2;
57 }
58 char *prog_path = argv[1];
59 const char *progname = "test-execute-child";
60 int test = atoi (argv[2]);
61
62 switch (test)
63 {
64 case 14:
65 case 15:
66 case 16:
67 /* Close file descriptors that have been inherited from the parent
68 process and that would cause failures in test-execute-child.c.
69 Such file descriptors have been seen:
70 - with GNU make, when invoked as 'make -j N' with j > 1,
71 - in some versions of the KDE desktop environment,
72 - on NetBSD,
73 - in MacPorts with the "trace mode" enabled.
74 */
75 #if HAVE_CLOSE_RANGE
76 if (close_range (3, 20 - 1, 0) < 0)
77 #endif
78 {
79 int fd;
80 for (fd = 3; fd < 20; fd++)
81 close (fd);
82 }
83 default:
84 break;
85 }
86
87 switch (test)
88 {
89 case 0:
90 {
91 /* Check an invocation without arguments. Check the exit code. */
92 const char *prog_argv[2] = { prog_path, NULL };
93 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
94 false, false, false, false, true, false, NULL);
95 ASSERT (ret == 40);
96 }
97 break;
98 case 1:
99 {
100 /* Check an invocation of a non-existent program. */
101 const char *prog_argv[3] = { "./non-existent", NULL };
102 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
103 false, false, false, false, true, false, NULL);
104 ASSERT (ret == 127);
105 }
106 break;
107 case 2:
108 {
109 /* Check argument passing. */
110 const char *prog_argv[13] =
111 {
112 prog_path,
113 "2",
114 "abc def",
115 "abc\"def\"ghi",
116 "xyz\"",
117 "abc\\def\\ghi",
118 "xyz\\",
119 "???",
120 "***",
121 "",
122 "foo",
123 "",
124 NULL
125 };
126 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
127 false, false, false, false, true, false, NULL);
128 ASSERT (ret == 0);
129 }
130 break;
131 case 3:
132 #if !(defined _WIN32 && !defined __CYGWIN__)
133 {
134 /* Check SIGPIPE handling with ignore_sigpipe = false. */
135 const char *prog_argv[3] = { prog_path, "3", NULL };
136 int termsig = 0x7DEADBEE;
137 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
138 false, false, false, false, true, false, &termsig);
139 ASSERT (ret == 127);
140 ASSERT (termsig == SIGPIPE);
141 }
142 #endif
143 break;
144 case 4:
145 #if !(defined _WIN32 && !defined __CYGWIN__)
146 {
147 /* Check SIGPIPE handling with ignore_sigpipe = true. */
148 const char *prog_argv[3] = { prog_path, "4", NULL };
149 int termsig = 0x7DEADBEE;
150 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
151 true, false, false, false, true, false, &termsig);
152 ASSERT (ret == 0);
153 ASSERT (termsig == SIGPIPE);
154 }
155 #endif
156 break;
157 case 5:
158 {
159 /* Check other signal. */
160 const char *prog_argv[3] = { prog_path, "5", NULL };
161 int termsig = 0x7DEADBEE;
162 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
163 false, false, false, false, true, false, &termsig);
164 ASSERT (ret == 127);
165 #if defined _WIN32 && !defined __CYGWIN__
166 ASSERT (termsig == SIGTERM); /* dummy, from WTERMSIG in <sys/wait.h> */
167 #else
168 ASSERT (termsig == SIGINT);
169 #endif
170 }
171 break;
172 case 6:
173 {
174 /* Check stdin is inherited. */
175 FILE *fp = fopen (BASE ".tmp", "w");
176 fputs ("Foo", fp);
177 ASSERT (fclose (fp) == 0);
178
179 fp = freopen (BASE ".tmp", "r", stdin);
180 ASSERT (fp != NULL);
181
182 const char *prog_argv[3] = { prog_path, "6", NULL };
183 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
184 false, false, false, false, true, false, NULL);
185 ASSERT (ret == 0);
186
187 ASSERT (fclose (stdin) == 0);
188 ASSERT (remove (BASE ".tmp") == 0);
189 }
190 break;
191 case 7:
192 {
193 /* Check null_stdin = true. */
194 FILE *fp = fopen (BASE ".tmp", "w");
195 fputs ("Foo", fp);
196 ASSERT (fclose (fp) == 0);
197
198 fp = freopen (BASE ".tmp", "r", stdin);
199 ASSERT (fp != NULL);
200
201 const char *prog_argv[3] = { prog_path, "7", NULL };
202 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
203 false, true, false, false, true, false, NULL);
204 ASSERT (ret == 0);
205
206 ASSERT (fclose (stdin) == 0);
207 ASSERT (remove (BASE ".tmp") == 0);
208 }
209 break;
210 case 8:
211 {
212 /* Check stdout is inherited, part 1 (regular file). */
213 FILE *fp = freopen (BASE ".tmp", "w", stdout);
214 ASSERT (fp != NULL);
215
216 const char *prog_argv[3] = { prog_path, "8", NULL };
217 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
218 false, false, false, false, true, false, NULL);
219 ASSERT (ret == 0);
220
221 ASSERT (fclose (stdout) == 0);
222
223 size_t length;
224 char *contents = read_file (BASE ".tmp", 0, &length);
225 ASSERT (length == 3 && memcmp (contents, "bar", 3) == 0);
226
227 ASSERT (remove (BASE ".tmp") == 0);
228 }
229 break;
230 case 9:
231 {
232 /* Check stdout is inherited, part 2 (device). */
233 FILE *fp = freopen (DEV_NULL, "w", stdout);
234 ASSERT (fp != NULL);
235
236 const char *prog_argv[3] = { prog_path, "9", NULL };
237 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
238 false, false, false, false, true, false, NULL);
239 ASSERT (ret == 0);
240 }
241 break;
242 case 10:
243 {
244 /* Check null_stdout = true. */
245 FILE *fp = freopen (BASE ".tmp", "w", stdout);
246 ASSERT (fp != NULL);
247
248 const char *prog_argv[3] = { prog_path, "10", NULL };
249 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
250 false, false, true, false, true, false, NULL);
251 ASSERT (ret == 0);
252
253 ASSERT (fclose (stdout) == 0);
254
255 size_t length;
256 (void) read_file (BASE ".tmp", 0, &length);
257 ASSERT (length == 0);
258
259 ASSERT (remove (BASE ".tmp") == 0);
260 }
261 break;
262 case 11:
263 {
264 /* Check stderr is inherited, part 1 (regular file). */
265 FILE *fp = freopen (BASE ".tmp", "w", stderr);
266 ASSERT (fp != NULL);
267
268 const char *prog_argv[3] = { prog_path, "11", NULL };
269 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
270 false, false, false, false, true, false, NULL);
271 ASSERT (ret == 0);
272
273 ASSERT (fclose (stderr) == 0);
274
275 size_t length;
276 char *contents = read_file (BASE ".tmp", 0, &length);
277 ASSERT (length == 3 && memcmp (contents, "bar", 3) == 0);
278
279 ASSERT (remove (BASE ".tmp") == 0);
280 }
281 break;
282 case 12:
283 {
284 /* Check stderr is inherited, part 2 (device). */
285 FILE *fp = freopen (DEV_NULL, "w", stderr);
286 ASSERT (fp != NULL);
287
288 const char *prog_argv[3] = { prog_path, "12", NULL };
289 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
290 false, false, false, false, true, false, NULL);
291 ASSERT (ret == 0);
292 }
293 break;
294 case 13:
295 {
296 /* Check null_stderr = true. */
297 FILE *fp = freopen (BASE ".tmp", "w", stderr);
298 ASSERT (fp != NULL);
299
300 const char *prog_argv[3] = { prog_path, "13", NULL };
301 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
302 false, false, false, true, true, false, NULL);
303 ASSERT (ret == 0);
304
305 ASSERT (fclose (stderr) == 0);
306
307 size_t length;
308 (void) read_file (BASE ".tmp", 0, &length);
309 ASSERT (length == 0);
310
311 ASSERT (remove (BASE ".tmp") == 0);
312 }
313 break;
314 case 14:
315 {
316 /* Check file descriptors >= 3 can be inherited. */
317 ASSERT (dup2 (STDOUT_FILENO, 10) >= 0);
318 const char *prog_argv[3] = { prog_path, "14", NULL };
319 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
320 true, false, false, false, true, false, NULL);
321 ASSERT (ret == 0);
322 }
323 break;
324 case 15:
325 {
326 /* Check file descriptors >= 3 can be inherited. */
327 ASSERT (fcntl (STDOUT_FILENO, F_DUPFD, 10) >= 0);
328 const char *prog_argv[3] = { prog_path, "15", NULL };
329 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
330 true, false, false, false, true, false, NULL);
331 ASSERT (ret == 0);
332 }
333 break;
334 case 16:
335 {
336 /* Check file descriptors >= 3 with O_CLOEXEC bit are not inherited. */
337 ASSERT (fcntl (STDOUT_FILENO, F_DUPFD_CLOEXEC, 10) >= 0);
338 const char *prog_argv[3] = { prog_path, "16", NULL };
339 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
340 true, false, false, false, true, false, NULL);
341 ASSERT (ret == 0);
342 }
343 break;
344 case 17:
345 {
346 /* Check that file descriptors >= 3, open for reading, can be inherited,
347 including the file position. */
348 FILE *fp = fopen (BASE ".tmp", "w");
349 fputs ("Foobar", fp);
350 ASSERT (fclose (fp) == 0);
351
352 int fd = open (BASE ".tmp", O_RDONLY);
353 ASSERT (fd >= 0 && fd < 10);
354
355 ASSERT (dup2 (fd, 10) >= 0);
356 close (fd);
357 fd = 10;
358
359 char buf[2];
360 ASSERT (read (fd, buf, sizeof (buf)) == sizeof (buf));
361 /* The file position is now 2. */
362
363 const char *prog_argv[3] = { prog_path, "17", NULL };
364 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
365 false, false, false, false, true, false, NULL);
366 ASSERT (ret == 0);
367
368 close (fd);
369 ASSERT (remove (BASE ".tmp") == 0);
370 }
371 break;
372 case 18:
373 {
374 /* Check that file descriptors >= 3, open for writing, can be inherited,
375 including the file position. */
376 remove (BASE ".tmp");
377 int fd = open (BASE ".tmp", O_RDWR | O_CREAT | O_TRUNC, 0600);
378 ASSERT (fd >= 0 && fd < 10);
379
380 ASSERT (dup2 (fd, 10) >= 0);
381 close (fd);
382 fd = 10;
383
384 ASSERT (write (fd, "Foo", 3) == 3);
385 /* The file position is now 3. */
386
387 const char *prog_argv[3] = { prog_path, "18", NULL };
388 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
389 false, false, false, false, true, false, NULL);
390 ASSERT (ret == 0);
391
392 close (fd);
393
394 size_t length;
395 char *contents = read_file (BASE ".tmp", 0, &length);
396 ASSERT (length == 6 && memcmp (contents, "Foobar", 6) == 0);
397
398 ASSERT (remove (BASE ".tmp") == 0);
399 }
400 break;
401 case 19:
402 {
403 /* Check that file descriptors >= 3, when inherited, preserve their
404 isatty() property, part 1 (regular file). */
405 FILE *fp = fopen (BASE ".tmp", "w");
406 fputs ("Foo", fp);
407 ASSERT (fclose (fp) == 0);
408
409 int fd_in = open (BASE ".tmp", O_RDONLY);
410 ASSERT (fd_in >= 0 && fd_in < 10);
411
412 int fd_out = open (BASE ".tmp", O_WRONLY | O_APPEND);
413 ASSERT (fd_out >= 0 && fd_out < 10);
414
415 ASSERT (dup2 (fd_in, 10) >= 0);
416 close (fd_in);
417 fd_in = 10;
418
419 ASSERT (dup2 (fd_out, 11) >= 0);
420 close (fd_out);
421 fd_out = 11;
422
423 const char *prog_argv[3] = { prog_path, "19", NULL };
424 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
425 false, false, false, false, true, false, NULL);
426 #if defined _WIN32 && ! defined __CYGWIN__
427 ASSERT (ret == 4 + 2 * (_isatty (10) != 0) + (_isatty (11) != 0));
428 #else
429 ASSERT (ret == 4 + 2 * (isatty (10) != 0) + (isatty (11) != 0));
430 #endif
431
432 close (fd_in);
433 close (fd_out);
434 ASSERT (remove (BASE ".tmp") == 0);
435 }
436 break;
437 case 20:
438 {
439 /* Check that file descriptors >= 3, when inherited, preserve their
440 isatty() property, part 2 (character devices). */
441 ASSERT (dup2 (STDIN_FILENO, 10) >= 0);
442 int fd_in = 10;
443
444 ASSERT (dup2 (STDOUT_FILENO, 11) >= 0);
445 int fd_out = 11;
446
447 const char *prog_argv[3] = { prog_path, "20", NULL };
448 int ret = execute (progname, prog_argv[0], prog_argv, NULL,
449 false, false, false, false, true, false, NULL);
450 #if defined _WIN32 && ! defined __CYGWIN__
451 ASSERT (ret == 4 + 2 * (_isatty (10) != 0) + (_isatty (11) != 0));
452 #else
453 ASSERT (ret == 4 + 2 * (isatty (10) != 0) + (isatty (11) != 0));
454 #endif
455
456 close (fd_in);
457 close (fd_out);
458 }
459 break;
460 case 21:
461 {
462 /* Check execution in a different directory. */
463 rmdir (BASE ".sub");
464 ASSERT (mkdir (BASE ".sub", 0700) == 0);
465
466 char cwd[1024];
467 #if defined _WIN32 && ! defined __CYGWIN__
468 ASSERT (_getcwd (cwd, sizeof (cwd)) != NULL);
469 #else
470 ASSERT (getcwd (cwd, sizeof (cwd)) != NULL);
471 #endif
472
473 const char *prog_argv[4] = { prog_path, "21", cwd, NULL };
474 int ret = execute (progname, prog_argv[0], prog_argv, BASE ".sub",
475 false, false, false, false, true, false, NULL);
476 ASSERT (ret == 0);
477
478 ASSERT (rmdir (BASE ".sub") == 0);
479 }
480 break;
481 default:
482 ASSERT (false);
483 }
484 return 0;
485 }