PATCH: implement delay (sleep) after last tunnelled connection exits

John Bowman bowman at math.ualberta.ca
Fri May 18 14:03:43 EST 2001


Here is a patch to implement a handy new feature proposed by
John Hardin <johnh at aproposretail.com>. This is his description of the feature:

New option for OpenSSH: Delay before exit.

Command line option: -S delay

Config file option:  sleep {delay}

Purpose: Wait the specified number of seconds after last traffic before
dropping the connection and exiting. If ports are forwarded, this causes
the ssh client to allow another forwarded connection to begin after the
current one closes. This permits multiple sequential port-forwarded
connections without using a long-running remote sleep command.

For example, for fetchmail polling of several accounts on a remote POP
server over an SSH tunnel, you might say:

    ssh -L 11000:popserver:110 -S 30 host exit
or
    ssh -N -L 11000:popserver:110 -S 30 host

The ssh client would exit after thirty seconds of inactivity, rather
than exiting immediately when the first forwarded connection closes.
This would allow multiple sequential POP sessions to be carried over the
same tunnel without specifying a long-running remote sleep command.

Why not just use a long-running remote sleep command? In the above
example, you may wish the ssh session to terminate promptly if no new
mail is spooled: maybe this is running over a demand-dialled ISP
connection.

The -S option also removes the need to run a sleep command on the remote
host in the first place. A delay value of zero means wait forever.

-- John Bowman

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


diff -ur openssh-2.9p1/clientloop.c openssh-2.9p1S/clientloop.c
--- openssh-2.9p1/clientloop.c	Fri Apr 20 06:50:51 2001
+++ openssh-2.9p1S/clientloop.c	Thu May 17 21:41:44 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,15 @@
 		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
@@ -751,7 +773,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 +798,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 +863,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.9p1/readconf.c openssh-2.9p1S/readconf.c
--- openssh-2.9p1/readconf.c	Tue Apr 17 12:11:37 2001
+++ openssh-2.9p1S/readconf.c	Thu May 17 19:23:52 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.9p1S/readconf.h
--- openssh-2.9p1/readconf.h	Tue Apr 17 12:11:37 2001
+++ openssh-2.9p1S/readconf.h	Thu May 17 19:23:25 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/ssh.c openssh-2.9p1S/ssh.c
--- openssh-2.9p1/ssh.c	Tue Apr 17 12:14:35 2001
+++ openssh-2.9p1S/ssh.c	Thu May 17 21:13:54 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