mosh-server: Support timeouts on lost connectivity to network client.

Closes #690.
This commit is contained in:
John Hood
2015-10-18 16:27:31 -04:00
parent 4b8444988e
commit b742e958b6
10 changed files with 253 additions and 18 deletions
+73 -8
View File
@@ -54,6 +54,7 @@
#include <netdb.h>
#include <time.h>
#include <sys/stat.h>
#include <inttypes.h>
#ifdef HAVE_UTMPX_H
#include <utmpx.h>
@@ -93,7 +94,9 @@ typedef Network::Transport< Terminal::Complete, Network::UserStream > ServerConn
static void serve( int host_fd,
Terminal::Complete &terminal,
ServerConnection &network );
ServerConnection &network,
long network_timeout,
long network_signaled_timeout );
static int run_server( const char *desired_ip, const char *desired_port,
const string &command_path, char *command_argv[],
@@ -340,6 +343,34 @@ int main( int argc, char *argv[] )
static int run_server( const char *desired_ip, const char *desired_port,
const string &command_path, char *command_argv[],
const int colors, bool verbose, bool with_motd ) {
/* get network idle timeout */
long network_timeout = 0;
char *timeout_envar = getenv( "MOSH_SERVER_NETWORK_TMOUT" );
if ( timeout_envar && *timeout_envar ) {
errno = 0;
char *endptr;
network_timeout = strtol( timeout_envar, &endptr, 10 );
if ( *endptr != '\0' || ( network_timeout == 0 && errno == EINVAL ) ) {
fprintf( stderr, "MOSH_SERVER_NETWORK_TMOUT not a valid integer, ignoring\n" );
} else if ( network_timeout < 0 ) {
fprintf( stderr, "MOSH_SERVER_NETWORK_TMOUT is negative, ignoring\n" );
network_timeout = 0;
}
}
/* get network signaled idle timeout */
long network_signaled_timeout = 0;
char *signal_envar = getenv( "MOSH_SERVER_SIGNAL_TMOUT" );
if ( signal_envar && *signal_envar ) {
errno = 0;
char *endptr;
network_signaled_timeout = strtol( signal_envar, &endptr, 10 );
if ( *endptr != '\0' || ( network_signaled_timeout == 0 && errno == EINVAL ) ) {
fprintf( stderr, "MOSH_SERVER_SIGNAL_TMOUT not a valid integer, ignoring\n" );
} else if ( network_signaled_timeout < 0 ) {
fprintf( stderr, "MOSH_SERVER_SIGNAL_TMOUT is negative, ignoring\n" );
network_signaled_timeout = 0;
}
}
/* get initial window size */
struct winsize window_size;
if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ||
@@ -505,7 +536,7 @@ static int run_server( const char *desired_ip, const char *desired_port,
#endif
try {
serve( master, terminal, *network );
serve( master, terminal, *network, network_timeout, network_signaled_timeout );
} catch ( const Network::NetworkException &e ) {
fprintf( stderr, "Network exception: %s\n",
e.what() );
@@ -531,12 +562,16 @@ static int run_server( const char *desired_ip, const char *desired_port,
return 0;
}
static void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network )
static void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network, long network_timeout, long network_signaled_timeout )
{
/* scale timeouts */
const uint64_t network_timeout_ms = static_cast<uint64_t>( network_timeout ) * 1000;
const uint64_t network_signaled_timeout_ms = static_cast<uint64_t>( network_signaled_timeout ) * 1000;
/* prepare to poll for events */
Select &sel = Select::get_instance();
sel.add_signal( SIGTERM );
sel.add_signal( SIGINT );
sel.add_signal( SIGUSR1 );
uint64_t last_remote_num = network.get_remote_state_num();
@@ -549,14 +584,31 @@ static void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &
while ( 1 ) {
try {
static const uint64_t timeout_if_no_client = 60000;
int timeout = INT_MAX;
uint64_t now = Network::timestamp();
const int timeout_if_no_client = 60000;
int timeout = min( network.wait_time(), terminal.wait_time( now ) );
timeout = min( timeout, network.wait_time() );
timeout = min( timeout, terminal.wait_time( now ) );
if ( (!network.get_remote_state_num())
|| network.shutdown_in_progress() ) {
timeout = min( timeout, 5000 );
}
/*
* The server goes completely asleep if it has no remote peer.
* We may want to wake up sooner.
*/
if ( network_timeout_ms ) {
int64_t network_sleep = network_timeout_ms -
( now - network.get_latest_remote_state().timestamp );
if ( network_sleep < 0 ) {
network_sleep = 0;
} else if ( network_sleep > INT_MAX ) {
/* 24 days might be too soon. That's OK. */
network_sleep = INT_MAX;
}
timeout = min( timeout, static_cast<int>(network_sleep) );
}
/* poll for events */
sel.clear_fds();
@@ -679,7 +731,20 @@ static void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &
}
}
if ( sel.any_signal() ) {
bool idle_shutdown = false;
if ( network_timeout_ms &&
network_timeout_ms <= time_since_remote_state ) {
idle_shutdown = true;
fprintf( stderr, "Network idle for %" PRIu64 " seconds.\n", time_since_remote_state / 1000 );
}
if ( sel.signal( SIGUSR1 ) ) {
if ( !network_signaled_timeout_ms || network_signaled_timeout_ms <= time_since_remote_state ) {
idle_shutdown = true;
fprintf( stderr, "Network idle for %"PRIu64" seconds when SIGUSR1 received\n", time_since_remote_state / 1000 );
}
}
if ( sel.any_signal() || idle_shutdown ) {
/* shutdown signal */
if ( network.has_remote_addr() && (!network.shutdown_in_progress()) ) {
network.start_shutdown();
@@ -736,8 +801,8 @@ static void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &
}
if ( !network.get_remote_state_num()
&& time_since_remote_state >= uint64_t( timeout_if_no_client ) ) {
fprintf( stderr, "No connection within %d seconds.\n",
&& time_since_remote_state >= timeout_if_no_client ) {
fprintf( stderr, "No connection within %" PRIu64 " seconds.\n",
timeout_if_no_client / 1000 );
break;
}