Mercurial > hg > octave-jordi > gnulib-hg
view lib/wait-process.c @ 7302:8a1a9361108c
* _fpending.c: Include <config.h> unconditionally, since we no
longer worry about uses that don't define HAVE_CONFIG_H.
* acl.c, alloca.c, argmatch.c, atexit.c, backupfile.c:
* basename.c, c-stack.c, c-strtod.c, calloc.c, canon-host.c:
* canonicalize.c, chdir-long.c, chdir-safer.c, chown.c:
* cloexec.c, close-stream.c, closeout.c, creat-safer.c:
* cycle-check.c, diacrit.c, dirchownmod.c, dirfd.c, dirname.c:
* dup-safer.c, dup2.c, error.c, euidaccess.c, exclude.c:
* exitfail.c, fchmodat.c, fchown-stub.c, fd-safer.c:
* file-type.c, fileblocks.c, filemode.c, filenamecat.c:
* fnmatch.c, fopen-safer.c, fprintftime.c, free.c, fsusage.c:
* ftruncate.c, fts-cycle.c, fts.c, full-write.c, gai_strerror.c:
* getcwd.c, getdate.y, getdomainname.c, getgroups.c:
* gethostname.c, gethrxtime.c, getloadavg.c, getlogin_r.c:
* getndelim2.c, getnline.c, getopt.c, getopt1.c, getpass.c:
* gettime.c, gettimeofday.c, getugroups.c, getusershell.c:
* glob.c, group-member.c, hard-locale.c, hash-pjw.c, hash.c:
* human.c, idcache.c, inet_ntop.c, inet_pton.c, inttostr.c:
* isdir.c, lchown.c, linebuffer.c, long-options.c, lstat.c:
* malloc.c, md5.c, memcasecmp.c, memchr.c, memcmp.c, memcoll.c:
* memcpy.c, memmove.c, memrchr.c, mkancesdirs.c, mkdir-p.c:
* mkdir.c, mkdirat.c, mkstemp-safer.c, mkstemp.c, modechange.c:
* mountlist.c, nanosleep.c, obstack.c, open-safer.c:
* openat-die.c, openat.c, pagealign_alloc.c, physmem.c:
* pipe-safer.c, posixtm.c, posixver.c, putenv.c, quote.c:
* quotearg.c, raise.c, readtokens.c, readtokens0.c, readutmp.c:
* realloc.c, regex.c, rename.c, rmdir.c, rpmatch.c, safe-read.c:
* same.c, save-cwd.c, savedir.c, setenv.c, settime.c, sha1.c:
* sig2str.c, snprintf.c, strdup.c, strerror.c, strftime.c:
* stripslash.c, strndup.c, strnlen.c, strpbrk.c, strtod.c:
* strtoimax.c, strtol.c, strverscmp.c, tempname.c, time_r.c:
* timegm.c, tmpfile-safer.c, unlinkdir.c, userspec.c, utime.c:
* utimecmp.c, utimens.c, version-etc-fsf.c, version-etc.c:
* xalloc-die.c, xgetcwd.c, xgethostname.c, xmalloc.c:
* xmemcoll.c, xnanosleep.c, xreadlink.c, xstrtod.c:
* xstrtoimax.c, xstrtol.c, xstrtoumax.c, yesno.c:
Likewise.
author | Paul Eggert <eggert@cs.ucla.edu> |
---|---|
date | Wed, 13 Sep 2006 22:38:14 +0000 |
parents | 43d80ebad26f |
children | 1c4ed7637c24 |
line wrap: on
line source
/* Waiting for a subprocess to finish. Copyright (C) 2001-2003, 2005 Free Software Foundation, Inc. Written by Bruno Haible <haible@clisp.cons.org>, 2001. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H # include <config.h> #endif /* Specification. */ #include "wait-process.h" #include <errno.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <sys/types.h> #if defined _MSC_VER || defined __MINGW32__ /* Native Woe32 API. */ #include <process.h> #define waitpid(pid,statusp,options) _cwait (statusp, pid, WAIT_CHILD) #define WAIT_T int #define WTERMSIG(x) ((x) & 0xff) /* or: SIGABRT ?? */ #define WCOREDUMP(x) 0 #define WEXITSTATUS(x) (((x) >> 8) & 0xff) /* or: (x) ?? */ #define WIFSIGNALED(x) (WTERMSIG (x) != 0) /* or: ((x) == 3) ?? */ #define WIFEXITED(x) (WTERMSIG (x) == 0) /* or: ((x) != 3) ?? */ #define WIFSTOPPED(x) 0 #else /* Unix API. */ #include <sys/wait.h> /* On Linux, WEXITSTATUS are bits 15..8 and WTERMSIG are bits 7..0, while BeOS uses the contrary. Therefore we use the abstract macros. */ #if HAVE_UNION_WAIT # define WAIT_T union wait # ifndef WTERMSIG # define WTERMSIG(x) ((x).w_termsig) # endif # ifndef WCOREDUMP # define WCOREDUMP(x) ((x).w_coredump) # endif # ifndef WEXITSTATUS # define WEXITSTATUS(x) ((x).w_retcode) # endif #else # define WAIT_T int # ifndef WTERMSIG # define WTERMSIG(x) ((x) & 0x7f) # endif # ifndef WCOREDUMP # define WCOREDUMP(x) ((x) & 0x80) # endif # ifndef WEXITSTATUS # define WEXITSTATUS(x) (((x) >> 8) & 0xff) # endif #endif /* For valid x, exactly one of WIFSIGNALED(x), WIFEXITED(x), WIFSTOPPED(x) is true. */ #ifndef WIFSIGNALED # define WIFSIGNALED(x) (WTERMSIG (x) != 0 && WTERMSIG(x) != 0x7f) #endif #ifndef WIFEXITED # define WIFEXITED(x) (WTERMSIG (x) == 0) #endif #ifndef WIFSTOPPED # define WIFSTOPPED(x) (WTERMSIG (x) == 0x7f) #endif /* Note that portable applications may access WTERMSIG(x) only if WIFSIGNALED(x) is true, and WEXITSTATUS(x) only if WIFEXITED(x) is true. */ #endif #include "error.h" #include "exit.h" #include "fatal-signal.h" #include "xalloc.h" #include "gettext.h" #define _(str) gettext (str) #define SIZEOF(a) (sizeof(a) / sizeof(a[0])) #if defined _MSC_VER || defined __MINGW32__ #define WIN32_LEAN_AND_MEAN #include <windows.h> /* The return value of spawnvp() is really a process handle as returned by CreateProcess(). Therefore we can kill it using TerminateProcess. */ #define kill(pid,sig) TerminateProcess ((HANDLE) (pid), sig) #endif /* Type of an entry in the slaves array. The 'used' bit determines whether this entry is currently in use. (If pid_t was an atomic type like sig_atomic_t, we could just set the 'child' field to 0 when unregistering a slave process, and wouldn't need the 'used' field.) The 'used' and 'child' fields are accessed from within the cleanup_slaves() action, therefore we mark them as 'volatile'. */ typedef struct { volatile sig_atomic_t used; volatile pid_t child; } slaves_entry_t; /* The registered slave subprocesses. */ static slaves_entry_t static_slaves[32]; static slaves_entry_t * volatile slaves = static_slaves; static sig_atomic_t volatile slaves_count = 0; static size_t slaves_allocated = SIZEOF (static_slaves); /* The termination signal for slave subprocesses. 2003-10-07: Terminator becomes Governator. */ #ifdef SIGHUP # define TERMINATOR SIGHUP #else # define TERMINATOR SIGTERM #endif /* The cleanup action. It gets called asynchronously. */ static void cleanup_slaves (void) { for (;;) { /* Get the last registered slave. */ size_t n = slaves_count; if (n == 0) break; n--; slaves_count = n; /* Skip unused entries in the slaves array. */ if (slaves[n].used) { pid_t slave = slaves[n].child; /* Kill the slave. */ kill (slave, TERMINATOR); } } } /* Register a subprocess as being a slave process. This means that the subprocess will be terminated when its creator receives a catchable fatal signal or exits normally. Registration ends when wait_subprocess() notices that the subprocess has exited. */ void register_slave_subprocess (pid_t child) { static bool cleanup_slaves_registered = false; if (!cleanup_slaves_registered) { atexit (cleanup_slaves); at_fatal_signal (cleanup_slaves); cleanup_slaves_registered = true; } /* Try to store the new slave in an unused entry of the slaves array. */ { slaves_entry_t *s = slaves; slaves_entry_t *s_end = s + slaves_count; for (; s < s_end; s++) if (!s->used) { /* The two uses of 'volatile' in the slaves_entry_t type above (and ISO C 99 section 5.1.2.3.(5)) ensure that we mark the entry as used only after the child pid has been written to the memory location s->child. */ s->child = child; s->used = 1; return; } } if (slaves_count == slaves_allocated) { /* Extend the slaves array. Note that we cannot use xrealloc(), because then the cleanup_slaves() function could access an already deallocated array. */ slaves_entry_t *old_slaves = slaves; size_t new_slaves_allocated = 2 * slaves_allocated; slaves_entry_t *new_slaves = malloc (new_slaves_allocated * sizeof (slaves_entry_t)); if (new_slaves == NULL) { /* xalloc_die() will call exit() which will invoke cleanup_slaves(). Additionally we need to kill child, because it's not yet among the slaves list. */ kill (child, TERMINATOR); xalloc_die (); } memcpy (new_slaves, old_slaves, slaves_allocated * sizeof (slaves_entry_t)); slaves = new_slaves; slaves_allocated = new_slaves_allocated; /* Now we can free the old slaves array. */ if (old_slaves != static_slaves) free (old_slaves); } /* The three uses of 'volatile' in the types above (and ISO C 99 section 5.1.2.3.(5)) ensure that we increment the slaves_count only after the new slave and its 'used' bit have been written to the memory locations that make up slaves[slaves_count]. */ slaves[slaves_count].child = child; slaves[slaves_count].used = 1; slaves_count++; } /* Unregister a child from the list of slave subprocesses. */ static inline void unregister_slave_subprocess (pid_t child) { /* The easiest way to remove an entry from a list that can be used by an asynchronous signal handler is just to mark it as unused. For this, we rely on sig_atomic_t. */ slaves_entry_t *s = slaves; slaves_entry_t *s_end = s + slaves_count; for (; s < s_end; s++) if (s->used && s->child == child) s->used = 0; } /* Wait for a subprocess to finish. Return its exit code. If it didn't terminate correctly, exit if exit_on_error is true, otherwise return 127. */ int wait_subprocess (pid_t child, const char *progname, bool ignore_sigpipe, bool null_stderr, bool slave_process, bool exit_on_error) { #if HAVE_WAITID && defined WNOWAIT && 0 /* Commented out because waitid() with WNOWAIT doesn't work: On Solaris 7 and OSF/1 4.0, it returns -1 and sets errno = ECHILD, and on HP-UX 10.20 it just hangs. */ /* Use of waitid() with WNOWAIT avoids a race condition: If slave_process is true, and this process sleeps a very long time between the return from waitpid() and the execution of unregister_slave_subprocess(), and meanwhile another process acquires the same PID as child, and then - still before unregister_slave_subprocess() - this process gets a fatal signal, it would kill the other totally unrelated process. */ siginfo_t info; for (;;) { if (waitid (P_PID, child, &info, slave_process ? WNOWAIT : 0) < 0) { # ifdef EINTR if (errno == EINTR) continue; # endif if (exit_on_error || !null_stderr) error (exit_on_error ? EXIT_FAILURE : 0, errno, _("%s subprocess"), progname); return 127; } /* info.si_code is set to one of CLD_EXITED, CLD_KILLED, CLD_DUMPED, CLD_TRAPPED, CLD_STOPPED, CLD_CONTINUED. Loop until the program terminates. */ if (info.si_code == CLD_EXITED || info.si_code == CLD_KILLED || info.si_code == CLD_DUMPED) break; } /* The child process has exited or was signalled. */ if (slave_process) { /* Unregister the child from the list of slave subprocesses, so that later, when we exit, we don't kill a totally unrelated process which may have acquired the same pid. */ unregister_slave_subprocess (child); /* Now remove the zombie from the process list. */ for (;;) { if (waitid (P_PID, child, &info, 0) < 0) { # ifdef EINTR if (errno == EINTR) continue; # endif if (exit_on_error || !null_stderr) error (exit_on_error ? EXIT_FAILURE : 0, errno, _("%s subprocess"), progname); return 127; } break; } } switch (info.si_code) { case CLD_KILLED: case CLD_DUMPED: # ifdef SIGPIPE if (info.si_status == SIGPIPE && ignore_sigpipe) return 0; # endif if (exit_on_error || !null_stderr) error (exit_on_error ? EXIT_FAILURE : 0, 0, _("%s subprocess got fatal signal %d"), progname, info.si_status); return 127; case CLD_EXITED: if (info.si_status == 127) { if (exit_on_error || !null_stderr) error (exit_on_error ? EXIT_FAILURE : 0, 0, _("%s subprocess failed"), progname); return 127; } return info.si_status; default: abort (); } #else /* waitpid() is just as portable as wait() nowadays. */ WAIT_T status; *(int *) &status = 0; for (;;) { int result = waitpid (child, &status, 0); if (result != child) { # ifdef EINTR if (errno == EINTR) continue; # endif # if 0 /* defined ECHILD */ if (errno == ECHILD) { /* Child process nonexistent?! Assume it terminated successfully. */ *(int *) &status = 0; break; } # endif if (exit_on_error || !null_stderr) error (exit_on_error ? EXIT_FAILURE : 0, errno, _("%s subprocess"), progname); return 127; } /* One of WIFSIGNALED (status), WIFEXITED (status), WIFSTOPPED (status) must always be true. Loop until the program terminates. */ if (!WIFSTOPPED (status)) break; } /* The child process has exited or was signalled. */ if (slave_process) /* Unregister the child from the list of slave subprocesses, so that later, when we exit, we don't kill a totally unrelated process which may have acquired the same pid. */ unregister_slave_subprocess (child); if (WIFSIGNALED (status)) { # ifdef SIGPIPE if (WTERMSIG (status) == SIGPIPE && ignore_sigpipe) return 0; # endif if (exit_on_error || !null_stderr) error (exit_on_error ? EXIT_FAILURE : 0, 0, _("%s subprocess got fatal signal %d"), progname, (int) WTERMSIG (status)); return 127; } if (WEXITSTATUS (status) == 127) { if (exit_on_error || !null_stderr) error (exit_on_error ? EXIT_FAILURE : 0, 0, _("%s subprocess failed"), progname); return 127; } return WEXITSTATUS (status); #endif }