John Bowman
2001-May-18 04:03 UTC
PATCH: implement delay (sleep) after last tunnelled connection exits
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) {
Reasonably Related Threads
- [PATCH] Add an exit delay to Openssh-3.0.2p1 for use in tunneling
- [PATCH] fix for Linux hang on exit bug in 2.9.9p2
- [PATCH]: Patch to fix hang on exit bug under Linux and add optional exit delay
- sshd hangs on logout -- is this a bug?
- patch: properly zeroing fd_set in clientloop