Preserve errno after the output write, and if the output pipe is closed, propagate this to the main loop so it does not wait forever for the pipe buffer to empty.
This commit is contained in:
@@ -175,6 +175,7 @@ struct pvstate_s {
|
||||
volatile sig_atomic_t clear_tty_tostop_on_exit; /* whether to clear tty TOSTOP on exit */
|
||||
volatile sig_atomic_t suspend_stderr; /* whether writing to stderr is suspended */
|
||||
volatile sig_atomic_t skip_next_sigcont; /* whether to ignore the next SIGCONT */
|
||||
volatile sig_atomic_t pipe_closed; /* whether the output pipe was closed */
|
||||
} flag;
|
||||
|
||||
/*****************
|
||||
|
||||
+8
-3
@@ -95,12 +95,12 @@ int pv_main_loop(pvstate_t state)
|
||||
struct stat sb;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
if (0 == fstat(output_fd, &sb)) {
|
||||
/*@-type@*/
|
||||
/*@-type@ */
|
||||
if ((sb.st_mode & S_IFMT) == S_IFIFO) {
|
||||
output_is_pipe = true;
|
||||
debug("%s", "output is a pipe");
|
||||
}
|
||||
/*@+type@*/ /* splint says st_mode is __mode_t, not mode_t */
|
||||
/*@+type@ *//* splint says st_mode is __mode_t, not mode_t */
|
||||
} else {
|
||||
debug("%s(%d): %s", "fstat", output_fd, strerror(errno));
|
||||
}
|
||||
@@ -292,7 +292,12 @@ int pv_main_loop(pvstate_t state)
|
||||
if (output_is_pipe) {
|
||||
int nbytes;
|
||||
nbytes = 0;
|
||||
if (0 == ioctl(output_fd, FIONREAD, &nbytes)) {
|
||||
if (0 != state->flag.pipe_closed) {
|
||||
if (0 != state->transfer.written_but_not_consumed)
|
||||
debug("%s",
|
||||
"clearing written_but_not_consumed because the output pipe was closed");
|
||||
state->transfer.written_but_not_consumed = 0;
|
||||
} else if (0 == ioctl(output_fd, FIONREAD, &nbytes)) {
|
||||
if (nbytes >= 0) {
|
||||
if (((size_t) nbytes) != state->transfer.written_but_not_consumed)
|
||||
debug("%s: %d", "written_but_not_consumed is now", nbytes);
|
||||
|
||||
+8
-4
@@ -636,6 +636,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
|
||||
static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long *lineswritten)
|
||||
{
|
||||
ssize_t nwritten;
|
||||
int write_errno;
|
||||
|
||||
if (NULL == state->transfer.transfer_buffer) {
|
||||
pv_error(state, "%s", _("no transfer buffer allocated"));
|
||||
@@ -646,6 +647,7 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
|
||||
}
|
||||
|
||||
nwritten = 0;
|
||||
write_errno = 0;
|
||||
|
||||
if (state->control.discard_input) {
|
||||
nwritten = state->transfer.to_write;
|
||||
@@ -693,6 +695,7 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
|
||||
(size_t) (state->transfer.to_write),
|
||||
state->control.sync_after_write);
|
||||
if (nwritten < 0) {
|
||||
write_errno = (int) errno;
|
||||
debug("%s: %ld: %s", "bytes written", (long) nwritten, strerror(errno));
|
||||
} else {
|
||||
debug("%s: %ld", "bytes written", (long) nwritten);
|
||||
@@ -808,11 +811,11 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
|
||||
* blocked on first write such that nwritten == 0, just wait a bit and
|
||||
* then return zero, since this was a transient error.
|
||||
*/
|
||||
if ((0 == nwritten) || (EINTR == errno) || (EAGAIN == errno)) {
|
||||
if ((0 == nwritten) || (EINTR == write_errno) || (EAGAIN == write_errno)) {
|
||||
if (0 == nwritten) {
|
||||
debug("%s", "attempted write blocked - waiting briefly");
|
||||
} else {
|
||||
debug("%s: %s", "transient write error - waiting briefly", strerror(errno));
|
||||
debug("%s: %s", "transient write error - waiting briefly", strerror(write_errno));
|
||||
}
|
||||
(void) is_data_ready(-1, NULL, -1, NULL, 10000);
|
||||
return 0;
|
||||
@@ -822,13 +825,14 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
|
||||
* SIGPIPE means we've finished. Don't output an error because it's
|
||||
* not really our error to report.
|
||||
*/
|
||||
if (EPIPE == errno) {
|
||||
if (EPIPE == write_errno) {
|
||||
*eof_in = true;
|
||||
*eof_out = true;
|
||||
state->flag.pipe_closed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pv_error(state, "%s: %s", _("write failed"), strerror(errno));
|
||||
pv_error(state, "%s: %s", _("write failed"), strerror(write_errno));
|
||||
state->status.exit_status |= PV_ERROREXIT_TRANSFER;
|
||||
*eof_out = true;
|
||||
state->transfer.written = -1;
|
||||
|
||||
Reference in New Issue
Block a user