Blame support/support_subprocess.c

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