Blame support/support_subprocess.c

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