Bernie Bright:
Here is the new super improved telnet property interface. CVS changelog is at the end of this message. Once this new telnet code is in and compiles every where we can remove Network/props.[ch]xx. I've added a --telnet=<port> command line option to invoke the new server. Later on we could remove the --props option, or least change it to invoke the new server. I'll let you decide. I've added some new commands to the telnet interface: view next Select the next view. view prev Select the previous view. view set <n> Select view 'n' view get Return index of current view I'm not sure if these same effects could be achieved through property operations. The commands provide a convenient shortcut in any case. I'm also planning on adding a panel command to manipulate panels and objects contained therein (eg simulated mouse clicks). There is going to be some commonality with the command objects so we may need to rationalize this in the near future. Finally, I've also included my python stuff. This is still very much a work in progress, basically I've been using it to test the new telnet server. I have tested it with python 2.2. Feel free to add it to the repository if you want. I would suggest a scripts/python directory CVS Changelog Network/telnet.cxx: New property telnet protocol interface. It supports the same user interface provided by the --props server. Additionally it handles multiple simultaneous connections. Added "view" command to manipulate viewmgr. Network/protocol.hxx: Added protocol configuration exception. Main/fg_io.cxx: Added new "telnet" protocol. Added protocol configuration parse exceptions. Simplified protocol configuration parsing. Main/options.cxx: Added --telnet=<port> command line option and help message.
This commit is contained in:
parent
52c1cb2f7d
commit
f9f05aa870
7 changed files with 772 additions and 170 deletions
|
@ -35,6 +35,7 @@
|
||||||
#include <simgear/io/sg_socket_udp.hxx>
|
#include <simgear/io/sg_socket_udp.hxx>
|
||||||
#include <simgear/math/sg_types.hxx>
|
#include <simgear/math/sg_types.hxx>
|
||||||
#include <simgear/timing/timestamp.hxx>
|
#include <simgear/timing/timestamp.hxx>
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
|
|
||||||
#include <Network/protocol.hxx>
|
#include <Network/protocol.hxx>
|
||||||
#include <Network/atc610x.hxx>
|
#include <Network/atc610x.hxx>
|
||||||
|
@ -51,6 +52,7 @@
|
||||||
#include <Network/opengc.hxx>
|
#include <Network/opengc.hxx>
|
||||||
#include <Network/nmea.hxx>
|
#include <Network/nmea.hxx>
|
||||||
#include <Network/props.hxx>
|
#include <Network/props.hxx>
|
||||||
|
#include <Network/telnet.hxx>
|
||||||
#include <Network/pve.hxx>
|
#include <Network/pve.hxx>
|
||||||
#include <Network/ray.hxx>
|
#include <Network/ray.hxx>
|
||||||
#include <Network/rul.hxx>
|
#include <Network/rul.hxx>
|
||||||
|
@ -68,172 +70,126 @@ io_container global_io_list;
|
||||||
// configure a port based on the config string
|
// configure a port based on the config string
|
||||||
static FGProtocol *parse_port_config( const string& config )
|
static FGProtocol *parse_port_config( const string& config )
|
||||||
{
|
{
|
||||||
bool short_circuit = false;
|
|
||||||
|
|
||||||
string::size_type begin, end;
|
|
||||||
|
|
||||||
begin = 0;
|
|
||||||
|
|
||||||
SG_LOG( SG_IO, SG_INFO, "Parse I/O channel request: " << config );
|
SG_LOG( SG_IO, SG_INFO, "Parse I/O channel request: " << config );
|
||||||
|
|
||||||
// determine protocol
|
vector<string> tokens = simgear::strutils::split( config, "," );
|
||||||
end = config.find(",", begin);
|
if (tokens.empty())
|
||||||
if ( end == string::npos ) {
|
{
|
||||||
return NULL; // dummy
|
SG_LOG( SG_IO, SG_ALERT,
|
||||||
|
"Port configuration error: empty config string" );
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
string protocol = config.substr(begin, end - begin);
|
string protocol = tokens[0];
|
||||||
begin = end + 1;
|
|
||||||
SG_LOG( SG_IO, SG_INFO, " protocol = " << protocol );
|
SG_LOG( SG_IO, SG_INFO, " protocol = " << protocol );
|
||||||
|
|
||||||
FGProtocol *io;
|
FGProtocol *io = 0;
|
||||||
if ( protocol == "atc610x" ) {
|
|
||||||
FGATC610x *atc610x = new FGATC610x;
|
try
|
||||||
io = atc610x;
|
{
|
||||||
short_circuit = true;
|
if ( protocol == "atc610x" ) {
|
||||||
cout << "here ..." << endl;
|
return new FGATC610x;
|
||||||
} else if ( protocol == "atlas" ) {
|
} else if ( protocol == "atlas" ) {
|
||||||
FGAtlas *atlas = new FGAtlas;
|
FGAtlas *atlas = new FGAtlas;
|
||||||
io = atlas;
|
io = atlas;
|
||||||
} else if ( protocol == "opengc" ) {
|
} else if ( protocol == "opengc" ) {
|
||||||
// char wait;
|
// char wait;
|
||||||
// printf("Parsed opengc\n"); cin >> wait;
|
// printf("Parsed opengc\n"); cin >> wait;
|
||||||
FGOpenGC *opengc = new FGOpenGC;
|
FGOpenGC *opengc = new FGOpenGC;
|
||||||
io = opengc;
|
io = opengc;
|
||||||
} else if ( protocol == "garmin" ) {
|
} else if ( protocol == "garmin" ) {
|
||||||
FGGarmin *garmin = new FGGarmin;
|
FGGarmin *garmin = new FGGarmin;
|
||||||
io = garmin;
|
io = garmin;
|
||||||
} else if ( protocol == "httpd" ) {
|
} else if ( protocol == "httpd" ) {
|
||||||
// determine port
|
// determine port
|
||||||
string port = config.substr(begin);
|
string port = tokens[1];
|
||||||
FGHttpd *httpd = new FGHttpd( atoi(port.c_str()) );
|
return new FGHttpd( atoi(port.c_str()) );
|
||||||
io = httpd;
|
|
||||||
short_circuit = true;
|
|
||||||
#ifdef FG_JPEG_SERVER
|
#ifdef FG_JPEG_SERVER
|
||||||
} else if ( protocol == "jpg-httpd" ) {
|
} else if ( protocol == "jpg-httpd" ) {
|
||||||
// determine port
|
// determine port
|
||||||
string port = config.substr(begin);
|
string port = tokens[1];
|
||||||
FGJpegHttpd *jpeg_httpd = new FGJpegHttpd( atoi(port.c_str()) );
|
return new FGJpegHttpd( atoi(port.c_str()) );
|
||||||
io = jpeg_httpd;
|
|
||||||
short_circuit = true;
|
|
||||||
#endif
|
#endif
|
||||||
} else if ( protocol == "joyclient" ) {
|
} else if ( protocol == "joyclient" ) {
|
||||||
FGJoyClient *joyclient = new FGJoyClient;
|
FGJoyClient *joyclient = new FGJoyClient;
|
||||||
io = joyclient;
|
io = joyclient;
|
||||||
} else if ( protocol == "native" ) {
|
} else if ( protocol == "native" ) {
|
||||||
FGNative *native = new FGNative;
|
FGNative *native = new FGNative;
|
||||||
io = native;
|
io = native;
|
||||||
} else if ( protocol == "native_ctrls" ) {
|
} else if ( protocol == "native_ctrls" ) {
|
||||||
FGNativeCtrls *native_ctrls = new FGNativeCtrls;
|
FGNativeCtrls *native_ctrls = new FGNativeCtrls;
|
||||||
io = native_ctrls;
|
io = native_ctrls;
|
||||||
} else if ( protocol == "native_fdm" ) {
|
} else if ( protocol == "native_fdm" ) {
|
||||||
FGNativeFDM *native_fdm = new FGNativeFDM;
|
FGNativeFDM *native_fdm = new FGNativeFDM;
|
||||||
io = native_fdm;
|
io = native_fdm;
|
||||||
} else if ( protocol == "nmea" ) {
|
} else if ( protocol == "nmea" ) {
|
||||||
FGNMEA *nmea = new FGNMEA;
|
FGNMEA *nmea = new FGNMEA;
|
||||||
io = nmea;
|
io = nmea;
|
||||||
} else if ( protocol == "props" ) {
|
} else if ( protocol == "props" ) {
|
||||||
FGProps *props = new FGProps;
|
io = new FGProps();
|
||||||
io = props;
|
} else if ( protocol == "telnet" ) {
|
||||||
} else if ( protocol == "pve" ) {
|
io = new FGTelnet( tokens );
|
||||||
FGPVE *pve = new FGPVE;
|
return io;
|
||||||
io = pve;
|
} else if ( protocol == "pve" ) {
|
||||||
} else if ( protocol == "ray" ) {
|
FGPVE *pve = new FGPVE;
|
||||||
FGRAY *ray = new FGRAY;
|
io = pve;
|
||||||
io = ray;
|
} else if ( protocol == "ray" ) {
|
||||||
} else if ( protocol == "rul" ) {
|
FGRAY *ray = new FGRAY;
|
||||||
FGRUL *rul = new FGRUL;
|
io = ray;
|
||||||
io = rul;
|
} else if ( protocol == "rul" ) {
|
||||||
} else {
|
FGRUL *rul = new FGRUL;
|
||||||
return NULL;
|
io = rul;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FGProtocolConfigError& err)
|
||||||
|
{
|
||||||
|
SG_LOG( SG_IO, SG_ALERT, "Port configuration error: " << err.what() );
|
||||||
|
delete io;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! short_circuit ) {
|
string medium = tokens[1];
|
||||||
// determine medium
|
SG_LOG( SG_IO, SG_INFO, " medium = " << medium );
|
||||||
end = config.find(",", begin);
|
|
||||||
if ( end == string::npos ) {
|
|
||||||
return NULL; // dummy
|
|
||||||
}
|
|
||||||
|
|
||||||
string medium = config.substr(begin, end - begin);
|
|
||||||
begin = end + 1;
|
|
||||||
SG_LOG( SG_IO, SG_INFO, " medium = " << medium );
|
|
||||||
|
|
||||||
// determine direction
|
string direction = tokens[2];
|
||||||
end = config.find(",", begin);
|
io->set_direction( direction );
|
||||||
if ( end == string::npos ) {
|
SG_LOG( SG_IO, SG_INFO, " direction = " << direction );
|
||||||
return NULL; // dummy
|
|
||||||
}
|
|
||||||
|
|
||||||
string direction = config.substr(begin, end - begin);
|
|
||||||
begin = end + 1;
|
|
||||||
io->set_direction( direction );
|
|
||||||
SG_LOG( SG_IO, SG_INFO, " direction = " << direction );
|
|
||||||
|
|
||||||
// determine hertz
|
string hertz_str = tokens[3];
|
||||||
end = config.find(",", begin);
|
double hertz = atof( hertz_str.c_str() );
|
||||||
if ( end == string::npos ) {
|
io->set_hz( hertz );
|
||||||
return NULL; // dummy
|
SG_LOG( SG_IO, SG_INFO, " hertz = " << hertz );
|
||||||
}
|
|
||||||
|
|
||||||
string hertz_str = config.substr(begin, end - begin);
|
|
||||||
begin = end + 1;
|
|
||||||
double hertz = atof( hertz_str.c_str() );
|
|
||||||
io->set_hz( hertz );
|
|
||||||
SG_LOG( SG_IO, SG_INFO, " hertz = " << hertz );
|
|
||||||
|
|
||||||
if ( medium == "serial" ) {
|
if ( medium == "serial" ) {
|
||||||
// device name
|
// device name
|
||||||
end = config.find(",", begin);
|
string device = tokens[4];
|
||||||
if ( end == string::npos ) {
|
SG_LOG( SG_IO, SG_INFO, " device = " << device );
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
string device = config.substr(begin, end - begin);
|
|
||||||
begin = end + 1;
|
|
||||||
SG_LOG( SG_IO, SG_INFO, " device = " << device );
|
|
||||||
|
|
||||||
// baud
|
// baud
|
||||||
string baud = config.substr(begin);
|
string baud = tokens[5];
|
||||||
SG_LOG( SG_IO, SG_INFO, " baud = " << baud );
|
SG_LOG( SG_IO, SG_INFO, " baud = " << baud );
|
||||||
|
|
||||||
SGSerial *ch = new SGSerial( device, baud );
|
SGSerial *ch = new SGSerial( device, baud );
|
||||||
io->set_io_channel( ch );
|
io->set_io_channel( ch );
|
||||||
} else if ( medium == "file" ) {
|
} else if ( medium == "file" ) {
|
||||||
// file name
|
// file name
|
||||||
string file = config.substr(begin);
|
string file = tokens[4];
|
||||||
SG_LOG( SG_IO, SG_INFO, " file name = " << file );
|
SG_LOG( SG_IO, SG_INFO, " file name = " << file );
|
||||||
|
|
||||||
SGFile *ch = new SGFile( file );
|
SGFile *ch = new SGFile( file );
|
||||||
io->set_io_channel( ch );
|
io->set_io_channel( ch );
|
||||||
} else if ( medium == "socket" ) {
|
} else if ( medium == "socket" ) {
|
||||||
// hostname
|
string hostname = tokens[4];
|
||||||
end = config.find(",", begin);
|
string port = tokens[5];
|
||||||
if ( end == string::npos ) {
|
string style = tokens[6];
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
string hostname = config.substr(begin, end - begin);
|
|
||||||
begin = end + 1;
|
|
||||||
SG_LOG( SG_IO, SG_INFO, " hostname = " << hostname );
|
|
||||||
|
|
||||||
// port string
|
SG_LOG( SG_IO, SG_INFO, " hostname = " << hostname );
|
||||||
end = config.find(",", begin);
|
SG_LOG( SG_IO, SG_INFO, " port = " << port );
|
||||||
if ( end == string::npos ) {
|
SG_LOG( SG_IO, SG_INFO, " style = " << style );
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
string port = config.substr(begin, end - begin);
|
|
||||||
begin = end + 1;
|
|
||||||
SG_LOG( SG_IO, SG_INFO, " port string = " << port );
|
|
||||||
|
|
||||||
// socket style
|
|
||||||
string style_str = config.substr(begin);
|
|
||||||
SG_LOG( SG_IO, SG_INFO, " style string = " << style_str );
|
|
||||||
|
|
||||||
SGSocket *ch = new SGSocket( hostname, port, style_str );
|
io->set_io_channel( new SGSocket( hostname, port, style ) );
|
||||||
io->set_io_channel( ch );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return io;
|
return io;
|
||||||
|
@ -273,8 +229,6 @@ void fgIOInit() {
|
||||||
|
|
||||||
// process any serial port work
|
// process any serial port work
|
||||||
void fgIOProcess() {
|
void fgIOProcess() {
|
||||||
FGProtocol *p;
|
|
||||||
|
|
||||||
// cout << "processing I/O channels" << endl;
|
// cout << "processing I/O channels" << endl;
|
||||||
|
|
||||||
static int inited = 0;
|
static int inited = 0;
|
||||||
|
@ -292,9 +246,9 @@ void fgIOProcess() {
|
||||||
last = current;
|
last = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( int i = 0; i < (int)global_io_list.size(); ++i ) {
|
for ( unsigned int i = 0; i < global_io_list.size(); ++i ) {
|
||||||
// cout << " channel = " << i << endl;
|
// cout << " channel = " << i << endl;
|
||||||
p = global_io_list[i];
|
FGProtocol* p = global_io_list[i];
|
||||||
|
|
||||||
if ( p->is_enabled() ) {
|
if ( p->is_enabled() ) {
|
||||||
p->dec_count_down( interval );
|
p->dec_count_down( interval );
|
||||||
|
|
|
@ -1332,7 +1332,8 @@ int mainLoop( int argc, char **argv ) {
|
||||||
version = "unknown version";
|
version = "unknown version";
|
||||||
#endif
|
#endif
|
||||||
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version "
|
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version "
|
||||||
<< version << endl );
|
<< version );
|
||||||
|
SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR );
|
||||||
|
|
||||||
// Allocate global data structures. This needs to happen before
|
// Allocate global data structures. This needs to happen before
|
||||||
// we parse command line options
|
// we parse command line options
|
||||||
|
|
|
@ -24,7 +24,8 @@ libNetwork_a_SOURCES = \
|
||||||
pve.cxx pve.hxx \
|
pve.cxx pve.hxx \
|
||||||
raw_ctrls.hxx \
|
raw_ctrls.hxx \
|
||||||
ray.cxx ray.hxx \
|
ray.cxx ray.hxx \
|
||||||
rul.cxx rul.hxx
|
rul.cxx rul.hxx \
|
||||||
|
telnet.cxx telnet.hxx
|
||||||
|
|
||||||
if OLD_AUTOMAKE
|
if OLD_AUTOMAKE
|
||||||
INCLUDES += -I$(top_srcdir) -I$(top_srcdir)/src
|
INCLUDES += -I$(top_srcdir) -I$(top_srcdir)/src
|
||||||
|
|
|
@ -95,3 +95,14 @@ bool FGProtocol::parse_message() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FGProtocol::set_direction( const string& d ) {
|
||||||
|
if ( d == "in" ) {
|
||||||
|
dir = SG_IO_IN;
|
||||||
|
} else if ( d == "out" ) {
|
||||||
|
dir = SG_IO_OUT;
|
||||||
|
} else if ( d == "bi" ) {
|
||||||
|
dir = SG_IO_BI;
|
||||||
|
} else {
|
||||||
|
dir = SG_IO_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
|
#include <simgear/io/iochannel.hxx>
|
||||||
|
|
||||||
#include STL_STRING
|
#include STL_STRING
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -36,10 +37,6 @@ SG_USING_STD(vector);
|
||||||
|
|
||||||
#define FG_MAX_MSG_SIZE 16384
|
#define FG_MAX_MSG_SIZE 16384
|
||||||
|
|
||||||
// forward declaration
|
|
||||||
class SGIOChannel;
|
|
||||||
|
|
||||||
|
|
||||||
class FGProtocol {
|
class FGProtocol {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -68,17 +65,7 @@ public:
|
||||||
virtual bool close();
|
virtual bool close();
|
||||||
|
|
||||||
inline SGProtocolDir get_direction() const { return dir; }
|
inline SGProtocolDir get_direction() const { return dir; }
|
||||||
inline void set_direction( const string& d ) {
|
void set_direction( const string& d );
|
||||||
if ( d == "in" ) {
|
|
||||||
dir = SG_IO_IN;
|
|
||||||
} else if ( d == "out" ) {
|
|
||||||
dir = SG_IO_OUT;
|
|
||||||
} else if ( d == "bi" ) {
|
|
||||||
dir = SG_IO_BI;
|
|
||||||
} else {
|
|
||||||
dir = SG_IO_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline double get_hz() const { return hz; }
|
inline double get_hz() const { return hz; }
|
||||||
inline void set_hz( double t ) { hz = t; }
|
inline void set_hz( double t ) { hz = t; }
|
||||||
|
@ -108,6 +95,18 @@ typedef vector < FGProtocol * > io_container;
|
||||||
typedef io_container::iterator io_iterator;
|
typedef io_container::iterator io_iterator;
|
||||||
typedef io_container::const_iterator const_io_iterator;
|
typedef io_container::const_iterator const_io_iterator;
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
SG_USING_STD(invalid_argument);
|
||||||
|
|
||||||
|
//namespace flightgear { namespace network {
|
||||||
|
class FGProtocolConfigError : public invalid_argument
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FGProtocolConfigError( const string& what_string )
|
||||||
|
: invalid_argument(what_string) {}
|
||||||
|
};
|
||||||
|
//}} // end namespace flightgear::network
|
||||||
|
|
||||||
|
|
||||||
#endif // _PROTOCOL_HXX
|
#endif // _PROTOCOL_HXX
|
||||||
|
|
||||||
|
|
547
src/Network/telnet.cxx
Normal file
547
src/Network/telnet.cxx
Normal file
|
@ -0,0 +1,547 @@
|
||||||
|
// \file telnet.cx
|
||||||
|
// Property telnet server class.
|
||||||
|
//
|
||||||
|
// Written by Bernie Bright, started May 2002.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2002 Bernie Bright - bbright@bigpond.net.au
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <simgear/compiler.h>
|
||||||
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
|
#include <simgear/misc/props.hxx>
|
||||||
|
#include <simgear/misc/props_io.hxx>
|
||||||
|
|
||||||
|
#include STL_STRSTREAM
|
||||||
|
|
||||||
|
#include <Main/globals.hxx>
|
||||||
|
#include <Main/viewmgr.hxx>
|
||||||
|
|
||||||
|
#include <plib/netChat.h>
|
||||||
|
|
||||||
|
#include "telnet.hxx"
|
||||||
|
|
||||||
|
#if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
|
||||||
|
SG_USING_STD(strstream);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Telnet connection class.
|
||||||
|
* This class represents a connection to a telnet-style client.
|
||||||
|
*/
|
||||||
|
class TelnetChannel : public netChat
|
||||||
|
{
|
||||||
|
netBuffer buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current property node name.
|
||||||
|
*/
|
||||||
|
string path;
|
||||||
|
|
||||||
|
enum Mode {
|
||||||
|
PROMPT,
|
||||||
|
DATA
|
||||||
|
};
|
||||||
|
Mode mode;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
TelnetChannel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append incoming data to our request buffer.
|
||||||
|
*
|
||||||
|
* @param s Character string to append to buffer
|
||||||
|
* @param n Number of characters to append.
|
||||||
|
*/
|
||||||
|
void collectIncomingData( const char* s, int n );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a complete request from the telnet client.
|
||||||
|
*/
|
||||||
|
void foundTerminator();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Return a "Node no found" error message to the client.
|
||||||
|
*/
|
||||||
|
void node_not_found_error( const string& node_name );
|
||||||
|
|
||||||
|
void view_cmd( const vector<string>& );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TelnetChannel::TelnetChannel()
|
||||||
|
: buffer(512),
|
||||||
|
path("/"),
|
||||||
|
mode(PROMPT)
|
||||||
|
{
|
||||||
|
setTerminator( "\r\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TelnetChannel::collectIncomingData( const char* s, int n )
|
||||||
|
{
|
||||||
|
buffer.append( s, n );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TelnetChannel::node_not_found_error( const string& node_name )
|
||||||
|
{
|
||||||
|
string error = "ERR Node \"";
|
||||||
|
error += node_name;
|
||||||
|
error += "\" not found.";
|
||||||
|
push( error.c_str() );
|
||||||
|
push( getTerminator() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// return a human readable form of the value "type"
|
||||||
|
static string
|
||||||
|
getValueTypeString( const SGPropertyNode *node )
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
|
||||||
|
if ( node == NULL )
|
||||||
|
{
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
SGPropertyNode::Type type = node->getType();
|
||||||
|
if ( type == SGPropertyNode::UNSPECIFIED ) {
|
||||||
|
result = "unspecified";
|
||||||
|
} else if ( type == SGPropertyNode::NONE ) {
|
||||||
|
result = "none";
|
||||||
|
} else if ( type == SGPropertyNode::BOOL ) {
|
||||||
|
result = "bool";
|
||||||
|
} else if ( type == SGPropertyNode::INT ) {
|
||||||
|
result = "int";
|
||||||
|
} else if ( type == SGPropertyNode::LONG ) {
|
||||||
|
result = "long";
|
||||||
|
} else if ( type == SGPropertyNode::FLOAT ) {
|
||||||
|
result = "float";
|
||||||
|
} else if ( type == SGPropertyNode::DOUBLE ) {
|
||||||
|
result = "double";
|
||||||
|
} else if ( type == SGPropertyNode::STRING ) {
|
||||||
|
result = "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We have a command.
|
||||||
|
*
|
||||||
|
* TODO: possible future commands:
|
||||||
|
* panel <subcmd>
|
||||||
|
* panel load [path]
|
||||||
|
* panel mouse <button> up|down|click <x> <y>
|
||||||
|
* panel visible 0|1
|
||||||
|
* panel height -> h, Retrieve panel height
|
||||||
|
* panel width -> w, Retrieve panel width
|
||||||
|
* panel xoffset -> x, Retrieve panel x offset
|
||||||
|
* panel yoffset -> y, Retrieve panel y offset
|
||||||
|
*
|
||||||
|
* property <subcmd>
|
||||||
|
* property toggle <prop>
|
||||||
|
* property adjust <prop> <step> <offset> <factor> <min> <max> <wrap>
|
||||||
|
* property multiply <prop> <factor>
|
||||||
|
* property swap <prop1> <prop2>
|
||||||
|
* property scale <prop> <setting> <offset> <factor>
|
||||||
|
*
|
||||||
|
* view <subcmd>
|
||||||
|
* view next
|
||||||
|
* view prev
|
||||||
|
* view set <n>
|
||||||
|
* view current -> n, Retrieve index of current view
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TelnetChannel::foundTerminator()
|
||||||
|
{
|
||||||
|
const char* cmd = buffer.getData();
|
||||||
|
SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
|
||||||
|
|
||||||
|
vector<string> tokens = simgear::strutils::split( cmd );
|
||||||
|
|
||||||
|
SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
|
||||||
|
|
||||||
|
if (!tokens.empty())
|
||||||
|
{
|
||||||
|
string command = tokens[0];
|
||||||
|
|
||||||
|
if (command == "ls")
|
||||||
|
{
|
||||||
|
SGPropertyNode* dir = node;
|
||||||
|
if (tokens.size() == 2)
|
||||||
|
{
|
||||||
|
if (tokens[1][0] == '/')
|
||||||
|
{
|
||||||
|
dir = globals->get_props()->getNode( tokens[1].c_str() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string s = path;
|
||||||
|
s += "/";
|
||||||
|
s += tokens[1];
|
||||||
|
dir = globals->get_props()->getNode( s.c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir == 0)
|
||||||
|
{
|
||||||
|
node_not_found_error( tokens[1] );
|
||||||
|
goto prompt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < dir->nChildren(); i++)
|
||||||
|
{
|
||||||
|
SGPropertyNode * child = dir->getChild(i);
|
||||||
|
string name = child->getName();
|
||||||
|
string line = name;
|
||||||
|
|
||||||
|
if (dir->getChild( name.c_str(), 1 ))
|
||||||
|
{
|
||||||
|
char buf[16];
|
||||||
|
sprintf(buf, "[%d]", child->getIndex());
|
||||||
|
line += buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( child->nChildren() > 0 )
|
||||||
|
{
|
||||||
|
line += "/";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (mode == PROMPT)
|
||||||
|
{
|
||||||
|
string value = dir->getStringValue( name.c_str(), "" );
|
||||||
|
line += " =\t'" + value + "'\t(";
|
||||||
|
line += getValueTypeString(
|
||||||
|
dir->getNode( name.c_str() ) );
|
||||||
|
line += ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line += getTerminator();
|
||||||
|
push( line.c_str() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( command == "dump" )
|
||||||
|
{
|
||||||
|
strstream buf;
|
||||||
|
if ( tokens.size() <= 1 )
|
||||||
|
{
|
||||||
|
writeProperties( buf, node );
|
||||||
|
push( buf.str() );
|
||||||
|
push( getTerminator() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SGPropertyNode *child = node->getNode( tokens[1].c_str() );
|
||||||
|
if ( child )
|
||||||
|
{
|
||||||
|
writeProperties ( buf, child );
|
||||||
|
push( buf.str() );
|
||||||
|
push( getTerminator() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node_not_found_error( tokens[1] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( command == "cd" )
|
||||||
|
{
|
||||||
|
if (tokens.size() == 2)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SGPropertyNode* child = node->getNode( tokens[1].c_str() );
|
||||||
|
if ( child )
|
||||||
|
{
|
||||||
|
node = child;
|
||||||
|
path = node->getPath();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node_not_found_error( tokens[1] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
// Ignore attempt to move past root node with ".."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( command == "pwd" )
|
||||||
|
{
|
||||||
|
string ttt = node->getPath();
|
||||||
|
if (ttt.empty())
|
||||||
|
{
|
||||||
|
ttt = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
push( ttt.c_str() );
|
||||||
|
push( getTerminator() );
|
||||||
|
}
|
||||||
|
else if ( command == "get" || command == "show" )
|
||||||
|
{
|
||||||
|
if ( tokens.size() == 2 )
|
||||||
|
{
|
||||||
|
string tmp;
|
||||||
|
string value = node->getStringValue ( tokens[1].c_str(), "" );
|
||||||
|
if ( mode == PROMPT )
|
||||||
|
{
|
||||||
|
tmp = tokens[1];
|
||||||
|
tmp += " = '";
|
||||||
|
tmp += value;
|
||||||
|
tmp += "' (";
|
||||||
|
tmp += getValueTypeString(
|
||||||
|
node->getNode( tokens[1].c_str() ) );
|
||||||
|
tmp += ")\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tmp = value + "\n";
|
||||||
|
}
|
||||||
|
push( tmp.c_str() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( command == "set" )
|
||||||
|
{
|
||||||
|
if ( tokens.size() == 3 )
|
||||||
|
{
|
||||||
|
node->getNode( tokens[1].c_str(), true )->setStringValue(tokens[2].c_str());
|
||||||
|
|
||||||
|
if ( mode == PROMPT )
|
||||||
|
{
|
||||||
|
// now fetch and write out the new value as confirmation
|
||||||
|
// of the change
|
||||||
|
string value = node->getStringValue ( tokens[1].c_str(), "" );
|
||||||
|
string tmp = tokens[1] + " = '" + value + "' (";
|
||||||
|
tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
|
||||||
|
tmp += ")\n";
|
||||||
|
push( tmp.c_str() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (command == "quit")
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
shouldDelete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( command == "data" )
|
||||||
|
{
|
||||||
|
mode = DATA;
|
||||||
|
}
|
||||||
|
else if ( command == "prompt" )
|
||||||
|
{
|
||||||
|
mode = PROMPT;
|
||||||
|
}
|
||||||
|
else if ( command == "view" )
|
||||||
|
{
|
||||||
|
view_cmd( tokens );
|
||||||
|
}
|
||||||
|
// else if ( command == "panel" )
|
||||||
|
// {
|
||||||
|
// panel_cmd( tokens );
|
||||||
|
// }
|
||||||
|
// else if ( command == "property" )
|
||||||
|
// {
|
||||||
|
// property_cmd( tokens );
|
||||||
|
// }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char* msg = "
|
||||||
|
Valid commands are:
|
||||||
|
|
||||||
|
cd <dir> cd to a directory, '..' to move back
|
||||||
|
data switch to raw data mode
|
||||||
|
dump dump current state (in xml)
|
||||||
|
get <var> show the value of a parameter
|
||||||
|
help show this help message
|
||||||
|
ls [<dir>] list directory
|
||||||
|
prompt switch to interactive mode (default)
|
||||||
|
pwd display your current path
|
||||||
|
quit terminate connection
|
||||||
|
set <var> <val> set <var> to a new <val>
|
||||||
|
show <var> synonym for get
|
||||||
|
view next display next view
|
||||||
|
view prev display prev view
|
||||||
|
view set <n> display view 'n'
|
||||||
|
view get return current view index
|
||||||
|
view current return current view index
|
||||||
|
";
|
||||||
|
push( msg );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt:
|
||||||
|
if (mode == PROMPT)
|
||||||
|
{
|
||||||
|
string prompt = node->getPath();
|
||||||
|
if (prompt.empty())
|
||||||
|
{
|
||||||
|
prompt = "/";
|
||||||
|
}
|
||||||
|
prompt += "> ";
|
||||||
|
push( prompt.c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TelnetChannel::view_cmd( const vector<string>& tokens )
|
||||||
|
{
|
||||||
|
if (tokens.size() <= 1)
|
||||||
|
{
|
||||||
|
// ERROR: no sub-command
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
string subcmd = tokens[1];
|
||||||
|
|
||||||
|
if (subcmd == "next")
|
||||||
|
{
|
||||||
|
globals->get_current_view()->setHeadingOffset_deg(0.0);
|
||||||
|
globals->get_viewmgr()->next_view();
|
||||||
|
}
|
||||||
|
else if (subcmd == "prev")
|
||||||
|
{
|
||||||
|
globals->get_current_view()->setHeadingOffset_deg(0.0);
|
||||||
|
globals->get_viewmgr()->prev_view();
|
||||||
|
}
|
||||||
|
else if (subcmd == "set")
|
||||||
|
{
|
||||||
|
if (tokens.size() == 3)
|
||||||
|
{
|
||||||
|
int i = atoi( tokens[2].c_str() );
|
||||||
|
if (0 >= i && i < globals->get_viewmgr()->size())
|
||||||
|
{
|
||||||
|
globals->get_current_view()->setHeadingOffset_deg(0.0);
|
||||||
|
globals->get_viewmgr()->set_view(i);
|
||||||
|
globals->get_viewmgr()->copyToCurrent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (subcmd == "get" || subcmd == "current")
|
||||||
|
{
|
||||||
|
int i = globals->get_viewmgr()->get_current();
|
||||||
|
char buf[16];
|
||||||
|
snprintf( buf, sizeof(buf), "%d", i );
|
||||||
|
push( buf );
|
||||||
|
push( getTerminator() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ERROR: invalid subcommand.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
FGTelnet::FGTelnet( const vector<string>& tokens )
|
||||||
|
{
|
||||||
|
if (tokens.size() != 2)
|
||||||
|
{
|
||||||
|
throw FGProtocolConfigError( "FGProps: expected 1 argument, <port>" );
|
||||||
|
}
|
||||||
|
|
||||||
|
port = atoi( tokens[1].c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
FGTelnet::~FGTelnet()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
FGTelnet::open()
|
||||||
|
{
|
||||||
|
if ( is_enabled() )
|
||||||
|
{
|
||||||
|
SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
|
||||||
|
<< "is already in use, ignoring" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
netChannel::open();
|
||||||
|
netChannel::bind( "", port );
|
||||||
|
netChannel::listen( 5 );
|
||||||
|
SG_LOG( SG_IO, SG_INFO, "Telnet server started on port " << port );
|
||||||
|
|
||||||
|
set_hz( 5 ); // default to processing requests @ 5Hz
|
||||||
|
set_enabled( true );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
FGTelnet::close()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
FGTelnet::process()
|
||||||
|
{
|
||||||
|
netChannel::poll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
FGTelnet::handleAccept()
|
||||||
|
{
|
||||||
|
netAddress addr;
|
||||||
|
int handle = accept( &addr );
|
||||||
|
SG_LOG( SG_IO, SG_INFO, "Telnet server accepted connection from "
|
||||||
|
<< addr.getHost() << ":" << addr.getPort() );
|
||||||
|
TelnetChannel* channel = new TelnetChannel();
|
||||||
|
channel->setHandle( handle );
|
||||||
|
}
|
89
src/Network/telnet.hxx
Normal file
89
src/Network/telnet.hxx
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// \file telnet.hxx
|
||||||
|
// Property server class.
|
||||||
|
//
|
||||||
|
// Written by Bernie Bright, started May 2002.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2002 Bernie Bright - bbright@bigpond.net.au
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
#ifndef TELNET_HXX_INCLUDED
|
||||||
|
#define TELNET_HXX_INCLUDED 1
|
||||||
|
|
||||||
|
#include <simgear/compiler.h>
|
||||||
|
#include STL_STRING
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
SG_USING_STD(string);
|
||||||
|
SG_USING_STD(vector);
|
||||||
|
|
||||||
|
#include <plib/netChannel.h>
|
||||||
|
|
||||||
|
#include "protocol.hxx"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property server class.
|
||||||
|
* This class provides a telnet-like server for remote access to
|
||||||
|
* FlightGear properties.
|
||||||
|
*/
|
||||||
|
class FGTelnet : public FGProtocol,
|
||||||
|
public netChannel
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server port to listen on.
|
||||||
|
*/
|
||||||
|
int port;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a new TCP server.
|
||||||
|
*
|
||||||
|
* @param tokens Tokenized configuration parameters
|
||||||
|
*/
|
||||||
|
FGTelnet( const vector<string>& tokens );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~FGTelnet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the telnet server.
|
||||||
|
*/
|
||||||
|
bool open();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process network activity.
|
||||||
|
*/
|
||||||
|
bool process();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a new client connection.
|
||||||
|
*/
|
||||||
|
void handleAccept();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //TELNET_HXX_INCLUDED
|
||||||
|
|
Loading…
Reference in a new issue