diff --git a/Makefile b/Makefile index 7633922..e6f5f06 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ proto = userinput.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 networkinstruction.cpp user.cpp userinput.pb.cc completeterminal.cpp rtm-server.cpp +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 networkinstruction.cpp user.cpp userinput.pb.cc completeterminal.cpp stm-server.cpp stm.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 networkinstruction.o user.o userinput.pb.o completeterminal.o repos = templates.rpo -executables = parse termemu ntester encrypt decrypt rtm-server +executables = parse termemu ntester encrypt decrypt stm-server stm CXX = g++ -CXXFLAGS = -g --std=c++0x -pedantic -Werror -Wall -Wextra -Weffc++ -fno-implicit-templates -fno-default-inline -pipe -D_FILE_OFFSET_BITS=64 -D_XOPEN_SOURCE=500 -D_GNU_SOURCE -D_BSD_SOURCE +CXXFLAGS = -g -O2 --std=c++0x -pedantic -Werror -Wall -Wextra -Weffc++ -fno-implicit-templates -fno-default-inline -pipe -D_FILE_OFFSET_BITS=64 -D_XOPEN_SOURCE=500 -D_GNU_SOURCE -D_BSD_SOURCE LIBS = -lutil -lssl -lrt -lm -lprotobuf-lite PROTOC = protoc @@ -26,8 +26,11 @@ encrypt: encrypt.o $(objects) ntester # serialize link steps because of -frepo decrypt: decrypt.o $(objects) encrypt # serialize link steps because of -frepo $(CXX) $(CXXFLAGS) -o $@ decrypt.o $(objects) $(LIBS) -rtm-server: rtm-server.o $(objects) decrypt # serialize link steps because of -frepo - $(CXX) $(CXXFLAGS) -o $@ rtm-server.o $(objects) $(LIBS) +stm-server: stm-server.o $(objects) decrypt # serialize link steps because of -frepo + $(CXX) $(CXXFLAGS) -o $@ stm-server.o $(objects) $(LIBS) + +stm: stm.o $(objects) stm-server # serialize link steps because of -frepo + $(CXX) $(CXXFLAGS) -o $@ stm.o $(objects) $(LIBS) templates.o: templates.cpp $(CXX) $(CXXFLAGS) -frepo -c -o $@ $< diff --git a/completeterminal.hpp b/completeterminal.hpp index ae5f763..9af689c 100644 --- a/completeterminal.hpp +++ b/completeterminal.hpp @@ -18,9 +18,6 @@ namespace Terminal { std::string act( const std::string &str ); std::string act( const Parser::Action *act ); - std::string open( void ) { return terminal.open(); } - std::string close( void ) { return terminal.close(); } - const Framebuffer & get_fb( void ) const { return terminal.get_fb(); } /* interface for Network::Transport */ diff --git a/rtm-server.cpp b/stm-server.cpp similarity index 95% rename from rtm-server.cpp rename to stm-server.cpp index f5a64f2..5d8ebd8 100644 --- a/rtm-server.cpp +++ b/stm-server.cpp @@ -91,7 +91,7 @@ int main( void ) serve( master ); } - printf( "[stm is exiting.]\n" ); + printf( "[stm-server is exiting.]\n" ); return 0; } @@ -132,8 +132,6 @@ void serve( int host_fd ) uint64_t last_remote_num = network.get_remote_state_num(); while ( 1 ) { - network.set_current_state( terminal ); - int active_fds = poll( pollfds, 2, network.tick() ); if ( active_fds < 0 ) { perror( "poll" ); @@ -155,6 +153,9 @@ void serve( int host_fd ) terminal_to_host += terminal.act( us.get_action( i ) ); } + /* update client with new state of terminal */ + network.set_current_state( terminal ); + /* write any writeback octets back to the host */ if ( swrite( host_fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { break; @@ -176,8 +177,12 @@ void serve( int host_fd ) return; } - /* write any writeback octets back to the host */ string terminal_to_host = terminal.act( string( buf, bytes_read ) ); + + /* update client with new state of terminal */ + network.set_current_state( terminal ); + + /* write any writeback octets back to the host */ if ( swrite( host_fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { break; } diff --git a/stm.cpp b/stm.cpp new file mode 100644 index 0000000..14e73d8 --- /dev/null +++ b/stm.cpp @@ -0,0 +1,186 @@ +#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; + +int main( int argc, char *argv[] ) +{ + /* Get arguments */ + char *ip, *key; + int port; + + if ( argc != 4 ) { + fprintf( stderr, "Usage: %s IP PORT KEY\n", argv[ 0 ] ); + exit( 1 ); + } + + ip = argv[ 1 ]; + port = atoi( argv[ 2 ] ); + key = argv[ 3 ]; + + struct termios saved_termios, raw_termios; + + /* Adopt implementation locale */ + if ( NULL == setlocale( LC_ALL, "" ) ) { + perror( "setlocale" ); + 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 ); + } + + /* 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 ); + } + + printf( "[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; + } + + /* get initial window size */ + struct winsize window_size; + if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { + perror( "ioctl TIOCGWINSZ" ); + return; + } + + /* XXX transmit initial resize */ + + /* local state */ + Terminal::Complete terminal( window_size.ws_col, window_size.ws_row ); + Terminal::Framebuffer state( window_size.ws_col, window_size.ws_row ); + + /* open network */ + Network::UserStream blank; + Network::Transport< Network::UserStream, Terminal::Complete > network( blank, terminal, + key, ip, port ); + + /* prepare to poll for events */ + struct pollfd pollfds[ 3 ]; + + 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; + + uint64_t last_remote_num = network.get_remote_state_num(); + + while ( 1 ) { + int active_fds = poll( pollfds, 3, network.tick() ); + 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; + } + + for ( int i = 0; i < bytes_read; i++ ) { + network.get_current_state().push_back( Parser::UserByte( buf[ i ] ) ); + } + } + + if ( pollfds[ 2 ].revents & POLLIN ) { + /* handle resize */ + } + + if ( (pollfds[ 0 ].revents | pollfds[ 1 ].revents) + & (POLLERR | POLLHUP | POLLNVAL) ) { + break; + } + } +} diff --git a/templates.cpp b/templates.cpp index d3a601a..84f35ee 100644 --- a/templates.cpp +++ b/templates.cpp @@ -31,5 +31,6 @@ template class vector; template class vector; template class Transport; template class Transport; +template class Transport; template class deque; diff --git a/termemu.cpp b/termemu.cpp index c8150a3..b4b2736 100644 --- a/termemu.cpp +++ b/termemu.cpp @@ -208,7 +208,7 @@ void emulate_terminal( int fd ) pollfds[ 2 ].fd = winch_fd; pollfds[ 2 ].events = POLLIN; - swrite( STDOUT_FILENO, complete.open().c_str() ); + swrite( STDOUT_FILENO, Terminal::Emulator::open().c_str() ); int poll_timeout = -1; @@ -295,5 +295,5 @@ void emulate_terminal( int fd ) std::string update = Terminal::Display::new_frame( true, state, complete.get_fb() ); swrite( STDOUT_FILENO, update.c_str() ); - swrite( STDOUT_FILENO, complete.close().c_str() ); + swrite( STDOUT_FILENO, Terminal::Emulator::close().c_str() ); } diff --git a/terminal.cpp b/terminal.cpp index 0ae582a..41dd4ee 100644 --- a/terminal.cpp +++ b/terminal.cpp @@ -155,7 +155,5 @@ void Emulator::resize( size_t s_width, size_t s_height ) bool Emulator::operator==( Emulator const &x ) const { - assert( dispatch == x.dispatch ); - assert( user == x.user ); - return fb == x.fb; + return ( dispatch == x.dispatch ) && ( user == x.user ) && ( fb == x.fb ); } diff --git a/terminal.hpp b/terminal.hpp index 6e04543..4fbacfb 100644 --- a/terminal.hpp +++ b/terminal.hpp @@ -46,8 +46,8 @@ namespace Terminal { std::string read_octets_to_host( void ); - std::string open( void ); /* put user cursor keys in application mode */ - std::string close( void ); /* restore user cursor keys */ + static std::string open( void ); /* put user cursor keys in application mode */ + static std::string close( void ); /* restore user cursor keys */ const Framebuffer & get_fb( void ) const { return fb; }