(root)/
gettext-0.22.4/
gettext-tools/
gnulib-tests/
test-posix_spawn-chdir.c
       1  /* Test of posix_spawn() function with 'chdir' action.
       2     Copyright (C) 2008-2023 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 of the License, or
       7     (at your option) 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  /* Written by Bruno Haible <bruno@clisp.org>, 2018.  */
      18  
      19  #include <config.h>
      20  
      21  #include <spawn.h>
      22  
      23  #include <errno.h>
      24  #include <fcntl.h>
      25  #include <signal.h>
      26  #include <stdio.h>
      27  #include <stdlib.h>
      28  #include <string.h>
      29  #include <unistd.h>
      30  #include <sys/types.h>
      31  #include <sys/wait.h>
      32  
      33  #include "findprog.h"
      34  
      35  static int
      36  fd_safer (int fd)
      37  {
      38    if (0 <= fd && fd <= 2)
      39      {
      40        int f = fd_safer (dup (fd));
      41        int e = errno;
      42        close (fd);
      43        errno = e;
      44        fd = f;
      45      }
      46  
      47    return fd;
      48  }
      49  
      50  static void
      51  test (const char *pwd_prog)
      52  {
      53    char *argv[2] = { (char *) "pwd", NULL };
      54    int ifd[2];
      55    sigset_t blocked_signals;
      56    sigset_t fatal_signal_set;
      57    posix_spawn_file_actions_t actions;
      58    bool actions_allocated;
      59    posix_spawnattr_t attrs;
      60    bool attrs_allocated;
      61    int err;
      62    pid_t child;
      63    int fd;
      64    FILE *fp;
      65    char line[80];
      66    int line_len;
      67    int status;
      68    int exitstatus;
      69  
      70    if (pipe (ifd) < 0 || (ifd[0] = fd_safer (ifd[0])) < 0)
      71      {
      72        perror ("cannot create pipe");
      73        exit (1);
      74      }
      75    sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
      76    sigemptyset (&fatal_signal_set);
      77    sigaddset (&fatal_signal_set, SIGINT);
      78    sigaddset (&fatal_signal_set, SIGTERM);
      79    #ifdef SIGHUP
      80    sigaddset (&fatal_signal_set, SIGHUP);
      81    #endif
      82    #ifdef SIGPIPE
      83    sigaddset (&fatal_signal_set, SIGPIPE);
      84    #endif
      85    sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
      86    actions_allocated = false;
      87    attrs_allocated = false;
      88    if ((err = posix_spawn_file_actions_init (&actions)) != 0
      89        || (actions_allocated = true,
      90            (err = posix_spawn_file_actions_adddup2 (&actions, ifd[1], STDOUT_FILENO)) != 0
      91            || (err = posix_spawn_file_actions_addclose (&actions, ifd[1])) != 0
      92            || (err = posix_spawn_file_actions_addclose (&actions, ifd[0])) != 0
      93            || (err = posix_spawn_file_actions_addopen (&actions, STDIN_FILENO, "/dev/null", O_RDONLY, 0)) != 0
      94            || (err = posix_spawn_file_actions_addchdir (&actions, "/")) != 0
      95            || (err = posix_spawnattr_init (&attrs)) != 0
      96            || (attrs_allocated = true,
      97                #if defined _WIN32 && !defined __CYGWIN__
      98                0
      99                #else
     100                (err = posix_spawnattr_setsigmask (&attrs, &blocked_signals)) != 0
     101                || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0
     102                #endif
     103               )
     104            || (err = posix_spawnp (&child, pwd_prog, &actions, &attrs, argv, environ)) != 0))
     105      {
     106        if (actions_allocated)
     107          posix_spawn_file_actions_destroy (&actions);
     108        if (attrs_allocated)
     109          posix_spawnattr_destroy (&attrs);
     110        sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
     111        errno = err;
     112        perror ("subprocess failed");
     113        exit (1);
     114      }
     115    posix_spawn_file_actions_destroy (&actions);
     116    posix_spawnattr_destroy (&attrs);
     117    sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
     118    close (ifd[1]);
     119    fd = ifd[0];
     120    fp = fdopen (fd, "rb");
     121    if (fp == NULL)
     122      {
     123        fprintf (stderr, "fdopen() failed\n");
     124        exit (1);
     125      }
     126    line_len = fread (line, 1, 80, fp);
     127    if (line_len < 2)
     128      {
     129        fprintf (stderr, "could not read expected output\n");
     130        exit (1);
     131      }
     132    if (!(line_len == 2 && memcmp (line, "/\n", 2) == 0))
     133  #if defined _WIN32 && !defined __CYGWIN__
     134      /* If the pwd program is Cygwin's pwd, its output in the root directory is
     135         "/cygdrive/N", where N is a lowercase letter.  */
     136      if (!(line_len > 11
     137            && memcmp (line, "/cygdrive/", 10) == 0
     138            && line[10] >= 'a' && line[10] <= 'z'
     139            && ((line_len == 12 && line[11] == '\n')
     140                || (line_len == 13 && line[11] == '\r' && line[12] == '\n'))))
     141  #endif
     142        {
     143          fprintf (stderr, "read output is not the expected output\n");
     144          exit (1);
     145        }
     146    fclose (fp);
     147    status = 0;
     148    while (waitpid (child, &status, 0) != child)
     149      ;
     150    if (!WIFEXITED (status))
     151      {
     152        fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
     153        exit (1);
     154      }
     155    exitstatus = WEXITSTATUS (status);
     156    if (exitstatus != 0)
     157      {
     158        fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
     159        exit (1);
     160      }
     161  }
     162  
     163  int
     164  main ()
     165  {
     166    test ("pwd");
     167  
     168    /* Verify that if a program is given as a relative file name with at least one
     169       slash, it is interpreted w.r.t. the current directory after chdir has been
     170       executed.  */
     171    {
     172      const char *abs_pwd_prog = find_in_path ("pwd");
     173  
     174      if (abs_pwd_prog != NULL
     175          && abs_pwd_prog[0] == '/'
     176          && abs_pwd_prog[1] != '0' && abs_pwd_prog[1] != '/')
     177        test (&abs_pwd_prog[1]);
     178    }
     179  
     180    return 0;
     181  }