Blame support/support_subprocess.c

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