diff --git a/Makefile b/Makefile index 3a4b829..bc51d91 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ proto = userinput.proto transportinstruction.proto -source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp termemu.cpp parseraction.cpp terminalfunctions.cpp swrite.cpp terminalframebuffer.cpp terminaldispatcher.cpp terminaluserinput.cpp terminaldisplay.cpp network.cpp ntester.cpp ocb.cpp base64.cpp encrypt.cpp decrypt.cpp crypto.cpp networktransport.cpp transportfragment.cpp user.cpp userinput.pb.cc completeterminal.cpp stm-server.cpp stm.cpp transportinstruction.pb.cc transportsender.cpp -objects = parserstate.o parser.o templates.o terminal.o parseraction.o terminalfunctions.o swrite.o terminalframebuffer.o terminaldispatcher.o terminaluserinput.o terminaldisplay.o network.o ocb.o base64.o crypto.o networktransport.o transportfragment.o user.o userinput.pb.o completeterminal.o transportinstruction.pb.o transportsender.o +source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp termemu.cpp parseraction.cpp terminalfunctions.cpp swrite.cpp terminalframebuffer.cpp terminaldispatcher.cpp terminaluserinput.cpp terminaldisplay.cpp network.cpp ntester.cpp ocb.cpp base64.cpp encrypt.cpp decrypt.cpp crypto.cpp networktransport.cpp transportfragment.cpp user.cpp userinput.pb.cc completeterminal.cpp stm-server.cpp stm.cpp transportinstruction.pb.cc transportsender.cpp stmclient.cpp +objects = parserstate.o parser.o templates.o terminal.o parseraction.o terminalfunctions.o swrite.o terminalframebuffer.o terminaldispatcher.o terminaluserinput.o terminaldisplay.o network.o ocb.o base64.o crypto.o networktransport.o transportfragment.o user.o userinput.pb.o completeterminal.o transportinstruction.pb.o transportsender.o stmclient.o repos = templates.rpo executables = parse termemu ntester encrypt decrypt stm-server stm diff --git a/stm.cpp b/stm.cpp index f40628e..758f726 100644 --- a/stm.cpp +++ b/stm.cpp @@ -1,26 +1,4 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "networktransport.hpp" -#include "completeterminal.hpp" -#include "swrite.hpp" -#include "user.hpp" - -void client( const char *ip, int port, const char *key ); - -using namespace std; +#include "stmclient.hpp" int main( int argc, char *argv[] ) { @@ -37,7 +15,7 @@ int main( int argc, char *argv[] ) port = atoi( argv[ 2 ] ); key = argv[ 3 ]; - struct termios saved_termios, raw_termios; + STMClient client( ip, port, key ); /* Adopt implementation locale */ if ( NULL == setlocale( LC_ALL, "" ) ) { @@ -45,223 +23,12 @@ int main( int argc, char *argv[] ) exit( 1 ); } - /* Verify locale calls for UTF-8 */ - if ( strcmp( nl_langinfo( CODESET ), "UTF-8" ) != 0 ) { - fprintf( stderr, "stm requires a UTF-8 locale.\n" ); - exit( 1 ); - } + client.init(); - /* Verify terminal configuration */ - if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) { - perror( "tcgetattr" ); - exit( 1 ); - } - - /* Put terminal driver in raw mode */ - raw_termios = saved_termios; - if ( !(raw_termios.c_iflag & IUTF8) ) { - fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); - raw_termios.c_iflag |= IUTF8; - } - - cfmakeraw( &raw_termios ); - - if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) { - perror( "tcsetattr" ); - exit( 1 ); - } - - /* Put terminal in application-cursor-key mode */ - swrite( STDOUT_FILENO, Terminal::Emulator::open().c_str() ); - - client( ip, port, key ); - - /* Restore terminal and terminal-driver state */ - swrite( STDOUT_FILENO, Terminal::Emulator::close().c_str() ); - - if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) { - perror( "tcsetattr" ); - exit( 1 ); - } + client.main(); + client.shutdown(); printf( "\n[stm is exiting.]\n" ); return 0; } - -void client( const char *ip, int port, const char *key ) -{ - /* establish WINCH fd and start listening for signal */ - sigset_t signal_mask; - assert( sigemptyset( &signal_mask ) == 0 ); - assert( sigaddset( &signal_mask, SIGWINCH ) == 0 ); - - /* stop "ignoring" WINCH signal */ - assert( sigprocmask( SIG_BLOCK, &signal_mask, NULL ) == 0 ); - - int winch_fd = signalfd( -1, &signal_mask, 0 ); - if ( winch_fd < 0 ) { - perror( "signalfd" ); - return; - } - - /* establish fd for shutdown signals */ - assert( sigemptyset( &signal_mask ) == 0 ); - assert( sigaddset( &signal_mask, SIGTERM ) == 0 ); - assert( sigaddset( &signal_mask, SIGINT ) == 0 ); - assert( sigaddset( &signal_mask, SIGHUP ) == 0 ); - assert( sigaddset( &signal_mask, SIGPIPE ) == 0 ); - - /* don't let signals kill us */ - assert( sigprocmask( SIG_BLOCK, &signal_mask, NULL ) == 0 ); - - int shutdown_signal_fd = signalfd( -1, &signal_mask, 0 ); - if ( shutdown_signal_fd < 0 ) { - perror( "signalfd" ); - return; - } - - /* get initial window size */ - struct winsize window_size; - if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { - perror( "ioctl TIOCGWINSZ" ); - return; - } - - /* local state */ - Terminal::Complete terminal( window_size.ws_col, window_size.ws_row ); - - /* initialize screen */ - string init = Terminal::Display::new_frame( false, terminal.get_fb(), terminal.get_fb() ); - swrite( STDOUT_FILENO, init.data(), init.size() ); - - /* open network */ - Network::UserStream blank; - Network::Transport< Network::UserStream, Terminal::Complete > network( blank, terminal, - key, ip, port ); - - /* tell server the size of the terminal */ - network.get_current_state().push_back( Parser::Resize( window_size.ws_col, window_size.ws_row ) ); - - /* prepare to poll for events */ - struct pollfd pollfds[ 4 ]; - - pollfds[ 0 ].fd = network.fd(); - pollfds[ 0 ].events = POLLIN; - - pollfds[ 1 ].fd = STDIN_FILENO; - pollfds[ 1 ].events = POLLIN; - - pollfds[ 2 ].fd = winch_fd; - pollfds[ 2 ].events = POLLIN; - - pollfds[ 3 ].fd = shutdown_signal_fd; - pollfds[ 3 ].events = POLLIN; - - uint64_t last_remote_num = network.get_remote_state_num(); - - while ( 1 ) { - try { - int active_fds = poll( pollfds, 4, network.wait_time() ); - if ( active_fds < 0 ) { - perror( "poll" ); - break; - } - - if ( pollfds[ 0 ].revents & POLLIN ) { - /* packet received from the network */ - network.recv(); - - /* is a new frame available from the terminal? */ - if ( network.get_remote_state_num() != last_remote_num ) { - string diff = network.get_remote_diff(); - swrite( STDOUT_FILENO, diff.data(), diff.size() ); - } - } - - if ( pollfds[ 1 ].revents & POLLIN ) { - /* input from the user needs to be fed to the network */ - const int buf_size = 16384; - char buf[ buf_size ]; - - /* fill buffer if possible */ - ssize_t bytes_read = read( pollfds[ 1 ].fd, buf, buf_size ); - if ( bytes_read == 0 ) { /* EOF */ - return; - } else if ( bytes_read < 0 ) { - perror( "read" ); - return; - } - - if ( !network.shutdown_in_progress() ) { - for ( int i = 0; i < bytes_read; i++ ) { - network.get_current_state().push_back( Parser::UserByte( buf[ i ] ) ); - } - } - } - - if ( pollfds[ 2 ].revents & POLLIN ) { - /* resize */ - struct signalfd_siginfo info; - assert( read( winch_fd, &info, sizeof( info ) ) == sizeof( info ) ); - assert( info.ssi_signo == SIGWINCH ); - - /* get new size */ - if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { - perror( "ioctl TIOCGWINSZ" ); - return; - } - - /* tell remote emulator */ - Parser::Resize res( window_size.ws_col, window_size.ws_row ); - - if ( !network.shutdown_in_progress() ) { - network.get_current_state().push_back( res ); - } - - /* tell local emulator -- there is probably a safer way to do this */ - for ( list< Network::TimestampedState >::iterator i = network.begin(); - i != network.end(); - i++ ) { - i->state.act( &res ); - } - } - - if ( pollfds[ 3 ].revents & POLLIN ) { - /* shutdown signal */ - if ( network.attached() ) { - network.start_shutdown(); - } else { - break; - } - } - - if ( (pollfds[ 0 ].revents) - & (POLLERR | POLLHUP | POLLNVAL) ) { - /* network problem */ - break; - } - - if ( (pollfds[ 1 ].revents) - & (POLLERR | POLLHUP | POLLNVAL) ) { - /* user problem */ - network.start_shutdown(); - } - - /* quit if our shutdown has been acknowledged */ - if ( network.shutdown_in_progress() && network.shutdown_acknowledged() ) { - break; - } - - /* quit if we received and acknowledged a shutdown request */ - if ( network.counterparty_shutdown_ack_sent() ) { - break; - } - - network.tick(); - } catch ( Network::NetworkException e ) { - fprintf( stderr, "%s: %s\r\n", e.function.c_str(), strerror( e.the_errno ) ); - sleep( 1 ); - } - } -} diff --git a/stmclient.cpp b/stmclient.cpp new file mode 100644 index 0000000..41348b6 --- /dev/null +++ b/stmclient.cpp @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stmclient.hpp" +#include "swrite.hpp" +#include "networktransport.hpp" +#include "completeterminal.hpp" +#include "user.hpp" + +void STMClient::init( void ) +{ + /* Verify locale calls for UTF-8 */ + if ( strcmp( nl_langinfo( CODESET ), "UTF-8" ) != 0 ) { + fprintf( stderr, "stm requires a UTF-8 locale.\n" ); + exit( 1 ); + } + + /* Verify terminal configuration */ + if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) { + perror( "tcgetattr" ); + exit( 1 ); + } + + /* Put terminal driver in raw mode */ + raw_termios = saved_termios; + if ( !(raw_termios.c_iflag & IUTF8) ) { + fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); + raw_termios.c_iflag |= IUTF8; + } + + cfmakeraw( &raw_termios ); + + if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) { + perror( "tcsetattr" ); + exit( 1 ); + } + + /* Put terminal in application-cursor-key mode */ + swrite( STDOUT_FILENO, Terminal::Emulator::open().c_str() ); +} + +void STMClient::shutdown( void ) +{ + /* Restore terminal and terminal-driver state */ + swrite( STDOUT_FILENO, Terminal::Emulator::close().c_str() ); + + if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) { + perror( "tcsetattr" ); + exit( 1 ); + } +} + +void STMClient::main( void ) +{ + /* establish WINCH fd and start listening for signal */ + sigset_t signal_mask; + assert( sigemptyset( &signal_mask ) == 0 ); + assert( sigaddset( &signal_mask, SIGWINCH ) == 0 ); + + /* stop "ignoring" WINCH signal */ + assert( sigprocmask( SIG_BLOCK, &signal_mask, NULL ) == 0 ); + + int winch_fd = signalfd( -1, &signal_mask, 0 ); + if ( winch_fd < 0 ) { + perror( "signalfd" ); + return; + } + + /* establish fd for shutdown signals */ + assert( sigemptyset( &signal_mask ) == 0 ); + assert( sigaddset( &signal_mask, SIGTERM ) == 0 ); + assert( sigaddset( &signal_mask, SIGINT ) == 0 ); + assert( sigaddset( &signal_mask, SIGHUP ) == 0 ); + assert( sigaddset( &signal_mask, SIGPIPE ) == 0 ); + + /* don't let signals kill us */ + assert( sigprocmask( SIG_BLOCK, &signal_mask, NULL ) == 0 ); + + int shutdown_signal_fd = signalfd( -1, &signal_mask, 0 ); + if ( shutdown_signal_fd < 0 ) { + perror( "signalfd" ); + return; + } + + /* get initial window size */ + struct winsize window_size; + if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { + perror( "ioctl TIOCGWINSZ" ); + return; + } + + /* local state */ + Terminal::Complete terminal( window_size.ws_col, window_size.ws_row ); + + /* initialize screen */ + string init = Terminal::Display::new_frame( false, terminal.get_fb(), terminal.get_fb() ); + swrite( STDOUT_FILENO, init.data(), init.size() ); + + /* open network */ + Network::UserStream blank; + Network::Transport< Network::UserStream, Terminal::Complete > network( blank, terminal, + key.c_str(), ip.c_str(), port ); + + /* tell server the size of the terminal */ + network.get_current_state().push_back( Parser::Resize( window_size.ws_col, window_size.ws_row ) ); + + /* prepare to poll for events */ + struct pollfd pollfds[ 4 ]; + + pollfds[ 0 ].fd = network.fd(); + pollfds[ 0 ].events = POLLIN; + + pollfds[ 1 ].fd = STDIN_FILENO; + pollfds[ 1 ].events = POLLIN; + + pollfds[ 2 ].fd = winch_fd; + pollfds[ 2 ].events = POLLIN; + + pollfds[ 3 ].fd = shutdown_signal_fd; + pollfds[ 3 ].events = POLLIN; + + uint64_t last_remote_num = network.get_remote_state_num(); + + while ( 1 ) { + try { + int active_fds = poll( pollfds, 4, network.wait_time() ); + if ( active_fds < 0 ) { + perror( "poll" ); + break; + } + + if ( pollfds[ 0 ].revents & POLLIN ) { + /* packet received from the network */ + network.recv(); + + /* is a new frame available from the terminal? */ + if ( network.get_remote_state_num() != last_remote_num ) { + string diff = network.get_remote_diff(); + swrite( STDOUT_FILENO, diff.data(), diff.size() ); + } + } + + if ( pollfds[ 1 ].revents & POLLIN ) { + /* input from the user needs to be fed to the network */ + const int buf_size = 16384; + char buf[ buf_size ]; + + /* fill buffer if possible */ + ssize_t bytes_read = read( pollfds[ 1 ].fd, buf, buf_size ); + if ( bytes_read == 0 ) { /* EOF */ + return; + } else if ( bytes_read < 0 ) { + perror( "read" ); + return; + } + + if ( !network.shutdown_in_progress() ) { + for ( int i = 0; i < bytes_read; i++ ) { + network.get_current_state().push_back( Parser::UserByte( buf[ i ] ) ); + } + } + } + + if ( pollfds[ 2 ].revents & POLLIN ) { + /* resize */ + struct signalfd_siginfo info; + assert( read( winch_fd, &info, sizeof( info ) ) == sizeof( info ) ); + assert( info.ssi_signo == SIGWINCH ); + + /* get new size */ + if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { + perror( "ioctl TIOCGWINSZ" ); + return; + } + + /* tell remote emulator */ + Parser::Resize res( window_size.ws_col, window_size.ws_row ); + + if ( !network.shutdown_in_progress() ) { + network.get_current_state().push_back( res ); + } + + /* tell local emulator -- there is probably a safer way to do this */ + for ( list< Network::TimestampedState >::iterator i = network.begin(); + i != network.end(); + i++ ) { + i->state.act( &res ); + } + } + + if ( pollfds[ 3 ].revents & POLLIN ) { + /* shutdown signal */ + if ( network.attached() ) { + network.start_shutdown(); + } else { + break; + } + } + + if ( (pollfds[ 0 ].revents) + & (POLLERR | POLLHUP | POLLNVAL) ) { + /* network problem */ + break; + } + + if ( (pollfds[ 1 ].revents) + & (POLLERR | POLLHUP | POLLNVAL) ) { + /* user problem */ + network.start_shutdown(); + } + + /* quit if our shutdown has been acknowledged */ + if ( network.shutdown_in_progress() && network.shutdown_acknowledged() ) { + break; + } + + /* quit if we received and acknowledged a shutdown request */ + if ( network.counterparty_shutdown_ack_sent() ) { + break; + } + + network.tick(); + } catch ( Network::NetworkException e ) { + fprintf( stderr, "%s: %s\r\n", e.function.c_str(), strerror( e.the_errno ) ); + sleep( 1 ); + } + } +} diff --git a/stmclient.hpp b/stmclient.hpp new file mode 100644 index 0000000..93a6db9 --- /dev/null +++ b/stmclient.hpp @@ -0,0 +1,26 @@ +#ifndef STM_CLIENT_HPP +#define STM_CLIENT_HPP + +#include +#include + +class STMClient { +private: + std::string ip; + int port; + std::string key; + + struct termios saved_termios, raw_termios; + +public: + STMClient( const char *s_ip, int s_port, const char *s_key ) + : ip( s_ip ), port( s_port ), key( s_key ), + saved_termios(), raw_termios() + {} + + void init( void ); + void shutdown( void ); + void main( void ); +}; + +#endif