Mercurial > hg > octave-kai > gnulib-hg
view lib/pipe-filter-gi.c @ 16366:bb182ee4a09d
maint: replace FSF snail-mail addresses with URLs
* config/argz.mk, lib/accept4.c, lib/alignof.h, lib/alloca.in.h:
* lib/alphasort.c, lib/arcfour.c, lib/arcfour.h, lib/arctwo.c:
* lib/arctwo.h, lib/argz.c, lib/arpa_inet.in.h, lib/asnprintf.c:
* lib/asprintf.c, lib/assert.in.h, lib/base32.c, lib/base32.h:
* lib/base64.c, lib/base64.h, lib/c-ctype.c, lib/c-ctype.h:
* lib/c-strcase.h, lib/c-strcasecmp.c, lib/c-strncasecmp.c:
* lib/check-version.c, lib/check-version.h, lib/config.charset:
* lib/ctype.in.h, lib/des.c, lib/des.h, lib/dup3.c, lib/errno.in.h:
* lib/float+.h, lib/fnmatch.c, lib/fnmatch.in.h, lib/fnmatch_loop.c:
* lib/fseeko.c, lib/gai_strerror.c, lib/gc-gnulib.c:
* lib/gc-libgcrypt.c, lib/gc-pbkdf2-sha1.c, lib/gc.h:
* lib/getaddrinfo.c, lib/getdelim.c, lib/getfilecon.c, lib/getline.c:
* lib/getlogin_r.c, lib/getpass.c, lib/getpass.h, lib/gettext.h:
* lib/gettimeofday.c, lib/glob.in.h, lib/glthread/cond.c:
* lib/glthread/cond.h, lib/glthread/lock.c, lib/glthread/lock.h:
* lib/glthread/thread.c, lib/glthread/thread.h:
* lib/glthread/threadlib.c, lib/glthread/yield.h, lib/hmac-md5.c:
* lib/hmac-sha1.c, lib/hmac.h, lib/iconv.c, lib/iconv.in.h:
* lib/iconv_close.c, lib/iconv_open.c, lib/inet_ntop.c, lib/isfinite.c:
* lib/isinf.c, lib/iswblank.c, lib/langinfo.in.h, lib/link.c:
* lib/localcharset.c, lib/localcharset.h, lib/lseek.c, lib/malloc.c:
* lib/malloca.c, lib/malloca.h, lib/md2.c, lib/md2.h, lib/md4.c:
* lib/md4.h, lib/md5.c, lib/md5.h, lib/memmem.c, lib/mempcpy.c:
* lib/memset.c, lib/memxor.c, lib/memxor.h, lib/minmax.h, lib/mktime.c:
* lib/msvc-inval.c, lib/msvc-inval.h, lib/msvc-nothrow.c:
* lib/msvc-nothrow.h, lib/netdb.in.h, lib/netinet_in.in.h, lib/nproc.c:
* lib/nproc.h, lib/obstack_printf.c, lib/pathmax.h, lib/pipe.c:
* lib/pipe2.c, lib/poll.c, lib/poll.in.h, lib/printf-args.c:
* lib/printf-args.h, lib/printf-parse.c, lib/printf-parse.h:
* lib/pselect.c, lib/pthread.in.h, lib/pty-private.h, lib/pty.in.h:
* lib/read-file.c, lib/read-file.h, lib/ref-add.sin, lib/ref-del.sin:
* lib/regcomp.c, lib/regex.c, lib/regex.h, lib/regex_internal.c:
* lib/regex_internal.h, lib/regexec.c, lib/rijndael-alg-fst.c:
* lib/rijndael-alg-fst.h, lib/rijndael-api-fst.c:
* lib/rijndael-api-fst.h, lib/rint.c, lib/rintf.c, lib/rintl.c:
* lib/round.c, lib/roundf.c, lib/roundl.c, lib/scandir.c, lib/select.c:
* lib/sha1.c, lib/sha1.h, lib/size_max.h, lib/snprintf.c:
* lib/stdalign.in.h, lib/stdarg.in.h, lib/stdbool.in.h:
* lib/stddef.in.h, lib/stdint.in.h, lib/stdio.in.h, lib/str-kmp.h:
* lib/str-two-way.h, lib/strcasecmp.c, lib/strcasestr.c, lib/strdup.c:
* lib/striconv.c, lib/striconv.h, lib/string.in.h, lib/strings.in.h:
* lib/strncasecmp.c, lib/strndup.c, lib/strnlen.c, lib/strpbrk.c:
* lib/strptime.c, lib/strsep.c, lib/strstr.c, lib/strverscmp.c:
* lib/sys_file.in.h, lib/sys_ioctl.in.h, lib/sys_select.in.h:
* lib/sys_socket.in.h, lib/sys_stat.in.h, lib/sys_time.in.h:
* lib/sys_times.in.h, lib/sys_types.in.h, lib/sys_uio.in.h:
* lib/sys_utsname.in.h, lib/sys_wait.in.h, lib/tcgetsid.c:
* lib/termios.in.h, lib/time.in.h, lib/time_r.c, lib/timegm.c:
* lib/times.c, lib/unictype/3level.h, lib/unictype/3levelbit.h:
* lib/unistd.in.h, lib/vasnprintf.c, lib/vasnprintf.h, lib/vasprintf.c:
* lib/vsnprintf.c, lib/waitpid.c, lib/wchar.in.h, lib/wctype.in.h:
* lib/xsize.h, tests/test-closein.c, tests/test-des.c:
* tests/test-fclose.c, tests/test-fgetc.c, tests/test-filevercmp.c:
* tests/test-fputc.c, tests/test-fread.c, tests/test-fwrite.c:
* tests/test-gc-arcfour.c, tests/test-gc-arctwo.c, tests/test-gc-des.c:
* tests/test-gc-hmac-md5.c, tests/test-gc-hmac-sha1.c:
* tests/test-gc-md2.c, tests/test-gc-md4.c, tests/test-gc-md5.c:
* tests/test-gc-pbkdf2-sha1.c, tests/test-gc-rijndael.c:
* tests/test-gc-sha1.c, tests/test-gc.c, tests/test-getdelim.c:
* tests/test-getline.c, tests/test-getndelim2.c, tests/test-md2.c:
* tests/test-md4.c, tests/test-parse-datetime.c, tests/test-perror.c:
* tests/test-perror2.c, tests/test-pipe.c, tests/test-pipe2.c:
* tests/test-poll.c, tests/test-quotearg-simple.c:
* tests/test-quotearg.c, tests/test-quotearg.h:
* tests/test-round-ieee.c, tests/test-round1.c:
* tests/test-roundf-ieee.c, tests/test-roundf1.c:
* tests/test-roundl-ieee.c, tests/test-roundl.c:
* tests/test-safe-alloc.c, tests/test-sigpipe.c:
* tests/test-spawn-pipe-child.c, tests/test-spawn-pipe-main.c:
* tests/test-strerror.c, tests/test-strerror_r.c:
* tests/test-strsignal.c, tests/test-strverscmp.c:
* tests/test-xmemdup0.c:
Replace FSF snail mail addresses with URLs, as per GNU coding
standards. See glibc bug
<http://sourceware.org/bugzilla/show_bug.cgi?id=13673>.
author | Paul Eggert <eggert@cs.ucla.edu> |
---|---|
date | Thu, 09 Feb 2012 21:39:05 -0800 |
parents | 59c686e5b2df |
children | 99fa3b05f1c8 |
line wrap: on
line source
/* Filtering of data through a subprocess. Copyright (C) 2001-2003, 2008-2012 Free Software Foundation, Inc. Written by Paolo Bonzini <bonzini@gnu.org>, 2009, and Bruno Haible <bruno@clisp.org>, 2009. 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */ #include <config.h> #include "pipe-filter.h" #include <errno.h> #include <fcntl.h> #include <stdbool.h> #include <stdint.h> #include <stdlib.h> #include <unistd.h> #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ # include <windows.h> #else # include <signal.h> # include <sys/select.h> #endif #include "error.h" #include "spawn-pipe.h" #include "wait-process.h" #include "xalloc.h" #include "gettext.h" #define _(str) gettext (str) #include "pipe-filter-aux.h" struct pipe_filter_gi { /* Arguments passed to pipe_filter_gi_create. */ const char *progname; bool null_stderr; bool exit_on_error; prepare_read_fn prepare_read; done_read_fn done_read; void *private_data; /* Management of the subprocess. */ pid_t child; int fd[2]; bool exited; int exitstatus; /* Status of the writer part. */ volatile bool writer_terminated; int writer_errno; /* Status of the reader part. */ volatile bool reader_terminated; volatile int reader_errno; #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ CRITICAL_SECTION lock; /* protects the volatile fields */ HANDLE reader_thread_handle; #else struct sigaction orig_sigpipe_action; fd_set readfds; /* All bits except fd[0] are always cleared. */ fd_set writefds; /* All bits except fd[1] are always cleared. */ #endif }; /* Platform dependent functions. */ /* Perform additional initializations. Return 0 if successful, -1 upon failure. */ static inline int filter_init (struct pipe_filter_gi *filter); /* Write count bytes starting at buf, while at the same time invoking the read iterator (the functions prepare_read/done_read) when needed. */ static void filter_loop (struct pipe_filter_gi *filter, const char *wbuf, size_t count); /* Perform cleanup actions at the end. finish_reading is true if there was no error, or false if some error occurred already. */ static inline void filter_cleanup (struct pipe_filter_gi *filter, bool finish_reading); #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ /* Native Windows API. */ static unsigned int WINAPI reader_thread_func (void *thread_arg) { struct pipe_filter_gi *filter = (struct pipe_filter_gi *) thread_arg; for (;;) { size_t bufsize; void *buf = filter->prepare_read (&bufsize, filter->private_data); if (!(buf != NULL && bufsize > 0)) /* prepare_read returned wrong values. */ abort (); { ssize_t nread = read (filter->fd[0], buf, bufsize > SSIZE_MAX ? SSIZE_MAX : bufsize); EnterCriticalSection (&filter->lock); /* If the writer already encountered an error, terminate. */ if (filter->writer_terminated) break; if (nread < 0) { filter->reader_errno = errno; break; } else if (nread > 0) filter->done_read (buf, nread, filter->private_data); else /* nread == 0 */ break; LeaveCriticalSection (&filter->lock); } } filter->reader_terminated = true; LeaveCriticalSection (&filter->lock); _endthreadex (0); /* calls ExitThread (0) */ abort (); } static inline int filter_init (struct pipe_filter_gi *filter) { InitializeCriticalSection (&filter->lock); EnterCriticalSection (&filter->lock); filter->reader_thread_handle = (HANDLE) _beginthreadex (NULL, 100000, reader_thread_func, filter, 0, NULL); if (filter->reader_thread_handle == NULL) { if (filter->exit_on_error) error (EXIT_FAILURE, 0, _("creation of reading thread failed")); return -1; } else return 0; } static void filter_loop (struct pipe_filter_gi *filter, const char *wbuf, size_t count) { if (!filter->writer_terminated) { for (;;) { ssize_t nwritten; /* Allow the reader thread to continue. */ LeaveCriticalSection (&filter->lock); nwritten = write (filter->fd[1], wbuf, count > SSIZE_MAX ? SSIZE_MAX : count); /* Get the lock back from the reader thread. */ EnterCriticalSection (&filter->lock); if (nwritten < 0) { /* Don't assume that the gnulib modules 'write' and 'sigpipe' are used. */ if (GetLastError () == ERROR_NO_DATA) errno = EPIPE; filter->writer_errno = errno; filter->writer_terminated = true; break; } else if (nwritten > 0) { count -= nwritten; if (count == 0) break; wbuf += nwritten; } else /* nwritten == 0 */ { filter->writer_terminated = true; break; } } } } static inline void filter_cleanup (struct pipe_filter_gi *filter, bool finish_reading) { if (finish_reading) { LeaveCriticalSection (&filter->lock); WaitForSingleObject (filter->reader_thread_handle, INFINITE); } else TerminateThread (filter->reader_thread_handle, 1); CloseHandle (filter->reader_thread_handle); DeleteCriticalSection (&filter->lock); } #else /* Unix API. */ static inline int filter_init (struct pipe_filter_gi *filter) { #if !((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__) /* When we write to the child process and it has just terminated, we don't want to die from a SIGPIPE signal. So set the SIGPIPE handler to SIG_IGN, and handle EPIPE error codes in write(). */ { struct sigaction sigpipe_action; sigpipe_action.sa_handler = SIG_IGN; sigpipe_action.sa_flags = 0; sigemptyset (&sigpipe_action.sa_mask); if (sigaction (SIGPIPE, &sigpipe_action, &filter->orig_sigpipe_action) < 0) abort (); } #endif /* Enable non-blocking I/O. This permits the read() and write() calls to return -1/EAGAIN without blocking; this is important for polling if HAVE_SELECT is not defined. It also permits the read() and write() calls to return after partial reads/writes; this is important if HAVE_SELECT is defined, because select() only says that some data can be read or written, not how many. Without non-blocking I/O, Linux 2.2.17 and BSD systems prefer to block instead of returning with partial results. */ { int fcntl_flags; if ((fcntl_flags = fcntl (filter->fd[1], F_GETFL, 0)) < 0 || fcntl (filter->fd[1], F_SETFL, fcntl_flags | O_NONBLOCK) == -1 || (fcntl_flags = fcntl (filter->fd[0], F_GETFL, 0)) < 0 || fcntl (filter->fd[0], F_SETFL, fcntl_flags | O_NONBLOCK) == -1) { if (filter->exit_on_error) error (EXIT_FAILURE, errno, _("cannot set up nonblocking I/O to %s subprocess"), filter->progname); return -1; } } FD_ZERO (&filter->readfds); FD_ZERO (&filter->writefds); return 0; } static void filter_loop (struct pipe_filter_gi *filter, const char *wbuf, size_t count) { /* This function is used in two situations: - in order to write some data to the subprocess [done_writing = false], - in order to read the remaining data after everything was written [done_writing = true]. In this case buf is NULL and count is ignored. */ bool done_writing = (wbuf == NULL); if (!done_writing) { if (filter->writer_terminated || filter->reader_terminated) /* pipe_filter_gi_write was called when it should not be. */ abort (); } else { if (filter->reader_terminated) return; } /* Loop, trying to write the given buffer or reading, whichever is possible. */ for (;;) { /* Here filter->writer_terminated is false. When it becomes true, this loop is terminated. */ /* Whereas filter->reader_terminated is initially false but may become true during this loop. */ /* Here, if !done_writing, count > 0. When count becomes 0, this loop is terminated. */ /* Here, if done_writing, filter->reader_terminated is false. When filter->reader_terminated becomes true, this loop is terminated. */ # if HAVE_SELECT int n; /* See whether reading or writing is possible. */ n = 1; if (!filter->reader_terminated) { FD_SET (filter->fd[0], &filter->readfds); n = filter->fd[0] + 1; } if (!done_writing) { FD_SET (filter->fd[1], &filter->writefds); if (n <= filter->fd[1]) n = filter->fd[1] + 1; } n = select (n, (!filter->reader_terminated ? &filter->readfds : NULL), (!done_writing ? &filter->writefds : NULL), NULL, NULL); if (n < 0) { if (filter->exit_on_error) error (EXIT_FAILURE, errno, _("communication with %s subprocess failed"), filter->progname); filter->writer_errno = errno; filter->writer_terminated = true; break; } if (!done_writing && FD_ISSET (filter->fd[1], &filter->writefds)) goto try_write; if (!filter->reader_terminated && FD_ISSET (filter->fd[0], &filter->readfds)) goto try_read; /* How could select() return if none of the two descriptors is ready? */ abort (); # endif /* Attempt to write. */ # if HAVE_SELECT try_write: # endif if (!done_writing) { ssize_t nwritten = write (filter->fd[1], wbuf, count > SSIZE_MAX ? SSIZE_MAX : count); if (nwritten < 0) { if (!IS_EAGAIN (errno)) { if (filter->exit_on_error) error (EXIT_FAILURE, errno, _("write to %s subprocess failed"), filter->progname); filter->writer_errno = errno; filter->writer_terminated = true; break; } } else if (nwritten > 0) { count -= nwritten; if (count == 0) break; wbuf += nwritten; } } # if HAVE_SELECT continue; # endif /* Attempt to read. */ # if HAVE_SELECT try_read: # endif if (!filter->reader_terminated) { size_t bufsize; void *buf = filter->prepare_read (&bufsize, filter->private_data); if (!(buf != NULL && bufsize > 0)) /* prepare_read returned wrong values. */ abort (); { ssize_t nread = read (filter->fd[0], buf, bufsize > SSIZE_MAX ? SSIZE_MAX : bufsize); if (nread < 0) { if (!IS_EAGAIN (errno)) { if (filter->exit_on_error) error (EXIT_FAILURE, errno, _("read from %s subprocess failed"), filter->progname); filter->reader_errno = errno; filter->reader_terminated = true; break; } } else if (nread > 0) filter->done_read (buf, nread, filter->private_data); else /* nread == 0 */ { filter->reader_terminated = true; if (done_writing) break; } } } # if HAVE_SELECT continue; # endif } } static void filter_cleanup (struct pipe_filter_gi *filter, bool finish_reading) { if (finish_reading) /* A select loop, with done_writing = true. */ filter_loop (filter, NULL, 0); if (sigaction (SIGPIPE, &filter->orig_sigpipe_action, NULL) < 0) abort (); } #endif /* Terminate the child process. Do nothing if it already exited. */ static void filter_terminate (struct pipe_filter_gi *filter) { if (!filter->exited) { /* Tell the child there is nothing more the parent will send. */ close (filter->fd[1]); filter_cleanup (filter, !filter->reader_terminated); close (filter->fd[0]); filter->exitstatus = wait_subprocess (filter->child, filter->progname, true, filter->null_stderr, true, filter->exit_on_error, NULL); if (filter->exitstatus != 0 && filter->exit_on_error) error (EXIT_FAILURE, 0, _("subprocess %s terminated with exit code %d"), filter->progname, filter->exitstatus); filter->exited = true; } } /* After filter_terminate: Return 0 upon success, or (only if exit_on_error is false): - -1 with errno set upon failure, - the positive exit code of the subprocess if that failed. */ static inline int filter_retcode (struct pipe_filter_gi *filter) { if (filter->writer_errno != 0) { errno = filter->writer_errno; return -1; } else if (filter->reader_errno != 0) { errno = filter->reader_errno; return -1; } else return filter->exitstatus; } struct pipe_filter_gi * pipe_filter_gi_create (const char *progname, const char *prog_path, const char **prog_argv, bool null_stderr, bool exit_on_error, prepare_read_fn prepare_read, done_read_fn done_read, void *private_data) { struct pipe_filter_gi *filter; filter = (struct pipe_filter_gi *) xmalloc (sizeof (struct pipe_filter_gi)); /* Open a bidirectional pipe to a subprocess. */ filter->child = create_pipe_bidi (progname, prog_path, (char **) prog_argv, null_stderr, true, exit_on_error, filter->fd); filter->progname = progname; filter->null_stderr = null_stderr; filter->exit_on_error = exit_on_error; filter->prepare_read = prepare_read; filter->done_read = done_read; filter->private_data = private_data; filter->exited = false; filter->exitstatus = 0; filter->writer_terminated = false; filter->writer_errno = 0; filter->reader_terminated = false; filter->reader_errno = 0; if (filter->child == -1) { /* Child process could not be created. Arrange for filter_retcode (filter) to be the current errno. */ filter->writer_errno = errno; filter->writer_terminated = true; filter->exited = true; } else if (filter_init (filter) < 0) filter_terminate (filter); return filter; } int pipe_filter_gi_write (struct pipe_filter_gi *filter, const void *buf, size_t size) { if (buf == NULL) /* Invalid argument. */ abort (); if (filter->exited) return filter_retcode (filter); if (size > 0) { filter_loop (filter, buf, size); if (filter->writer_terminated || filter->reader_terminated) { filter_terminate (filter); return filter_retcode (filter); } } return 0; } int pipe_filter_gi_close (struct pipe_filter_gi *filter) { int ret; int saved_errno; filter_terminate (filter); ret = filter_retcode (filter); saved_errno = errno; free (filter); errno = saved_errno; return ret; }