diff -r 028fa77f952f channel.h
--- a/channel.h	Mon Jul 08 22:43:56 2013 +0800
+++ b/channel.h	Wed Jul 24 21:16:08 2013 -0400
@@ -70,6 +70,10 @@
 	int sent_close, recv_close;
 	int recv_eof, sent_eof;
 
+	/* recv'd eow@openssh.com: remote failed to write to consumer (eg. EPIPE).
+	 * Should show our producer EPIPE. */
+	int recv_eow;
+
 	/* Set after running the ChanType-specific close hander
 	 * to ensure we don't run it twice (nor type->checkclose()). */
 	int close_handler_done;
@@ -115,6 +119,7 @@
 void recv_msg_channel_window_adjust();
 void recv_msg_channel_close();
 void recv_msg_channel_eof();
+void recv_msg_channel_eow(struct Channel *channel);
 
 void common_recv_msg_channel_data(struct Channel *channel, int fd, 
 		circbuffer * buf);
diff -r 028fa77f952f cli-chansession.c
--- a/cli-chansession.c	Mon Jul 08 22:43:56 2013 +0800
+++ b/cli-chansession.c	Wed Jul 24 21:16:08 2013 -0400
@@ -58,6 +58,7 @@
 
 	unsigned char* type = NULL;
 	int wantreply;
+	int success;
 
 	TRACE(("enter cli_chansessreq"))
 
@@ -67,12 +68,25 @@
 	if (strcmp(type, "exit-status") == 0) {
 		cli_ses.retval = buf_getint(ses.payload);
 		TRACE(("got exit-status of '%d'", cli_ses.retval))
+		success = 1;
 	} else if (strcmp(type, "exit-signal") == 0) {
 		TRACE(("got exit-signal, ignoring it"))
+		success = 1;
+	} else if (strcmp(type, "eow@openssh.com") == 0) {
+		/* TODO: refactor this up into recv_msg_channel_request */
+		recv_msg_channel_eow(channel);
+		success = 1;
 	} else {
 		TRACE(("unknown request '%s'", type))
-		send_msg_channel_failure(channel);
-		goto out;
+		success = 0;
+	}
+
+	if (wantreply) {
+		if (success) {
+			send_msg_channel_success(channel);
+		} else {
+			send_msg_channel_failure(channel);
+		}
 	}
 		
 out:
diff -r 028fa77f952f cli-session.c
--- a/cli-session.c	Mon Jul 08 22:43:56 2013 +0800
+++ b/cli-session.c	Wed Jul 24 21:16:08 2013 -0400
@@ -126,12 +126,18 @@
 	cli_ses.winchange = 0;
 
 	/* We store std{in,out,err}'s flags, so we can set them back on exit
-	 * (otherwise busybox's ash isn't happy */
-	cli_ses.stdincopy = dup(STDIN_FILENO);
+	 * (otherwise busybox's ash isn't happy
+	 *
+	 * XXX: This interferes with propagating EPIPE to producer. When remote sees
+	 * EPIPE, we close(1), but dup(1) holds a ref so pipe is never really closed,
+	 * producer never sees EPIPE.
+	 *
+	 * TODO: Figure out whether busybox ash really still needs this. */
+	cli_ses.stdincopy = -1; //dup(STDIN_FILENO);
 	cli_ses.stdinflags = fcntl(STDIN_FILENO, F_GETFL, 0);
-	cli_ses.stdoutcopy = dup(STDOUT_FILENO);
+	cli_ses.stdoutcopy = -1; //dup(STDOUT_FILENO);
 	cli_ses.stdoutflags = fcntl(STDOUT_FILENO, F_GETFL, 0);
-	cli_ses.stderrcopy = dup(STDERR_FILENO);
+	cli_ses.stderrcopy = -1; //dup(STDERR_FILENO);
 	cli_ses.stderrflags = fcntl(STDERR_FILENO, F_GETFL, 0);
 
 	cli_ses.retval = EXIT_SUCCESS; /* Assume it's clean if we don't get a
diff -r 028fa77f952f common-channel.c
--- a/common-channel.c	Mon Jul 08 22:43:56 2013 +0800
+++ b/common-channel.c	Wed Jul 24 21:16:08 2013 -0400
@@ -46,6 +46,7 @@
 		unsigned int incr);
 static void send_msg_channel_data(struct Channel *channel, int isextended);
 static void send_msg_channel_eof(struct Channel *channel);
+static void send_msg_channel_eow(struct Channel *channel);
 static void send_msg_channel_close(struct Channel *channel);
 static void remove_channel(struct Channel *channel);
 static void check_in_progress(struct Channel *channel);
@@ -146,6 +147,7 @@
 	newchan->index = i;
 	newchan->sent_close = newchan->recv_close = 0;
 	newchan->sent_eof = newchan->recv_eof = 0;
+	newchan->recv_eow = 0;
 	newchan->close_handler_done = 0;
 
 	newchan->remotechan = remotechan;
@@ -311,6 +313,18 @@
 		close_chan_fd(channel, channel->writefd, SHUT_WR);
 	}
 
+	/* If remote saw EPIPE during write, propagate to our producer. */
+	if (channel->recv_eow) {
+		if (channel->readfd != FD_CLOSED) {
+			TRACE(("recv eow, closing readfd"))
+			close_chan_fd(channel, channel->readfd, SHUT_RD);
+		}
+		if (ERRFD_IS_READ(channel) && channel->errfd != FD_CLOSED) {
+			TRACE(("recv eow, closing errfd"))
+			close_chan_fd(channel, channel->errfd, SHUT_RD);
+		}
+	}
+
 	/* Special handling for flushing read data after an exit. We
 	   read regardless of whether the select FD was set,
 	   and if there isn't data available, the channel will get closed. */
@@ -415,6 +429,22 @@
 	TRACE(("leave send_msg_channel_eof"))
 }
 
+/* call this when local write fails */
+static void send_msg_channel_eow(struct Channel *channel) {
+
+	TRACE(("enter send_msg_channel_eow"))
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+	buf_putint(ses.writepayload, channel->remotechan);
+	buf_putstring(ses.writepayload, "eow@openssh.com", strlen("eow@openssh.com"));
+	buf_putbyte(ses.writepayload, 0);
+
+	encrypt_packet();
+
+	TRACE(("leave send_msg_channel_eow"))
+}
+
 /* Called to write data out to the local side of the channel. 
  * Only called when we know we can write to a channel, writes as much as
  * possible */
@@ -432,6 +462,7 @@
 		TRACE(("errno %d len %d", errno, len))
 		if (len < 0 && errno != EINTR) {
 			close_chan_fd(channel, fd, SHUT_WR);
+			send_msg_channel_eow(channel);
 		}
 		TRACE(("leave writechannel: len <= 0"))
 		return;
@@ -520,6 +551,22 @@
 }
 
 
+/* handle eow (remote saw error while writing locally), by closing the channel
+ * readfd. This should show our producer EPIPE the next time it tries to write.
+ *
+ * Depending on the producer, this might be benign (producer never writes) or
+ * might cause it to abort because remote isn't consuming anymore. This
+ * preserves the behaviour of a local shell pipeline. */
+void recv_msg_channel_eow(struct Channel *channel) {
+	TRACE(("enter recv_msg_channel_eow %p", channel))
+
+	channel->recv_eow = 1;
+
+	check_close(channel);
+	TRACE(("leave recv_msg_channel_eow"))
+}
+
+
 /* Handle channel closure(), respond in kind and close the channels */
 void recv_msg_channel_close() {
 
diff -r 028fa77f952f svr-chansession.c
--- a/svr-chansession.c	Mon Jul 08 22:43:56 2013 +0800
+++ b/svr-chansession.c	Wed Jul 24 21:16:08 2013 -0400
@@ -364,6 +364,10 @@
 #endif
 	} else if (strcmp(type, "signal") == 0) {
 		ret = sessionsignal(chansess);
+	} else if (strcmp(type, "eow@openssh.com") == 0) {
+		/* TODO: refactor this up into recv_msg_channel_request */
+		recv_msg_channel_eow(channel);
+		ret = DROPBEAR_SUCCESS;
 	} else {
 		/* etc, todo "env", "subsystem" */
 	}
