Blame support/support_subprocess.c

Packit Bot 40fc07
/* Create subprocess.
Packit Bot 40fc07
   Copyright (C) 2019 Free Software Foundation, Inc.
Packit Bot 40fc07
   This file is part of the GNU C Library.
Packit Bot 40fc07
Packit Bot 40fc07
   The GNU C Library is free software; you can redistribute it and/or
Packit Bot 40fc07
   modify it under the terms of the GNU Lesser General Public
Packit Bot 40fc07
   License as published by the Free Software Foundation; either
Packit Bot 40fc07
   version 2.1 of the License, or (at your option) any later version.
Packit Bot 40fc07
Packit Bot 40fc07
   The GNU C Library is distributed in the hope that it will be useful,
Packit Bot 40fc07
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Bot 40fc07
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Bot 40fc07
   Lesser General Public License for more details.
Packit Bot 40fc07
Packit Bot 40fc07
   You should have received a copy of the GNU Lesser General Public
Packit Bot 40fc07
   License along with the GNU C Library; if not, see
Packit Bot 40fc07
   <http://www.gnu.org/licenses/>.  */
Packit Bot 40fc07
Packit Bot 40fc07
#include <stdio.h>
Packit Bot 40fc07
#include <signal.h>
Packit Bot 40fc07
#include <time.h>
Packit Bot 40fc07
#include <sys/wait.h>
Packit Bot 40fc07
#include <stdbool.h>
Packit Bot 40fc07
#include <support/xspawn.h>
Packit Bot 40fc07
#include <support/check.h>
Packit Bot 40fc07
#include <support/xunistd.h>
Packit Bot 40fc07
#include <support/subprocess.h>
Packit Bot 40fc07
Packit Bot 40fc07
static struct support_subprocess
Packit Bot 40fc07
support_suprocess_init (void)
Packit Bot 40fc07
{
Packit Bot 40fc07
  struct support_subprocess result;
Packit Bot 40fc07
Packit Bot 40fc07
  xpipe (result.stdout_pipe);
Packit Bot 40fc07
  TEST_VERIFY (result.stdout_pipe[0] > STDERR_FILENO);
Packit Bot 40fc07
  TEST_VERIFY (result.stdout_pipe[1] > STDERR_FILENO);
Packit Bot 40fc07
Packit Bot 40fc07
  xpipe (result.stderr_pipe);
Packit Bot 40fc07
  TEST_VERIFY (result.stderr_pipe[0] > STDERR_FILENO);
Packit Bot 40fc07
  TEST_VERIFY (result.stderr_pipe[1] > STDERR_FILENO);
Packit Bot 40fc07
Packit Bot 40fc07
  TEST_VERIFY (fflush (stdout) == 0);
Packit Bot 40fc07
  TEST_VERIFY (fflush (stderr) == 0);
Packit Bot 40fc07
Packit Bot 40fc07
  return result;
Packit Bot 40fc07
}
Packit Bot 40fc07
Packit Bot 40fc07
struct support_subprocess
Packit Bot 40fc07
support_subprocess (void (*callback) (void *), void *closure)
Packit Bot 40fc07
{
Packit Bot 40fc07
  struct support_subprocess result = support_suprocess_init ();
Packit Bot 40fc07
Packit Bot 40fc07
  result.pid = xfork ();
Packit Bot 40fc07
  if (result.pid == 0)
Packit Bot 40fc07
    {
Packit Bot 40fc07
      xclose (result.stdout_pipe[0]);
Packit Bot 40fc07
      xclose (result.stderr_pipe[0]);
Packit Bot 40fc07
      xdup2 (result.stdout_pipe[1], STDOUT_FILENO);
Packit Bot 40fc07
      xdup2 (result.stderr_pipe[1], STDERR_FILENO);
Packit Bot 40fc07
      xclose (result.stdout_pipe[1]);
Packit Bot 40fc07
      xclose (result.stderr_pipe[1]);
Packit Bot 40fc07
      callback (closure);
Packit Bot 40fc07
     _exit (0);
Packit Bot 40fc07
    }
Packit Bot 40fc07
  xclose (result.stdout_pipe[1]);
Packit Bot 40fc07
  xclose (result.stderr_pipe[1]);
Packit Bot 40fc07
Packit Bot 40fc07
  return result;
Packit Bot 40fc07
}
Packit Bot 40fc07
Packit Bot 40fc07
struct support_subprocess
Packit Bot 40fc07
support_subprogram (const char *file, char *const argv[])
Packit Bot 40fc07
{
Packit Bot 40fc07
  struct support_subprocess result = support_suprocess_init ();
Packit Bot 40fc07
Packit Bot 40fc07
  posix_spawn_file_actions_t fa;
Packit Bot 40fc07
  /* posix_spawn_file_actions_init does not fail.  */
Packit Bot 40fc07
  posix_spawn_file_actions_init (&fa);
Packit Bot 40fc07
Packit Bot 40fc07
  xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[0]);
Packit Bot 40fc07
  xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[0]);
Packit Bot 40fc07
  xposix_spawn_file_actions_adddup2 (&fa, result.stdout_pipe[1], STDOUT_FILENO);
Packit Bot 40fc07
  xposix_spawn_file_actions_adddup2 (&fa, result.stderr_pipe[1], STDERR_FILENO);
Packit Bot 40fc07
  xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[1]);
Packit Bot 40fc07
  xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[1]);
Packit Bot 40fc07
Packit Bot 1c7653
  result.pid = xposix_spawn (file, &fa, NULL, argv, environ);
Packit Bot 40fc07
Packit Bot 40fc07
  xclose (result.stdout_pipe[1]);
Packit Bot 40fc07
  xclose (result.stderr_pipe[1]);
Packit Bot 40fc07
Packit Bot 40fc07
  return result;
Packit Bot 40fc07
}
Packit Bot 40fc07
Packit Bot 40fc07
int
Packit Bot 40fc07
support_process_wait (struct support_subprocess *proc)
Packit Bot 40fc07
{
Packit Bot 40fc07
  xclose (proc->stdout_pipe[0]);
Packit Bot 40fc07
  xclose (proc->stderr_pipe[0]);
Packit Bot 40fc07
Packit Bot 40fc07
  int status;
Packit Bot 40fc07
  xwaitpid (proc->pid, &status, 0);
Packit Bot 40fc07
  return status;
Packit Bot 40fc07
}
Packit Bot 40fc07
Packit Bot 40fc07
Packit Bot 40fc07
static bool
Packit Bot 40fc07
support_process_kill (int pid, int signo, int *status)
Packit Bot 40fc07
{
Packit Bot 40fc07
  /* Kill the whole process group.  */
Packit Bot 40fc07
  kill (-pid, signo);
Packit Bot 40fc07
  /* In case setpgid failed in the child, kill it individually too.  */
Packit Bot 40fc07
  kill (pid, signo);
Packit Bot 40fc07
Packit Bot 40fc07
  /* Wait for it to terminate.  */
Packit Bot 40fc07
  pid_t killed;
Packit Bot 40fc07
  for (int i = 0; i < 5; ++i)
Packit Bot 40fc07
    {
Packit Bot 40fc07
      int status;
Packit Bot 40fc07
      killed = xwaitpid (pid, &status, WNOHANG|WUNTRACED);
Packit Bot 40fc07
      if (killed != 0)
Packit Bot 40fc07
        break;
Packit Bot 40fc07
Packit Bot 40fc07
      /* Delay, give the system time to process the kill.  If the
Packit Bot 40fc07
         nanosleep() call return prematurely, all the better.  We
Packit Bot 40fc07
         won't restart it since this probably means the child process
Packit Bot 40fc07
         finally died.  */
Packit Bot 40fc07
      nanosleep (&((struct timespec) { 0, 100000000 }), NULL);
Packit Bot 40fc07
    }
Packit Bot 40fc07
  if (killed != 0 && killed != pid)
Packit Bot 40fc07
    return false;
Packit Bot 40fc07
Packit Bot 40fc07
  return true;
Packit Bot 40fc07
}
Packit Bot 40fc07
Packit Bot 40fc07
int
Packit Bot 40fc07
support_process_terminate (struct support_subprocess *proc)
Packit Bot 40fc07
{
Packit Bot 40fc07
  xclose (proc->stdout_pipe[0]);
Packit Bot 40fc07
  xclose (proc->stderr_pipe[0]);
Packit Bot 40fc07
Packit Bot 40fc07
  int status;
Packit Bot 40fc07
  pid_t killed = xwaitpid (proc->pid, &status, WNOHANG|WUNTRACED);
Packit Bot 40fc07
  if (killed != 0 && killed == proc->pid)
Packit Bot 40fc07
    return status;
Packit Bot 40fc07
Packit Bot 40fc07
  /* Subprocess is still running, terminate it.  */
Packit Bot 40fc07
  if (!support_process_kill (proc->pid, SIGTERM, &status) )
Packit Bot 40fc07
    support_process_kill (proc->pid, SIGKILL, &status);
Packit Bot 40fc07
Packit Bot 40fc07
  return status;
Packit Bot 40fc07
}