Bump to m4 1.4.19
[platform/upstream/m4.git] / tests / test-execute-main.c
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 }