(root)/
bison-3.8.2/
lib/
wait-process.c
       1  /* Waiting for a subprocess to finish.
       2     Copyright (C) 2001-2003, 2005-2021 Free Software Foundation, Inc.
       3     Written by Bruno Haible <haible@clisp.cons.org>, 2001.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation; either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program 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
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  
      19  #include <config.h>
      20  
      21  /* Specification.  */
      22  #include "wait-process.h"
      23  
      24  #include <errno.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <signal.h>
      28  
      29  #include <sys/types.h>
      30  #include <sys/wait.h>
      31  
      32  #include "error.h"
      33  #include "fatal-signal.h"
      34  #include "xalloc.h"
      35  #include "gettext.h"
      36  
      37  #define _(str) gettext (str)
      38  
      39  #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
      40  
      41  
      42  #if defined _WIN32 && ! defined __CYGWIN__
      43  
      44  # define WIN32_LEAN_AND_MEAN
      45  # include <windows.h>
      46  
      47  /* The return value of _spawnvp() is really a process handle as returned
      48     by CreateProcess().  Therefore we can kill it using TerminateProcess.  */
      49  # define kill(pid,sig) TerminateProcess ((HANDLE) (pid), sig)
      50  
      51  #endif
      52  
      53  
      54  /* Type of an entry in the slaves array.
      55     The 'used' bit determines whether this entry is currently in use.
      56     (If pid_t was an atomic type like sig_atomic_t, we could just set the
      57     'child' field to 0 when unregistering a slave process, and wouldn't need
      58     the 'used' field.)
      59     The 'used' and 'child' fields are accessed from within the cleanup_slaves()
      60     action, therefore we mark them as 'volatile'.  */
      61  typedef struct
      62  {
      63    volatile sig_atomic_t used;
      64    volatile pid_t child;
      65  }
      66  slaves_entry_t;
      67  
      68  /* The registered slave subprocesses.  */
      69  static slaves_entry_t static_slaves[32];
      70  static slaves_entry_t * volatile slaves = static_slaves;
      71  static sig_atomic_t volatile slaves_count = 0;
      72  static size_t slaves_allocated = SIZEOF (static_slaves);
      73  
      74  /* The termination signal for slave subprocesses.
      75     2003-10-07:  Terminator becomes Governator.  */
      76  #ifdef SIGHUP
      77  # define TERMINATOR SIGHUP
      78  #else
      79  # define TERMINATOR SIGTERM
      80  #endif
      81  
      82  /* The cleanup action.  It gets called asynchronously.  */
      83  static _GL_ASYNC_SAFE void
      84  cleanup_slaves (void)
      85  {
      86    for (;;)
      87      {
      88        /* Get the last registered slave.  */
      89        size_t n = slaves_count;
      90        if (n == 0)
      91          break;
      92        n--;
      93        slaves_count = n;
      94        /* Skip unused entries in the slaves array.  */
      95        if (slaves[n].used)
      96          {
      97            pid_t slave = slaves[n].child;
      98  
      99            /* Kill the slave.  */
     100            kill (slave, TERMINATOR);
     101          }
     102      }
     103  }
     104  
     105  /* The cleanup action, taking a signal argument.
     106     It gets called asynchronously.  */
     107  static _GL_ASYNC_SAFE void
     108  cleanup_slaves_action (_GL_UNUSED int sig)
     109  {
     110    cleanup_slaves ();
     111  }
     112  
     113  /* Register a subprocess as being a slave process.  This means that the
     114     subprocess will be terminated when its creator receives a catchable fatal
     115     signal or exits normally.  Registration ends when wait_subprocess()
     116     notices that the subprocess has exited.  */
     117  void
     118  register_slave_subprocess (pid_t child)
     119  {
     120    static bool cleanup_slaves_registered = false;
     121    if (!cleanup_slaves_registered)
     122      {
     123        atexit (cleanup_slaves);
     124        if (at_fatal_signal (cleanup_slaves_action) < 0)
     125          xalloc_die ();
     126        cleanup_slaves_registered = true;
     127      }
     128  
     129    /* Try to store the new slave in an unused entry of the slaves array.  */
     130    {
     131      slaves_entry_t *s = slaves;
     132      slaves_entry_t *s_end = s + slaves_count;
     133  
     134      for (; s < s_end; s++)
     135        if (!s->used)
     136          {
     137            /* The two uses of 'volatile' in the slaves_entry_t type above
     138               (and ISO C 99 section 5.1.2.3.(5)) ensure that we mark the
     139               entry as used only after the child pid has been written to the
     140               memory location s->child.  */
     141            s->child = child;
     142            s->used = 1;
     143            return;
     144          }
     145    }
     146  
     147    if (slaves_count == slaves_allocated)
     148      {
     149        /* Extend the slaves array.  Note that we cannot use xrealloc(),
     150           because then the cleanup_slaves() function could access an already
     151           deallocated array.  */
     152        slaves_entry_t *old_slaves = slaves;
     153        size_t new_slaves_allocated = 2 * slaves_allocated;
     154        slaves_entry_t *new_slaves =
     155          (slaves_entry_t *)
     156          malloc (new_slaves_allocated * sizeof (slaves_entry_t));
     157        if (new_slaves == NULL)
     158          {
     159            /* xalloc_die() will call exit() which will invoke cleanup_slaves().
     160               Additionally we need to kill child, because it's not yet among
     161               the slaves list.  */
     162            kill (child, TERMINATOR);
     163            xalloc_die ();
     164          }
     165        memcpy (new_slaves, old_slaves,
     166                slaves_allocated * sizeof (slaves_entry_t));
     167        slaves = new_slaves;
     168        slaves_allocated = new_slaves_allocated;
     169        /* Now we can free the old slaves array.  */
     170        if (old_slaves != static_slaves)
     171          free (old_slaves);
     172      }
     173    /* The three uses of 'volatile' in the types above (and ISO C 99 section
     174       5.1.2.3.(5)) ensure that we increment the slaves_count only after the
     175       new slave and its 'used' bit have been written to the memory locations
     176       that make up slaves[slaves_count].  */
     177    slaves[slaves_count].child = child;
     178    slaves[slaves_count].used = 1;
     179    slaves_count++;
     180  }
     181  
     182  /* Unregister a child from the list of slave subprocesses.  */
     183  static void
     184  unregister_slave_subprocess (pid_t child)
     185  {
     186    /* The easiest way to remove an entry from a list that can be used by
     187       an asynchronous signal handler is just to mark it as unused.  For this,
     188       we rely on sig_atomic_t.  */
     189    slaves_entry_t *s = slaves;
     190    slaves_entry_t *s_end = s + slaves_count;
     191  
     192    for (; s < s_end; s++)
     193      if (s->used && s->child == child)
     194        s->used = 0;
     195  }
     196  
     197  
     198  /* Wait for a subprocess to finish.  Return its exit code.
     199     If it didn't terminate correctly, exit if exit_on_error is true, otherwise
     200     return 127.  */
     201  int
     202  wait_subprocess (pid_t child, const char *progname,
     203                   bool ignore_sigpipe, bool null_stderr,
     204                   bool slave_process, bool exit_on_error,
     205                   int *termsigp)
     206  {
     207  #if HAVE_WAITID && defined WNOWAIT && 0
     208    /* Commented out because waitid() without WEXITED and with WNOWAIT doesn't
     209       work: On Solaris 7 and OSF/1 4.0, it returns -1 and sets errno = ECHILD,
     210       and on HP-UX 10.20 it just hangs.  */
     211    /* Use of waitid() with WNOWAIT avoids a race condition: If slave_process is
     212       true, and this process sleeps a very long time between the return from
     213       waitpid() and the execution of unregister_slave_subprocess(), and
     214       meanwhile another process acquires the same PID as child, and then - still
     215       before unregister_slave_subprocess() - this process gets a fatal signal,
     216       it would kill the other totally unrelated process.  */
     217    siginfo_t info;
     218  
     219    if (termsigp != NULL)
     220      *termsigp = 0;
     221    for (;;)
     222      {
     223        if (waitid (P_PID, child, &info, WEXITED | (slave_process ? WNOWAIT : 0))
     224            < 0)
     225          {
     226  # ifdef EINTR
     227            if (errno == EINTR)
     228              continue;
     229  # endif
     230            if (exit_on_error || !null_stderr)
     231              error (exit_on_error ? EXIT_FAILURE : 0, errno,
     232                     _("%s subprocess"), progname);
     233            return 127;
     234          }
     235  
     236        /* info.si_code is set to one of CLD_EXITED, CLD_KILLED, CLD_DUMPED,
     237           CLD_TRAPPED, CLD_STOPPED, CLD_CONTINUED.  Loop until the program
     238           terminates.  */
     239        if (info.si_code == CLD_EXITED
     240            || info.si_code == CLD_KILLED || info.si_code == CLD_DUMPED)
     241          break;
     242      }
     243  
     244    /* The child process has exited or was signalled.  */
     245  
     246    if (slave_process)
     247      {
     248        /* Unregister the child from the list of slave subprocesses, so that
     249           later, when we exit, we don't kill a totally unrelated process which
     250           may have acquired the same pid.  */
     251        unregister_slave_subprocess (child);
     252  
     253        /* Now remove the zombie from the process list.  */
     254        for (;;)
     255          {
     256            if (waitid (P_PID, child, &info, WEXITED) < 0)
     257              {
     258  # ifdef EINTR
     259                if (errno == EINTR)
     260                  continue;
     261  # endif
     262                if (exit_on_error || !null_stderr)
     263                  error (exit_on_error ? EXIT_FAILURE : 0, errno,
     264                         _("%s subprocess"), progname);
     265                return 127;
     266              }
     267            break;
     268          }
     269      }
     270  
     271    switch (info.si_code)
     272      {
     273      case CLD_KILLED:
     274      case CLD_DUMPED:
     275        if (termsigp != NULL)
     276          *termsigp = info.si_status; /* TODO: or info.si_signo? */
     277  # ifdef SIGPIPE
     278        if (info.si_status == SIGPIPE && ignore_sigpipe)
     279          return 0;
     280  # endif
     281        if (exit_on_error || (!null_stderr && termsigp == NULL))
     282          error (exit_on_error ? EXIT_FAILURE : 0, 0,
     283                 _("%s subprocess got fatal signal %d"),
     284                 progname, info.si_status);
     285        return 127;
     286      case CLD_EXITED:
     287        if (info.si_status == 127)
     288          {
     289            if (exit_on_error || !null_stderr)
     290              error (exit_on_error ? EXIT_FAILURE : 0, 0,
     291                     _("%s subprocess failed"), progname);
     292            return 127;
     293          }
     294        return info.si_status;
     295      default:
     296        abort ();
     297      }
     298  #else
     299    /* waitpid() is just as portable as wait() nowadays.  */
     300    int status;
     301  
     302    if (termsigp != NULL)
     303      *termsigp = 0;
     304    status = 0;
     305    for (;;)
     306      {
     307        int result = waitpid (child, &status, 0);
     308  
     309        if (result != child)
     310          {
     311  # ifdef EINTR
     312            if (errno == EINTR)
     313              continue;
     314  # endif
     315  # if 0 /* defined ECHILD */
     316            if (errno == ECHILD)
     317              {
     318                /* Child process nonexistent?! Assume it terminated
     319                   successfully.  */
     320                status = 0;
     321                break;
     322              }
     323  # endif
     324            if (exit_on_error || !null_stderr)
     325              error (exit_on_error ? EXIT_FAILURE : 0, errno,
     326                     _("%s subprocess"), progname);
     327            return 127;
     328          }
     329  
     330        /* One of WIFSIGNALED (status), WIFEXITED (status), WIFSTOPPED (status)
     331           must always be true, since we did not specify WCONTINUED in the
     332           waitpid() call.  Loop until the program terminates.  */
     333        if (!WIFSTOPPED (status))
     334          break;
     335      }
     336  
     337    /* The child process has exited or was signalled.  */
     338  
     339    if (slave_process)
     340      /* Unregister the child from the list of slave subprocesses, so that
     341         later, when we exit, we don't kill a totally unrelated process which
     342         may have acquired the same pid.  */
     343      unregister_slave_subprocess (child);
     344  
     345    if (WIFSIGNALED (status))
     346      {
     347        if (termsigp != NULL)
     348          *termsigp = WTERMSIG (status);
     349  # ifdef SIGPIPE
     350        if (WTERMSIG (status) == SIGPIPE && ignore_sigpipe)
     351          return 0;
     352  # endif
     353        if (exit_on_error || (!null_stderr && termsigp == NULL))
     354          error (exit_on_error ? EXIT_FAILURE : 0, 0,
     355                 _("%s subprocess got fatal signal %d"),
     356                 progname, (int) WTERMSIG (status));
     357        return 127;
     358      }
     359    if (!WIFEXITED (status))
     360      abort ();
     361    if (WEXITSTATUS (status) == 127)
     362      {
     363        if (exit_on_error || !null_stderr)
     364          error (exit_on_error ? EXIT_FAILURE : 0, 0,
     365                 _("%s subprocess failed"), progname);
     366        return 127;
     367      }
     368    return WEXITSTATUS (status);
     369  #endif
     370  }