- djm@cvs.openbsd.org 2011/05/08 12:52:01
     [PROTOCOL.mux clientloop.c clientloop.h mux.c]
     improve our behaviour when TTY allocation fails: if we are in
     RequestTTY=auto mode (the default), then do not treat at TTY
     allocation error as fatal but rather just restore the local TTY
     to cooked mode and continue. This is more graceful on devices that
     never allocate TTYs.

     If RequestTTY is set to "yes" or "force", then failure to allocate
     a TTY is fatal.

     ok markus@
diff --git a/clientloop.c b/clientloop.c
index 5bd757d..ed1d8a2 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.233 2011/05/06 21:34:32 djm Exp $ */
+/* $OpenBSD: clientloop.c,v 1.234 2011/05/08 12:52:01 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -174,9 +174,11 @@
 };
 
 /* Context for channel confirmation replies */
+enum confirm_action { CONFIRM_WARN = 0, CONFIRM_CLOSE, CONFIRM_TTY };
 struct channel_reply_ctx {
 	const char *request_type;
-	int id, do_close;
+	int id;
+	enum confirm_action action;
 };
 
 /* Global request success/failure callbacks */
@@ -739,6 +741,15 @@
 	char errmsg[256];
 	int tochan;
 
+	/*
+	 * If a TTY was explicitly requested, then a failure to allocate
+	 * one is fatal.
+	 */
+	if (cr->action == CONFIRM_TTY &&
+	    (options.request_tty == REQUEST_TTY_FORCE ||
+	    options.request_tty == REQUEST_TTY_YES))
+		cr->action = CONFIRM_CLOSE;
+
 	/* XXX supress on mux _client_ quietmode */
 	tochan = options.log_level >= SYSLOG_LEVEL_ERROR &&
 	    c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE;
@@ -756,14 +767,27 @@
 			    cr->request_type, c->self);
 		}
 		/* If error occurred on primary session channel, then exit */
-		if (cr->do_close && c->self == session_ident)
+		if (cr->action == CONFIRM_CLOSE && c->self == session_ident)
 			fatal("%s", errmsg);
-		/* If error occurred on mux client, append to their stderr */
-		if (tochan)
-			buffer_append(&c->extended, errmsg, strlen(errmsg));
-		else
+		/*
+		 * If error occurred on mux client, append to
+		 * their stderr.
+		 */
+		if (tochan) {
+			buffer_append(&c->extended, errmsg,
+			    strlen(errmsg));
+		} else
 			error("%s", errmsg);
-		if (cr->do_close) {
+		if (cr->action == CONFIRM_TTY) {
+			/*
+			 * If a TTY allocation error occurred, then arrange
+			 * for the correct TTY to leave raw mode.
+			 */
+			if (c->self == session_ident)
+				leave_raw_mode(0);
+			else
+				mux_tty_alloc_failed(c);
+		} else if (cr->action == CONFIRM_CLOSE) {
 			chan_read_failed(c);
 			chan_write_failed(c);
 		}
@@ -778,12 +802,13 @@
 }
 
 static void
-client_expect_confirm(int id, const char *request, int do_close)
+client_expect_confirm(int id, const char *request,
+    enum confirm_action action)
 {
 	struct channel_reply_ctx *cr = xmalloc(sizeof(*cr));
 
 	cr->request_type = request;
-	cr->do_close = do_close;
+	cr->action = action;
 
 	channel_register_status_confirm(id, client_status_confirm,
 	    client_abandon_status_confirm, cr);
@@ -1983,7 +2008,7 @@
 			memset(&ws, 0, sizeof(ws));
 
 		channel_request_start(id, "pty-req", 1);
-		client_expect_confirm(id, "PTY allocation", 1);
+		client_expect_confirm(id, "PTY allocation", CONFIRM_TTY);
 		packet_put_cstring(term != NULL ? term : "");
 		packet_put_int((u_int)ws.ws_col);
 		packet_put_int((u_int)ws.ws_row);
@@ -2042,18 +2067,18 @@
 			debug("Sending subsystem: %.*s",
 			    len, (u_char*)buffer_ptr(cmd));
 			channel_request_start(id, "subsystem", 1);
-			client_expect_confirm(id, "subsystem", 1);
+			client_expect_confirm(id, "subsystem", CONFIRM_CLOSE);
 		} else {
 			debug("Sending command: %.*s",
 			    len, (u_char*)buffer_ptr(cmd));
 			channel_request_start(id, "exec", 1);
-			client_expect_confirm(id, "exec", 1);
+			client_expect_confirm(id, "exec", CONFIRM_CLOSE);
 		}
 		packet_put_string(buffer_ptr(cmd), buffer_len(cmd));
 		packet_send();
 	} else {
 		channel_request_start(id, "shell", 1);
-		client_expect_confirm(id, "shell", 1);
+		client_expect_confirm(id, "shell", CONFIRM_CLOSE);
 		packet_send();
 	}
 }