diff --git a/lib/libarchive/archive_read_support_compression_program.c b/lib/libarchive/archive_read_support_compression_program.c index 5f6cb463b21c..5d92b909f7fd 100644 --- a/lib/libarchive/archive_read_support_compression_program.c +++ b/lib/libarchive/archive_read_support_compression_program.c @@ -1,335 +1,342 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /* This capability is only available on POSIX systems. */ -#if !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) +#if !defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ + !(defined(HAVE_FORK) || defined(HAVE_VFORK)) /* * On non-Posix systems, allow the program to build, but choke if * this function is actually invoked. */ int archive_read_support_compression_program(struct archive *_a, const char *cmd) { archive_set_error(_a, -1, "External compression programs not supported on this platform"); return (ARCHIVE_FATAL); } #else #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_LIMITS_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" #include "filter_fork.h" struct archive_decompress_program { char *description; pid_t child; int child_stdin, child_stdout; char *child_out_buf; char *child_out_buf_next; size_t child_out_buf_len, child_out_buf_avail; const char *child_in_buf; size_t child_in_buf_avail; }; static int archive_decompressor_program_bid(const void *, size_t); static int archive_decompressor_program_finish(struct archive_read *); static int archive_decompressor_program_init(struct archive_read *, const void *, size_t); static ssize_t archive_decompressor_program_read_ahead(struct archive_read *, const void **, size_t); static ssize_t archive_decompressor_program_read_consume(struct archive_read *, size_t); int archive_read_support_compression_program(struct archive *_a, const char *cmd) { struct archive_read *a = (struct archive_read *)_a; struct decompressor_t *decompressor; if (cmd == NULL || *cmd == '\0') return (ARCHIVE_WARN); decompressor = __archive_read_register_compression(a, archive_decompressor_program_bid, archive_decompressor_program_init); if (decompressor == NULL) return (ARCHIVE_WARN); decompressor->config = strdup(cmd); return (ARCHIVE_OK); } /* * If the user used us to register, they must really want us to * handle it, so this module always bids INT_MAX. */ static int archive_decompressor_program_bid(const void *buff, size_t len) { (void)buff; /* UNUSED */ (void)len; /* UNUSED */ return (INT_MAX); /* Default: We'll take it. */ } static ssize_t child_read(struct archive_read *a, char *buf, size_t buf_len) { struct archive_decompress_program *state = a->decompressor->data; ssize_t ret, requested; const void *child_buf; if (state->child_stdout == -1) return (-1); if (buf_len == 0) return (-1); restart_read: requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len; do { ret = read(state->child_stdout, buf, requested); } while (ret == -1 && errno == EINTR); if (ret > 0) return (ret); if (ret == 0 || (ret == -1 && errno == EPIPE)) { close(state->child_stdout); state->child_stdout = -1; return (0); } if (ret == -1 && errno != EAGAIN) return (-1); if (state->child_in_buf_avail == 0) { child_buf = state->child_in_buf; ret = (a->client_reader)(&a->archive, a->client_data,&child_buf); state->child_in_buf = (const char *)child_buf; if (ret < 0) { close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); return (-1); } if (ret == 0) { close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); goto restart_read; } state->child_in_buf_avail = ret; } + if (state->child_stdin == -1) { + fcntl(state->child_stdout, F_SETFL, 0); + __archive_check_child(state->child_stdin, state->child_stdout); + goto restart_read; + } + do { ret = write(state->child_stdin, state->child_in_buf, state->child_in_buf_avail); } while (ret == -1 && errno == EINTR); if (ret > 0) { state->child_in_buf += ret; state->child_in_buf_avail -= ret; goto restart_read; } else if (ret == -1 && errno == EAGAIN) { __archive_check_child(state->child_stdin, state->child_stdout); goto restart_read; } else if (ret == 0 || (ret == -1 && errno == EPIPE)) { close(state->child_stdin); - state->child_stdout = -1; + state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); goto restart_read; } else { close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); return (-1); } } static int archive_decompressor_program_init(struct archive_read *a, const void *buff, size_t n) { struct archive_decompress_program *state; const char *cmd = a->decompressor->config; const char *prefix = "Program: "; state = (struct archive_decompress_program *)malloc(sizeof(*state)); if (!state) { archive_set_error(&a->archive, ENOMEM, "Can't allocate input data"); return (ARCHIVE_FATAL); } a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM; state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); strcpy(state->description, prefix); strcat(state->description, cmd); a->archive.compression_name = state->description; state->child_out_buf_next = state->child_out_buf = malloc(65536); if (!state->child_out_buf) { free(state); archive_set_error(&a->archive, ENOMEM, "Can't allocate filter buffer"); return (ARCHIVE_FATAL); } state->child_out_buf_len = 65536; state->child_out_buf_avail = 0; state->child_in_buf = buff; state->child_in_buf_avail = n; if ((state->child = __archive_create_child(cmd, &state->child_stdin, &state->child_stdout)) == -1) { free(state->child_out_buf); free(state); archive_set_error(&a->archive, EINVAL, "Can't initialise filter"); return (ARCHIVE_FATAL); } a->decompressor->data = state; a->decompressor->read_ahead = archive_decompressor_program_read_ahead; a->decompressor->consume = archive_decompressor_program_read_consume; a->decompressor->skip = NULL; a->decompressor->finish = archive_decompressor_program_finish; /* XXX Check that we can read at least one byte? */ return (ARCHIVE_OK); } static ssize_t archive_decompressor_program_read_ahead(struct archive_read *a, const void **buff, size_t min) { struct archive_decompress_program *state; ssize_t bytes_read; state = (struct archive_decompress_program *)a->decompressor->data; if (min > state->child_out_buf_len) min = state->child_out_buf_len; while (state->child_stdout != -1 && min > state->child_out_buf_avail) { if (state->child_out_buf != state->child_out_buf_next) { memmove(state->child_out_buf, state->child_out_buf_next, state->child_out_buf_avail); state->child_out_buf_next = state->child_out_buf; } bytes_read = child_read(a, state->child_out_buf + state->child_out_buf_avail, state->child_out_buf_len - state->child_out_buf_avail); if (bytes_read == -1) return (-1); if (bytes_read == 0) break; state->child_out_buf_avail += bytes_read; a->archive.raw_position += bytes_read; } *buff = state->child_out_buf_next; return (state->child_out_buf_avail); } static ssize_t archive_decompressor_program_read_consume(struct archive_read *a, size_t request) { struct archive_decompress_program *state; state = (struct archive_decompress_program *)a->decompressor->data; state->child_out_buf_next += request; state->child_out_buf_avail -= request; a->archive.file_position += request; return (request); } static int archive_decompressor_program_finish(struct archive_read *a) { struct archive_decompress_program *state; int status; state = (struct archive_decompress_program *)a->decompressor->data; /* Release our configuration data. */ free(a->decompressor->config); a->decompressor->config = NULL; /* Shut down the child. */ if (state->child_stdin != -1) close(state->child_stdin); if (state->child_stdout != -1) close(state->child_stdout); while (waitpid(state->child, &status, 0) == -1 && errno == EINTR) continue; /* Release our private data. */ free(state->child_out_buf); free(state->description); free(state); a->decompressor->data = NULL; return (ARCHIVE_OK); } #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ diff --git a/lib/libarchive/archive_write_set_compression_program.c b/lib/libarchive/archive_write_set_compression_program.c index b910ef6b4ac3..3cd5c934205b 100644 --- a/lib/libarchive/archive_write_set_compression_program.c +++ b/lib/libarchive/archive_write_set_compression_program.c @@ -1,341 +1,349 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); /* This capability is only available on POSIX systems. */ -#if !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) +#if !defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ + !(defined(HAVE_FORK) || defined(HAVE_VFORK)) +#include "archive.h" /* * On non-Posix systems, allow the program to build, but choke if * this function is actually invoked. */ int archive_write_set_compression_program(struct archive *_a, const char *cmd) { archive_set_error(_a, -1, "External compression programs not supported on this platform"); return (ARCHIVE_FATAL); } #else #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #include "archive.h" #include "archive_private.h" #include "archive_write_private.h" #include "filter_fork.h" struct private_data { char *description; pid_t child; int child_stdin, child_stdout; char *child_buf; size_t child_buf_len, child_buf_avail; }; static int archive_compressor_program_finish(struct archive_write *); static int archive_compressor_program_init(struct archive_write *); static int archive_compressor_program_write(struct archive_write *, const void *, size_t); /* * Allocate, initialize and return a archive object. */ int archive_write_set_compression_program(struct archive *_a, const char *cmd) { struct archive_write *a = (struct archive_write *)_a; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_program"); a->compressor.init = &archive_compressor_program_init; a->compressor.config = strdup(cmd); return (ARCHIVE_OK); } /* * Setup callback. */ static int archive_compressor_program_init(struct archive_write *a) { int ret; struct private_data *state; static const char *prefix = "Program: "; char *cmd = a->compressor.config; if (a->client_opener != NULL) { ret = (a->client_opener)(&a->archive, a->client_data); if (ret != ARCHIVE_OK) return (ret); } state = (struct private_data *)malloc(sizeof(*state)); if (state == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data for compression"); return (ARCHIVE_FATAL); } memset(state, 0, sizeof(*state)); a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM; state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); strcpy(state->description, prefix); strcat(state->description, cmd); a->archive.compression_name = state->description; state->child_buf_len = a->bytes_per_block; state->child_buf_avail = 0; state->child_buf = malloc(state->child_buf_len); if (state->child_buf == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data for compression buffer"); free(state); return (ARCHIVE_FATAL); } if ((state->child = __archive_create_child(cmd, &state->child_stdin, &state->child_stdout)) == -1) { archive_set_error(&a->archive, EINVAL, "Can't initialise filter"); free(state->child_buf); free(state); return (ARCHIVE_FATAL); } a->compressor.write = archive_compressor_program_write; a->compressor.finish = archive_compressor_program_finish; a->compressor.data = state; return (0); } static ssize_t child_write(struct archive_write *a, const char *buf, size_t buf_len) { struct private_data *state = a->compressor.data; ssize_t ret; if (state->child_stdin == -1) return (-1); if (buf_len == 0) return (-1); restart_write: do { ret = write(state->child_stdin, buf, buf_len); } while (ret == -1 && errno == EINTR); if (ret > 0) return (ret); if (ret == 0) { close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); return (0); } if (ret == -1 && errno != EAGAIN) return (-1); + if (state->child_stdout == -1) { + fcntl(state->child_stdin, F_SETFL, 0); + __archive_check_child(state->child_stdin, state->child_stdout); + goto restart_write; + } + do { ret = read(state->child_stdout, state->child_buf + state->child_buf_avail, state->child_buf_len - state->child_buf_avail); } while (ret == -1 && errno == EINTR); if (ret == 0 || (ret == -1 && errno == EPIPE)) { close(state->child_stdout); state->child_stdout = -1; fcntl(state->child_stdin, F_SETFL, 0); goto restart_write; } if (ret == -1 && errno == EAGAIN) { __archive_check_child(state->child_stdin, state->child_stdout); goto restart_write; } if (ret == -1) return (-1); state->child_buf_avail += ret; ret = (a->client_writer)(&a->archive, a->client_data, state->child_buf, state->child_buf_avail); if (ret <= 0) return (-1); if ((size_t)ret < state->child_buf_avail) { memmove(state->child_buf, state->child_buf + ret, state->child_buf_avail - ret); } state->child_buf_avail -= ret; a->archive.raw_position += ret; goto restart_write; } /* * Write data to the compressed stream. */ static int archive_compressor_program_write(struct archive_write *a, const void *buff, size_t length) { struct private_data *state; ssize_t ret; const char *buf; state = (struct private_data *)a->compressor.data; if (a->client_writer == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "No write callback is registered? " "This is probably an internal programming error."); return (ARCHIVE_FATAL); } buf = buff; while (length > 0) { ret = child_write(a, buf, length); if (ret == -1 || ret == 0) { archive_set_error(&a->archive, EIO, "Can't write to filter"); return (ARCHIVE_FATAL); } length -= ret; buf += ret; } a->archive.file_position += length; return (ARCHIVE_OK); } /* * Finish the compression... */ static int archive_compressor_program_finish(struct archive_write *a) { int ret, status; ssize_t bytes_read, bytes_written; struct private_data *state; state = (struct private_data *)a->compressor.data; ret = 0; if (a->client_writer == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "No write callback is registered? " "This is probably an internal programming error."); ret = ARCHIVE_FATAL; goto cleanup; } /* XXX pad compressed data. */ close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); for (;;) { do { bytes_read = read(state->child_stdout, state->child_buf + state->child_buf_avail, state->child_buf_len - state->child_buf_avail); } while (bytes_read == -1 && errno == EINTR); if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE)) break; if (bytes_read == -1) { archive_set_error(&a->archive, errno, "Read from filter failed unexpectedly."); ret = ARCHIVE_FATAL; goto cleanup; } state->child_buf_avail += bytes_read; bytes_written = (a->client_writer)(&a->archive, a->client_data, state->child_buf, state->child_buf_avail); if (bytes_written <= 0) { ret = ARCHIVE_FATAL; goto cleanup; } if ((size_t)bytes_written < state->child_buf_avail) { memmove(state->child_buf, state->child_buf + bytes_written, state->child_buf_avail - bytes_written); } state->child_buf_avail -= bytes_written; a->archive.raw_position += bytes_written; } /* XXX pad final compressed block. */ cleanup: /* Shut down the child. */ if (state->child_stdin != -1) close(state->child_stdin); if (state->child_stdout != -1) close(state->child_stdout); while (waitpid(state->child, &status, 0) == -1 && errno == EINTR) continue; if (status != 0) { archive_set_error(&a->archive, EIO, "Filter exited with failure."); ret = ARCHIVE_FATAL; } /* Release our configuration data. */ free(a->compressor.config); a->compressor.config = NULL; /* Release our private state data. */ free(state->child_buf); free(state->description); free(state); return (ret); } #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ diff --git a/lib/libarchive/filter_fork.c b/lib/libarchive/filter_fork.c index dde6114b6ba0..caf8c315c3a3 100644 --- a/lib/libarchive/filter_fork.c +++ b/lib/libarchive/filter_fork.c @@ -1,144 +1,161 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" /* This capability is only available on POSIX systems. */ -#if defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) +#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \ + (defined(HAVE_FORK) || defined(HAVE_VFORK)) __FBSDID("$FreeBSD$"); #if defined(HAVE_POLL) # if defined(HAVE_POLL_H) # include # elif defined(HAVE_SYS_POLL_H) # include # endif #elif defined(HAVE_SELECT) # if defined(HAVE_SYS_SELECT_H) # include # elif defined(HAVE_UNISTD_H) # include # endif #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "filter_fork.h" pid_t __archive_create_child(const char *path, int *child_stdin, int *child_stdout) { pid_t child; int stdin_pipe[2], stdout_pipe[2], tmp; if (pipe(stdin_pipe) == -1) goto state_allocated; if (stdin_pipe[0] == STDOUT_FILENO) { if ((tmp = dup(stdin_pipe[0])) == -1) goto stdin_opened; close(stdin_pipe[0]); stdin_pipe[0] = tmp; } if (pipe(stdout_pipe) == -1) goto stdin_opened; if (stdout_pipe[1] == STDIN_FILENO) { if ((tmp = dup(stdout_pipe[1])) == -1) goto stdout_opened; close(stdout_pipe[1]); stdout_pipe[1] = tmp; } +#if HAVE_VFORK switch ((child = vfork())) { +#else + switch ((child = fork())) { +#endif case -1: goto stdout_opened; case 0: close(stdin_pipe[1]); close(stdout_pipe[0]); if (dup2(stdin_pipe[0], STDIN_FILENO) == -1) _exit(254); if (stdin_pipe[0] != STDIN_FILENO) close(stdin_pipe[0]); if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1) _exit(254); if (stdout_pipe[1] != STDOUT_FILENO) close(stdout_pipe[1]); execlp(path, path, (char *)NULL); _exit(254); default: close(stdin_pipe[0]); close(stdout_pipe[1]); *child_stdin = stdin_pipe[1]; fcntl(*child_stdin, F_SETFL, O_NONBLOCK); *child_stdout = stdout_pipe[0]; fcntl(*child_stdout, F_SETFL, O_NONBLOCK); } return child; stdout_opened: close(stdout_pipe[0]); close(stdout_pipe[1]); stdin_opened: close(stdin_pipe[0]); close(stdin_pipe[1]); state_allocated: return -1; } void __archive_check_child(int in, int out) { #if defined(HAVE_POLL) struct pollfd fds[2]; + int idx; - fds[0].fd = in; - fds[0].events = POLLOUT; - fds[1].fd = out; - fds[1].events = POLLIN; + idx = 0; + if (in != -1) { + fds[idx].fd = in; + fds[idx].events = POLLOUT; + ++idx; + } + if (out != -1) { + fds[idx].fd = out; + fds[idx].events = POLLIN; + ++idx; + } - poll(fds, 2, -1); /* -1 == INFTIM, wait forever */ + poll(fds, idx, -1); /* -1 == INFTIM, wait forever */ #elif defined(HAVE_SELECT) fd_set fds_in, fds_out, fds_error; FD_ZERO(&fds_in); - FD_SET(out, &fds_in); FD_ZERO(&fds_out); - FD_SET(in, &fds_out); FD_ZERO(&fds_error); - FD_SET(in, &fds_error); - FD_SET(out, &fds_error); + if (out != -1) { + FD_SET(out, &fds_in); + FD_SET(out, &fds_error); + } + if (in != -1) { + FD_SET(in, &fds_out); + FD_SET(in, &fds_error); + } select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL); #else sleep(1); #endif } #endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */