SSH connection hanging on logout

John Bowman bowman at math.ualberta.ca
Fri May 18 14:52:38 EST 2001


>  			continue;
>  		if (ftab[c->type] == NULL)
>  			continue;
> +		if(c->istate == CHAN_INPUT_OPEN && c->rfd == -1) {
> +			int type=c->type;
> +			c->type=SSH_CHANNEL_CLOSED;
> +			if(channel_find_open() == -1)
> +			  	shutdown(packet_get_connection_out(),
> +					 SHUT_RDWR);

^^^^^^^^
	this cannot be correct. you may _not_ shutdown
	the TCP connection. this breaks
		ssh -N -L 1234:hostb:5678 hosta

> +			c->type=type;
> +			continue;
> +		}

Ah yes, now I see what you mean...

The OpenSSH -N extension to SSH is supposed to hold the connection open
indefinitely (BTW, the man page doesn't make this explicit). That certainly
seems useful. Thanks, for pointing this out, Markus; I didn't understand
at first what you meant by "break". 

Making use of the sleep (-S) option from the patch submitted earlier today,
this feature turned out to be easy to add to the hang-on-exit patch for Linux. 
All I had to do was add the line

if(no_tty_flag && options.sleep < 0) options.sleep=0;

after the options are read in in ssh.c.

Here is the complete patch to 2.9p1 to fix all known hanging problems and
restore the desired behaviour with -N, without data loss, on Linux systems.
This includes my sleep patch and Markus' X-hang patch:

-- John Bowman

University of Alberta
http://www.math.ualberta.ca/~bowman



diff -ur openssh-2.9p1/channels.c openssh-2.9p1J/channels.c
--- openssh-2.9p1/channels.c	Tue Apr 17 12:14:35 2001
+++ openssh-2.9p1J/channels.c	Thu May 17 22:21:05 2001
@@ -1137,6 +1137,15 @@
 			continue;
 		if (ftab[c->type] == NULL)
 			continue;
+		if(c->istate == CHAN_INPUT_OPEN && c->rfd == -1) {
+			int type=c->type;
+			c->type=SSH_CHANNEL_CLOSED;
+			if(channel_find_open() == -1)
+			  	shutdown(packet_get_connection_out(),
+					 SHUT_RDWR);
+			c->type=type;
+			continue;
+		}
 		(*ftab[c->type])(c, readset, writeset);
 		if (chan_is_dead(c)) {
 			/*
diff -ur openssh-2.9p1/clientloop.c openssh-2.9p1J/clientloop.c
--- openssh-2.9p1/clientloop.c	Fri Apr 20 06:50:51 2001
+++ openssh-2.9p1J/clientloop.c	Thu May 17 22:25:45 2001
@@ -121,8 +121,8 @@
 static int connection_in;	/* Connection to server (input). */
 static int connection_out;	/* Connection to server (output). */
 static int need_rekeying;	/* Set to non-zero if rekeying is requested. */
-static int session_closed = 0;	/* In SSH2: login session closed. */
-
+enum SessionStatus {SessionOpen, SessionClose, SessionWait};
+static int session_status = SessionOpen; /* In SSH2: login session closed. */
 void	client_init_dispatch(void);
 int	session_ident = -1;
 
@@ -324,6 +324,10 @@
 client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
     int *maxfdp, int rekeying)
 {
+	struct timeval timer;
+	struct timeval *timerp;
+	int rc;
+	
 	/* Add any selections by the channel mechanism. */
 	channel_prepare_select(readsetp, writesetp, maxfdp, rekeying);
 
@@ -346,7 +350,14 @@
 		if (buffer_len(&stderr_buffer) > 0)
 			FD_SET(fileno(stderr), *writesetp);
 	} else {
-		FD_SET(connection_in, *readsetp);
+		/* channel_prepare_select could have closed the last channel */
+		if ((session_status == SessionClose)
+		    && !channel_still_open()) {
+			if (!packet_have_data_to_write())
+				return;
+		} else {
+			FD_SET(connection_in, *readsetp);
+		}
 	}
 
 	/* Select server connection if have data to write to the server. */
@@ -362,7 +374,16 @@
 	 * SSH_MSG_IGNORE packet when the timeout expires.
 	 */
 
-	if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) {
+	if(session_status == SessionWait && options.sleep > 0) {
+		timer.tv_sec=options.sleep;
+		timer.tv_usec=0;
+		timerp=&timer;
+	} else {
+		timerp=NULL;
+	}
+	
+	rc=select((*maxfdp)+1, *readsetp, *writesetp, NULL, timerp);
+	if (rc < 0) {
 		char buf[100];
 
 		/*
@@ -379,7 +400,8 @@
 		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
 		buffer_append(&stderr_buffer, buf, strlen(buf));
 		quit_pending = 1;
-	}
+	} else if (rc == 0 && session_status == SessionWait)
+		session_status=SessionClose;
 }
 
 void
@@ -440,9 +462,13 @@
 		len = read(connection_in, buf, sizeof(buf));
 		if (len == 0) {
 			/* Received EOF.  The remote host has closed the connection. */
-			snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
-				 host);
-			buffer_append(&stderr_buffer, buf, strlen(buf));
+/* 
+ * This message duplicates the one already in client_loop().
+ *
+ *			snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
+ *				 host);
+ *			buffer_append(&stderr_buffer, buf, strlen(buf));
+ */			
 			quit_pending = 1;
 			return;
 		}
@@ -751,7 +777,7 @@
 	if (id != session_ident)
 		error("client_channel_closed: id %d != session_ident %d",
 		    id, session_ident);
-	session_closed = 1;
+	session_status = (options.sleep >= 0) ? SessionWait : SessionClose;
 	if (in_raw_mode())
 		leave_raw_mode();
 }
@@ -776,6 +802,7 @@
 	start_time = get_current_time();
 
 	/* Initialize variables. */
+	if(!have_pty) session_status=SessionWait;
 	escape_pending = 0;
 	last_was_cr = 1;
 	exit_status = -1;
@@ -840,7 +867,8 @@
 		/* Process buffered packets sent by the server. */
 		client_process_buffered_input_packets();
 
-		if (compat20 && session_closed && !channel_still_open())
+		if (compat20 && (session_status == SessionClose)
+		    && !channel_still_open())
 			break;
 
 		rekeying = (xxx_kex != NULL && !xxx_kex->done);
Only in openssh-2.9p1J: clientloop.c.orig
diff -ur openssh-2.9p1/nchan.c openssh-2.9p1J/nchan.c
--- openssh-2.9p1/nchan.c	Tue Apr  3 07:02:48 2001
+++ openssh-2.9p1J/nchan.c	Thu May 17 22:21:05 2001
@@ -56,7 +56,7 @@
 
 /* helper */
 static void	chan_shutdown_write(Channel *c);
-static void	chan_shutdown_read(Channel *c);
+void		chan_shutdown_read(Channel *c);
 
 /*
  * SSH1 specific implementation of event functions
@@ -479,7 +479,7 @@
 		c->wfd = -1;
 	}
 }
-static void
+void
 chan_shutdown_read(Channel *c)
 {
 	if (compat20 && c->type == SSH_CHANNEL_LARVAL)
diff -ur openssh-2.9p1/nchan.h openssh-2.9p1J/nchan.h
--- openssh-2.9p1/nchan.h	Sun Mar  4 23:16:12 2001
+++ openssh-2.9p1J/nchan.h	Thu May 17 22:21:05 2001
@@ -88,4 +88,5 @@
 
 void    chan_init_iostates(Channel * c);
 void	chan_init(void);
+void	chan_shutdown_read(Channel *c);
 #endif
diff -ur openssh-2.9p1/readconf.c openssh-2.9p1J/readconf.c
--- openssh-2.9p1/readconf.c	Tue Apr 17 12:11:37 2001
+++ openssh-2.9p1J/readconf.c	Thu May 17 22:21:05 2001
@@ -111,7 +111,7 @@
 	oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
 	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
 	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
-	oHostKeyAlgorithms
+	oHostKeyAlgorithms, oSleep
 } OpCodes;
 
 /* Textual representations of the tokens. */
@@ -177,6 +177,7 @@
 	{ "dynamicforward", oDynamicForward },
 	{ "preferredauthentications", oPreferredAuthentications },
 	{ "hostkeyalgorithms", oHostKeyAlgorithms },
+	{ "sleep", oSleep },
 	{ NULL, 0 }
 };
 
@@ -494,6 +495,10 @@
 		intptr = &options->connection_attempts;
 		goto parse_int;
 
+	case oSleep:
+		intptr = &options->sleep;
+		goto parse_int;
+
 	case oCipher:
 		intptr = &options->cipher;
 		arg = strdelim(&s);
@@ -761,6 +766,7 @@
 	options->num_remote_forwards = 0;
 	options->log_level = (LogLevel) - 1;
 	options->preferred_authentications = NULL;
+	options->sleep = -1;
 }
 
 /*
diff -ur openssh-2.9p1/readconf.h openssh-2.9p1J/readconf.h
--- openssh-2.9p1/readconf.h	Tue Apr 17 12:11:37 2001
+++ openssh-2.9p1J/readconf.h	Thu May 17 22:21:05 2001
@@ -97,6 +97,7 @@
 	/* Remote TCP/IP forward requests. */
 	int     num_remote_forwards;
 	Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION];
+        int     sleep;		/* Exit delay in seconds */
 }       Options;
 
 
diff -ur openssh-2.9p1/session.c openssh-2.9p1J/session.c
--- openssh-2.9p1/session.c	Wed Apr 18 09:29:34 2001
+++ openssh-2.9p1J/session.c	Thu May 17 22:21:05 2001
@@ -1960,6 +1960,9 @@
 	 */
 	if (c->ostate != CHAN_OUTPUT_CLOSED)
 		chan_write_failed(c);
+	if (c->istate == CHAN_INPUT_OPEN && compat20) {
+		chan_shutdown_read(c);
+	}
 	s->chanid = -1;
 }
 
diff -ur openssh-2.9p1/ssh.c openssh-2.9p1J/ssh.c
--- openssh-2.9p1/ssh.c	Tue Apr 17 12:14:35 2001
+++ openssh-2.9p1J/ssh.c	Thu May 17 22:21:05 2001
@@ -182,6 +182,7 @@
 	fprintf(stderr, "  -R listen-port:host:port   Forward remote port to local address\n");
 	fprintf(stderr, "              These cause %s to listen for connections on a port, and\n", __progname);
 	fprintf(stderr, "              forward them to the other side by connecting to host:port.\n");
+	fprintf(stderr, "  -S delay    Set exit delay (in seconds; 0 means wait forever).\n");
 	fprintf(stderr, "  -C          Enable compression.\n");
 	fprintf(stderr, "  -N          Do not execute a shell or command.\n");
 	fprintf(stderr, "  -g          Allow remote hosts to connect to forwarded ports.\n");
@@ -318,7 +319,7 @@
 		opt = av[optind][1];
 		if (!opt)
 			usage();
-		if (strchr("eilcmpLRDo", opt)) {   /* options with arguments */
+		if (strchr("eilcmpLRSDo", opt)) {  /* options with arguments */
 			optarg = av[optind] + 2;
 			if (strcmp(optarg, "") == 0) {
 				if (optind >= ac - 1)
@@ -488,7 +489,13 @@
 			}
 			add_local_forward(&options, fwd_port, buf, fwd_host_port);
 			break;
-
+		case 'S':
+			options.sleep = atoi(optarg);
+			if (options.sleep < 0) {
+				fprintf(stderr, "Bad delay value '%s'\n", optarg);
+				exit(1);
+			}
+			break;
 		case 'D':
 			fwd_port = a2port(optarg);
 			if (fwd_port == 0) {
@@ -526,6 +533,8 @@
 	if (!host)
 		usage();
 
+	if(no_tty_flag && options.sleep < 0) options.sleep=0;
+	  
 	SSLeay_add_all_algorithms();
 	ERR_load_crypto_strings();
 



More information about the openssh-unix-dev mailing list