sshd hangs on logout -- is this a bug?

John Bowman bowman at math.ualberta.ca
Tue Sep 11 04:14:44 EST 2001


>In the changelog, there is an entry:
>
>20001129
> - (djm) Back out all the serverloop.c hacks. sshd will now hang again
>   if there are background children with open fds.
>
> Does this mean that this is regarded as expected (and correct) behavior, that
> should not change in the future, or does it mean that this behavior is a
> known problem that someone will eventually fix?

Yes, this is a known bug with openssh-2.9p2 under Linux (and some other
operating systems). This behaviour deviates from the programs rsh and ssh
that openssh purportedly replaces.

This problem has been fixed with a patch that was posted to this list
several months ago. Unlike the original serverloop hacks mentioned above,
under Linux the patch does not break ssh or scp or lead to data loss.

Note: A minor update to the patch was made 29Aug01 so that it works
correctly with the -T option. I attach the latest version below.

For more information, see http://www.math.ualberta.ca/imaging/snfs/

-- John Bowman
University of Alberta

diff -ur openssh-2.9p2/channels.c openssh-2.9p2J2/channels.c
--- openssh-2.9p2/channels.c	Wed Jun 13 13:18:05 2001
+++ openssh-2.9p2J2/channels.c	Wed Aug 29 15:04:44 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.9p2/clientloop.c openssh-2.9p2J2/clientloop.c
--- openssh-2.9p2/clientloop.c	Fri Apr 20 06:50:51 2001
+++ openssh-2.9p2J2/clientloop.c	Wed Aug 29 14:58:14 2001
@@ -84,6 +84,7 @@
 
 /* import options */
 extern Options options;
+extern int no_tty_flag;
 
 /* Flag indicating that stdin should be redirected from /dev/null. */
 extern int stdin_null_flag;
@@ -121,8 +122,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 +325,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 +351,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,17 @@
 	 * SSH_MSG_IGNORE packet when the timeout expires.
 	 */
 
-	if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) {
+	if((session_status == SessionWait && options.sleep > 0) ||
+	    (no_tty_flag && options.sleep == -1)) {
+		timer.tv_sec=options.sleep > 0 ? options.sleep : 0;
+		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 +401,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 +463,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 +778,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 +803,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 +868,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);
diff -ur openssh-2.9p2/nchan.c openssh-2.9p2J2/nchan.c
--- openssh-2.9p2/nchan.c	Tue Apr  3 07:02:48 2001
+++ openssh-2.9p2J2/nchan.c	Tue Jun 19 03:27:30 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.9p2/nchan.h openssh-2.9p2J2/nchan.h
--- openssh-2.9p2/nchan.h	Sun Mar  4 23:16:12 2001
+++ openssh-2.9p2J2/nchan.h	Tue Jun 19 03:27:30 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.9p2/readconf.c openssh-2.9p2J2/readconf.c
--- openssh-2.9p2/readconf.c	Tue Apr 17 12:11:37 2001
+++ openssh-2.9p2J2/readconf.c	Tue Jun 19 03:27:30 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.9p2/readconf.h openssh-2.9p2J2/readconf.h
--- openssh-2.9p2/readconf.h	Tue Apr 17 12:11:37 2001
+++ openssh-2.9p2J2/readconf.h	Tue Jun 19 03:27:30 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.9p2/session.c openssh-2.9p2J2/session.c
--- openssh-2.9p2/session.c	Sat Jun 16 21:40:51 2001
+++ openssh-2.9p2J2/session.c	Tue Jun 19 03:27:30 2001
@@ -1928,6 +1928,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.9p2/ssh.c openssh-2.9p2J2/ssh.c
--- openssh-2.9p2/ssh.c	Tue Apr 17 12:14:35 2001
+++ openssh-2.9p2J2/ssh.c	Wed Aug 29 14:56:31 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) {



More information about the openssh-unix-dev mailing list