(root)/
glibc-2.38/
support/
support_subprocess.c
       1  /* Create subprocess.
       2     Copyright (C) 2019-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <stdio.h>
      20  #include <signal.h>
      21  #include <time.h>
      22  #include <sys/wait.h>
      23  #include <stdbool.h>
      24  #include <support/xspawn.h>
      25  #include <support/check.h>
      26  #include <support/xunistd.h>
      27  #include <support/subprocess.h>
      28  
      29  static struct support_subprocess
      30  support_subprocess_init (void)
      31  {
      32    struct support_subprocess result;
      33  
      34    xpipe (result.stdout_pipe);
      35    TEST_VERIFY (result.stdout_pipe[0] > STDERR_FILENO);
      36    TEST_VERIFY (result.stdout_pipe[1] > STDERR_FILENO);
      37  
      38    xpipe (result.stderr_pipe);
      39    TEST_VERIFY (result.stderr_pipe[0] > STDERR_FILENO);
      40    TEST_VERIFY (result.stderr_pipe[1] > STDERR_FILENO);
      41  
      42    TEST_VERIFY (fflush (stdout) == 0);
      43    TEST_VERIFY (fflush (stderr) == 0);
      44  
      45    return result;
      46  }
      47  
      48  struct support_subprocess
      49  support_subprocess (void (*callback) (void *), void *closure)
      50  {
      51    struct support_subprocess result = support_subprocess_init ();
      52  
      53    result.pid = xfork ();
      54    if (result.pid == 0)
      55      {
      56        xclose (result.stdout_pipe[0]);
      57        xclose (result.stderr_pipe[0]);
      58        xdup2 (result.stdout_pipe[1], STDOUT_FILENO);
      59        xdup2 (result.stderr_pipe[1], STDERR_FILENO);
      60        xclose (result.stdout_pipe[1]);
      61        xclose (result.stderr_pipe[1]);
      62        callback (closure);
      63        _exit (0);
      64      }
      65    xclose (result.stdout_pipe[1]);
      66    xclose (result.stderr_pipe[1]);
      67  
      68    return result;
      69  }
      70  
      71  struct support_subprocess
      72  support_subprogram (const char *file, char *const argv[])
      73  {
      74    struct support_subprocess result = support_subprocess_init ();
      75  
      76    posix_spawn_file_actions_t fa;
      77    /* posix_spawn_file_actions_init does not fail.  */
      78    posix_spawn_file_actions_init (&fa);
      79  
      80    xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[0]);
      81    xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[0]);
      82    xposix_spawn_file_actions_adddup2 (&fa, result.stdout_pipe[1], STDOUT_FILENO);
      83    xposix_spawn_file_actions_adddup2 (&fa, result.stderr_pipe[1], STDERR_FILENO);
      84    xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[1]);
      85    xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[1]);
      86  
      87    result.pid = xposix_spawn (file, &fa, NULL, argv, environ);
      88  
      89    xclose (result.stdout_pipe[1]);
      90    xclose (result.stderr_pipe[1]);
      91  
      92    return result;
      93  }
      94  
      95  int
      96  support_subprogram_wait (const char *file, char *const argv[])
      97  {
      98    posix_spawn_file_actions_t fa;
      99  
     100    posix_spawn_file_actions_init (&fa);
     101    struct support_subprocess res = support_subprocess_init ();
     102  
     103    res.pid = xposix_spawn (file, &fa, NULL, argv, environ);
     104  
     105    return support_process_wait (&res);
     106  }
     107  
     108  int
     109  support_process_wait (struct support_subprocess *proc)
     110  {
     111    xclose (proc->stdout_pipe[0]);
     112    xclose (proc->stderr_pipe[0]);
     113  
     114    int status;
     115    xwaitpid (proc->pid, &status, 0);
     116    return status;
     117  }
     118  
     119  
     120  static bool
     121  support_process_kill (int pid, int signo, int *status)
     122  {
     123    /* Kill the whole process group.  */
     124    kill (-pid, signo);
     125    /* In case setpgid failed in the child, kill it individually too.  */
     126    kill (pid, signo);
     127  
     128    /* Wait for it to terminate.  */
     129    pid_t killed;
     130    for (int i = 0; i < 5; ++i)
     131      {
     132        int status;
     133        killed = xwaitpid (pid, &status, WNOHANG|WUNTRACED);
     134        if (killed != 0)
     135          break;
     136  
     137        /* Delay, give the system time to process the kill.  If the
     138           nanosleep() call return prematurely, all the better.  We
     139           won't restart it since this probably means the child process
     140           finally died.  */
     141        nanosleep (&((struct timespec) { 0, 100000000 }), NULL);
     142      }
     143    if (killed != 0 && killed != pid)
     144      return false;
     145  
     146    return true;
     147  }
     148  
     149  int
     150  support_process_terminate (struct support_subprocess *proc)
     151  {
     152    xclose (proc->stdout_pipe[0]);
     153    xclose (proc->stderr_pipe[0]);
     154  
     155    int status;
     156    pid_t killed = xwaitpid (proc->pid, &status, WNOHANG|WUNTRACED);
     157    if (killed != 0 && killed == proc->pid)
     158      return status;
     159  
     160    /* Subprocess is still running, terminate it.  */
     161    if (!support_process_kill (proc->pid, SIGTERM, &status) )
     162      support_process_kill (proc->pid, SIGKILL, &status);
     163  
     164    return status;
     165  }