(root)/
m4-1.4.19/
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  }