/*
*
* clientloop.c
*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
*
*
* Created: Sat Sep 23 12:23:57 1995 ylo
*
* The main loop for the interactive session (client side).
*
*/
#include "includes.h"
RCSID("$Id: clientloop.c,v 1.7 1999/12/07 04:38:32 damien Exp $");
#include "xmalloc.h"
#include "ssh.h"
#include "packet.h"
#include "buffer.h"
#include "authfd.h"
#include "readconf.h"
/* Flag indicating that stdin should be redirected from /dev/null. */
extern int stdin_null_flag;
/*
* Name of the host we are connecting to. This is the name given on the
* command line, or the HostName specified for the user-supplied name in a
* configuration file.
*/
extern char *host;
/*
* Flag to indicate that we have received a window change signal which has
* not yet been processed. This will cause a message indicating the new
* window size to be sent to the server a little later. This is volatile
* because this is updated in a signal handler.
*/
static volatile int received_window_change_signal = 0;
/* Terminal modes, as saved by enter_raw_mode. */
static struct termios saved_tio;
/*
* Flag indicating whether we are in raw mode. This is used by
* enter_raw_mode and leave_raw_mode.
*/
static int in_raw_mode = 0;
/* Flag indicating whether the user\'s terminal is in non-blocking mode. */
static int in_non_blocking_mode = 0;
/* Common data for the client loop code. */
static int escape_pending; /* Last character was the escape character */
static int last_was_cr; /* Last character was a newline. */
static int exit_status; /* Used to store the exit status of the command. */
static int stdin_eof; /* EOF has been encountered on standard error. */
static Buffer stdin_buffer; /* Buffer for stdin data. */
static Buffer stdout_buffer; /* Buffer for stdout data. */
static Buffer stderr_buffer; /* Buffer for stderr data. */
static unsigned int buffer_high;/* Soft max buffer size. */
static int max_fd; /* Maximum file descriptor number in select(). */
static int connection_in; /* Connection to server (input). */
static int connection_out; /* Connection to server (output). */
static unsigned long stdin_bytes, stdout_bytes, stderr_bytes;
static int quit_pending; /* Set to non-zero to quit the client loop. */
static int escape_char; /* Escape character. */
/* Returns the user\'s terminal to normal mode if it had been put in raw mode. */
void
leave_raw_mode()
{
if (!in_raw_mode)
return;
in_raw_mode = 0;
if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0)
perror("tcsetattr");
fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL);
}
/* Puts the user\'s terminal in raw mode. */
void
enter_raw_mode()
{
struct termios tio;
if (tcgetattr(fileno(stdin), &tio) < 0)
perror("tcgetattr");
saved_tio = tio;
tio.c_iflag |= IGNPAR;
tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
#ifdef IEXTEN
tio.c_lflag &= ~IEXTEN;
#endif /* IEXTEN */
tio.c_oflag &= ~OPOST;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0)
perror("tcsetattr");
in_raw_mode = 1;
fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL);
}
/* Restores stdin to blocking mode. */
void
leave_non_blocking()
{
if (in_non_blocking_mode) {
(void) fcntl(fileno(stdin), F_SETFL, 0);
in_non_blocking_mode = 0;
fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL);
}
}
/* Puts stdin terminal in non-blocking mode. */
void
enter_non_blocking()
{
in_non_blocking_mode = 1;
(void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL);
}
/*
* Signal handler for the window change signal (SIGWINCH). This just sets a
* flag indicating that the window has changed.
*/
void
window_change_handler(int sig)
{
received_window_change_signal = 1;
signal(SIGWINCH, window_change_handler);
}
/*
* Signal handler for signals that cause the program to terminate. These
* signals must be trapped to restore terminal modes.
*/
void
signal_handler(int sig)
{
if (in_raw_mode)
leave_raw_mode();
if (in_non_blocking_mode)
leave_non_blocking();
channel_stop_listening();
packet_close();
fatal("Killed by signal %d.", sig);
}
/*
* Returns current time in seconds from Jan 1, 1970 with the maximum
* available resolution.
*/
double
get_current_time()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (double) tv.tv_sec + (double) tv<