Telent protocol Nasal command
This commit is contained in:
parent
bd66bfb696
commit
a05cdce793
6 changed files with 130 additions and 35 deletions
|
@ -386,6 +386,26 @@ void fgResetIdleState()
|
||||||
fgRegisterIdleHandler( &fgIdleFunction );
|
fgRegisterIdleHandler( &fgIdleFunction );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fgInitSecureMode()
|
||||||
|
{
|
||||||
|
bool secureMode = true;
|
||||||
|
if (Options::sharedInstance()->isOptionSet("allow-nasal-from-sockets")) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_ALERT, "\n!! Network connections allowed to use Nasal !!\n"
|
||||||
|
"Network connections will be allowed full access to the simulator \n"
|
||||||
|
"including running arbitrary scripts. Ensure you have adequate security\n"
|
||||||
|
"(such as a firewall which blocks external connections).\n");
|
||||||
|
secureMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's by design that we overwrite any existing property tree value
|
||||||
|
// here - this prevents an aircraft or add-on setting the property
|
||||||
|
// value underneath us, eg in their -set.xml
|
||||||
|
SGPropertyNode_ptr secureFlag = fgGetNode("/sim/secure-flag", true);
|
||||||
|
secureFlag->setBoolValue(secureMode);
|
||||||
|
secureFlag->setAttributes(SGPropertyNode::READ |
|
||||||
|
SGPropertyNode::PRESERVE |
|
||||||
|
SGPropertyNode::PROTECTED);
|
||||||
|
}
|
||||||
|
|
||||||
static void upper_case_property(const char *name)
|
static void upper_case_property(const char *name)
|
||||||
{
|
{
|
||||||
|
@ -470,8 +490,7 @@ int fgMainInit( int argc, char **argv )
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string version(FLIGHTGEAR_VERSION);
|
std::string version(FLIGHTGEAR_VERSION);
|
||||||
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version "
|
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version " << version );
|
||||||
<< version );
|
|
||||||
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Build Type " << FG_BUILD_TYPE );
|
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Build Type " << FG_BUILD_TYPE );
|
||||||
SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR);
|
SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR);
|
||||||
SG_LOG( SG_GENERAL, SG_INFO, "Jenkins number/ID " << HUDSON_BUILD_NUMBER << ":"
|
SG_LOG( SG_GENERAL, SG_INFO, "Jenkins number/ID " << HUDSON_BUILD_NUMBER << ":"
|
||||||
|
@ -529,9 +548,11 @@ int fgMainInit( int argc, char **argv )
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (showLauncher) {
|
if (showLauncher) {
|
||||||
SG_LOG(SG_GENERAL, SG_ALERT, "\n!Launcher requested, but FlightGear was compiled without Qt support!");
|
SG_LOG(SG_GENERAL, SG_ALERT, "\n!Launcher requested, but FlightGear was compiled without Qt support!\n");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
fgInitSecureMode();
|
||||||
fgInitAircraftPaths(false);
|
fgInitAircraftPaths(false);
|
||||||
|
|
||||||
configResult = fgInitAircraft(false);
|
configResult = fgInitAircraft(false);
|
||||||
|
|
|
@ -1621,6 +1621,7 @@ struct OptionDesc {
|
||||||
{"language", true, OPTION_IGNORE, "", false, "", 0 },
|
{"language", true, OPTION_IGNORE, "", false, "", 0 },
|
||||||
{"console", false, OPTION_FUNC, "", false, "", fgOptConsole },
|
{"console", false, OPTION_FUNC, "", false, "", fgOptConsole },
|
||||||
{"launcher", false, OPTION_IGNORE, "", false, "", 0 },
|
{"launcher", false, OPTION_IGNORE, "", false, "", 0 },
|
||||||
|
{"allow-nasal-from-sockets", false, OPTION_IGNORE, "", false, "", 0 },
|
||||||
{"disable-rembrandt", false, OPTION_BOOL, "/sim/rendering/rembrandt/enabled", false, "", 0 },
|
{"disable-rembrandt", false, OPTION_BOOL, "/sim/rendering/rembrandt/enabled", false, "", 0 },
|
||||||
{"enable-rembrandt", false, OPTION_BOOL, "/sim/rendering/rembrandt/enabled", true, "", 0 },
|
{"enable-rembrandt", false, OPTION_BOOL, "/sim/rendering/rembrandt/enabled", true, "", 0 },
|
||||||
{"renderer", true, OPTION_STRING, "/sim/rendering/rembrandt/renderer", false, "", 0 },
|
{"renderer", true, OPTION_STRING, "/sim/rendering/rembrandt/renderer", false, "", 0 },
|
||||||
|
|
|
@ -40,9 +40,11 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include <Main/globals.hxx>
|
#include <Main/globals.hxx>
|
||||||
#include <Viewer/viewmgr.hxx>
|
#include <Viewer/viewmgr.hxx>
|
||||||
|
#include <Scripting/NasalSys.hxx>
|
||||||
|
|
||||||
#include <simgear/io/sg_netChat.hxx>
|
#include <simgear/io/sg_netChat.hxx>
|
||||||
|
|
||||||
|
@ -73,13 +75,13 @@ class FGProps::PropsChannel : public simgear::NetChat, public SGPropertyChangeLi
|
||||||
/**
|
/**
|
||||||
* Current property node name.
|
* Current property node name.
|
||||||
*/
|
*/
|
||||||
string path;
|
string path= "/";
|
||||||
|
|
||||||
enum Mode {
|
enum Mode {
|
||||||
PROMPT,
|
PROMPT,
|
||||||
DATA
|
DATA
|
||||||
};
|
};
|
||||||
Mode mode;
|
Mode mode = PROMPT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -136,22 +138,23 @@ private:
|
||||||
// callback implementations:
|
// callback implementations:
|
||||||
void subscribe(const ParameterList &p);
|
void subscribe(const ParameterList &p);
|
||||||
void unsubscribe(const ParameterList &p);
|
void unsubscribe(const ParameterList &p);
|
||||||
|
void beginNasal(const ParameterList &p);
|
||||||
|
|
||||||
FGProps* _owner = nullptr;
|
FGProps* _owner = nullptr;
|
||||||
|
bool _colletingNasal = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
FGProps::PropsChannel::PropsChannel(FGProps* owner)
|
FGProps::PropsChannel::PropsChannel(FGProps* owner)
|
||||||
: buffer(8192),
|
: buffer(8192)
|
||||||
path("/"),
|
, _owner(owner)
|
||||||
mode(PROMPT),
|
|
||||||
_owner(owner)
|
|
||||||
{
|
{
|
||||||
setTerminator( "\r\n" );
|
setTerminator( "\r\n" );
|
||||||
callback_map["subscribe"] = &PropsChannel::subscribe;
|
callback_map["subscribe"] = &PropsChannel::subscribe;
|
||||||
callback_map["unsubscribe"] = &PropsChannel::unsubscribe;
|
callback_map["unsubscribe"] = &PropsChannel::unsubscribe;
|
||||||
|
callback_map["nasal"] = &PropsChannel::beginNasal;
|
||||||
}
|
}
|
||||||
|
|
||||||
FGProps::PropsChannel::~PropsChannel()
|
FGProps::PropsChannel::~PropsChannel()
|
||||||
|
@ -204,6 +207,18 @@ void FGProps::PropsChannel::unsubscribe(const ParameterList ¶m) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FGProps::PropsChannel::beginNasal(const ParameterList ¶m)
|
||||||
|
{
|
||||||
|
std::string eofMarker = "##EOF##";
|
||||||
|
if (param.size() > 1) {
|
||||||
|
if ((param.at(1) == "eof") && (param.size() >= 3)) {
|
||||||
|
eofMarker = param.at(2);
|
||||||
|
}
|
||||||
|
} // of optional argument parsing
|
||||||
|
|
||||||
|
_colletingNasal = true;
|
||||||
|
setTerminator(eofMarker);
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: provide support for different types of subscriptions MODES ? (child added/removed, thesholds, min/max)
|
//TODO: provide support for different types of subscriptions MODES ? (child added/removed, thesholds, min/max)
|
||||||
void FGProps::PropsChannel::valueChanged(SGPropertyNode* ptr)
|
void FGProps::PropsChannel::valueChanged(SGPropertyNode* ptr)
|
||||||
|
@ -276,8 +291,35 @@ getValueTypeString( const SGPropertyNode *node )
|
||||||
void
|
void
|
||||||
FGProps::PropsChannel::foundTerminator()
|
FGProps::PropsChannel::foundTerminator()
|
||||||
{
|
{
|
||||||
|
if (_colletingNasal) {
|
||||||
|
std::string nasalSource = buffer.getData(); // make a copy
|
||||||
|
_colletingNasal = false;
|
||||||
|
setTerminator("\r\n");
|
||||||
|
buffer.remove(); // safe since we copied the source above
|
||||||
|
|
||||||
|
if (globals->get_props()->getBoolValue("sim/secure-flag", true) == true) {
|
||||||
|
SG_LOG(SG_IO, SG_ALERT, "Telnet connection trying to run Nasal, blocked it.\n"
|
||||||
|
"Run the simulator with --allow-nasal-from-sockets to allow this.");
|
||||||
|
error("Simulator running in secure mode, Nasal execution blocked.");
|
||||||
|
} else {
|
||||||
|
auto nasal = globals->get_subsystem<FGNasalSys>();
|
||||||
|
if (nasal) {
|
||||||
|
std::string errors, output;
|
||||||
|
bool ok = nasal->parseAndRunWithOutput(nasalSource, output, errors);
|
||||||
|
if (!ok) {
|
||||||
|
error("Nasal error" + errors);
|
||||||
|
} else if (!output.empty()) {
|
||||||
|
// success and we have output: push it
|
||||||
|
push(output.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const char* cmd = buffer.getData();
|
const char* cmd = buffer.getData();
|
||||||
SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
|
SG_LOG( SG_IO, SG_DEBUG, "processing command = \"" << cmd << "\"" );
|
||||||
|
|
||||||
ParameterList tokens = simgear::strutils::split( cmd );
|
ParameterList tokens = simgear::strutils::split( cmd );
|
||||||
|
|
||||||
|
@ -506,12 +548,12 @@ FGProps::PropsChannel::foundTerminator()
|
||||||
} else if ( command == "prompt" ) {
|
} else if ( command == "prompt" ) {
|
||||||
mode = PROMPT;
|
mode = PROMPT;
|
||||||
} else if (callback_map.find(command) != callback_map.end() ) {
|
} else if (callback_map.find(command) != callback_map.end() ) {
|
||||||
TelnetCallback t = callback_map[ command ];
|
TelnetCallback t = callback_map[ command ];
|
||||||
if (t)
|
if (t)
|
||||||
(this->*t) (tokens);
|
(this->*t) (tokens);
|
||||||
else
|
else
|
||||||
error("No matching callback found for command:"+command);
|
error("No matching callback found for command:"+command);
|
||||||
}
|
}
|
||||||
else if ( command == "seti" ) {
|
else if ( command == "seti" ) {
|
||||||
string value, tmp;
|
string value, tmp;
|
||||||
if (tokens.size() == 3) {
|
if (tokens.size() == 3) {
|
||||||
|
@ -604,7 +646,9 @@ setf <var> <val> alias for setd\r\n\
|
||||||
seti <var> <val> set Int <var> to a new <val>\r\n\
|
seti <var> <val> set Int <var> to a new <val>\r\n\
|
||||||
del <var> <nod> delete <nod> in <var>\r\n\
|
del <var> <nod> delete <nod> in <var>\r\n\
|
||||||
subscribe <var> subscribe to property changes \r\n\
|
subscribe <var> subscribe to property changes \r\n\
|
||||||
unscubscribe <var> unscubscribe from property changes (var must be the property name/path used by subscribe)\r\n";
|
unsubscribe <var> unscubscribe from property changes (var must be the property name/path used by subscribe)\r\n\
|
||||||
|
nasal [EOF <marker>] execute arbitrary Nasal code (simulator must be running with Nasal allowed from sockets)\r\n\
|
||||||
|
";
|
||||||
push( msg );
|
push( msg );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -615,7 +659,7 @@ unscubscribe <var> unscubscribe from property changes (var must be the property
|
||||||
push( getTerminator() );
|
push( getTerminator() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mode == PROMPT ) {
|
if ( (mode == PROMPT) && !_colletingNasal) {
|
||||||
string prompt = node->getPath();
|
string prompt = node->getPath();
|
||||||
if (prompt.empty()) {
|
if (prompt.empty()) {
|
||||||
prompt = "/";
|
prompt = "/";
|
||||||
|
|
|
@ -49,7 +49,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* Server port to listen on.
|
* Server port to listen on.
|
||||||
*/
|
*/
|
||||||
int port;
|
int port = 5501;
|
||||||
simgear::NetChannelPoller poller;
|
simgear::NetChannelPoller poller;
|
||||||
|
|
||||||
std::vector<PropsChannel*> _activeChannels;
|
std::vector<PropsChannel*> _activeChannels;
|
||||||
|
@ -69,22 +69,22 @@ public:
|
||||||
/**
|
/**
|
||||||
* Start the telnet server.
|
* Start the telnet server.
|
||||||
*/
|
*/
|
||||||
bool open();
|
bool open() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process network activity.
|
* Process network activity.
|
||||||
*/
|
*/
|
||||||
bool process();
|
bool process() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool close();
|
bool close() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accept a new client connection.
|
* Accept a new client connection.
|
||||||
*/
|
*/
|
||||||
void handleAccept();
|
void handleAccept() override;
|
||||||
|
|
||||||
void removeChannel(PropsChannel* channel);
|
void removeChannel(PropsChannel* channel);
|
||||||
};
|
};
|
||||||
|
|
|
@ -298,20 +298,37 @@ FGNasalSys::~FGNasalSys()
|
||||||
nasalSys = 0;
|
nasalSys = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FGNasalSys::parseAndRun(const char* sourceCode)
|
bool FGNasalSys::parseAndRunWithOutput(const std::string& source, std::string& output, std::string& errors)
|
||||||
{
|
{
|
||||||
naContext ctx = naNewContext();
|
naContext ctx = naNewContext();
|
||||||
naRef code = parse(ctx, "FGNasalSys::parseAndRun()", sourceCode,
|
naRef code = parse(ctx, "FGNasalSys::parseAndRun()", source.c_str(),
|
||||||
strlen(sourceCode));
|
source.size(), errors);
|
||||||
if(naIsNil(code)) {
|
if(naIsNil(code)) {
|
||||||
naFreeContext(ctx);
|
naFreeContext(ctx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
callWithContext(ctx, code, 0, 0, naNil());
|
naRef result = callWithContext(ctx, code, 0, 0, naNil());
|
||||||
|
|
||||||
|
// if there was a result value, try to convert it to a string
|
||||||
|
// value.
|
||||||
|
if (!naIsNil(result)) {
|
||||||
|
naRef s = naStringValue(ctx, result);
|
||||||
|
if (!naIsNil(s)) {
|
||||||
|
output = naStr_data(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
naFreeContext(ctx);
|
naFreeContext(ctx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FGNasalSys::parseAndRun(const std::string& source)
|
||||||
|
{
|
||||||
|
std::string errors;
|
||||||
|
std::string output;
|
||||||
|
return parseAndRunWithOutput(source, output, errors);
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
FGNasalScript* FGNasalSys::parseScript(const char* src, const char* name)
|
FGNasalScript* FGNasalSys::parseScript(const char* src, const char* name)
|
||||||
{
|
{
|
||||||
|
@ -1208,7 +1225,8 @@ bool FGNasalSys::createModule(const char* moduleName, const char* fileName,
|
||||||
int argc, naRef* args)
|
int argc, naRef* args)
|
||||||
{
|
{
|
||||||
naContext ctx = naNewContext();
|
naContext ctx = naNewContext();
|
||||||
naRef code = parse(ctx, fileName, src, len);
|
std::string errors;
|
||||||
|
naRef code = parse(ctx, fileName, src, len, errors);
|
||||||
if(naIsNil(code)) {
|
if(naIsNil(code)) {
|
||||||
naFreeContext(ctx);
|
naFreeContext(ctx);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1257,16 +1275,21 @@ naRef FGNasalSys::getModule(const char* moduleName)
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
naRef FGNasalSys::parse(naContext ctx, const char* filename, const char* buf, int len)
|
naRef FGNasalSys::parse(naContext ctx, const char* filename,
|
||||||
|
const char* buf, int len,
|
||||||
|
std::string& errors)
|
||||||
{
|
{
|
||||||
int errLine = -1;
|
int errLine = -1;
|
||||||
naRef srcfile = naNewString(ctx);
|
naRef srcfile = naNewString(ctx);
|
||||||
naStr_fromdata(srcfile, (char*)filename, strlen(filename));
|
naStr_fromdata(srcfile, (char*)filename, strlen(filename));
|
||||||
naRef code = naParseCode(ctx, srcfile, 1, (char*)buf, len, &errLine);
|
naRef code = naParseCode(ctx, srcfile, 1, (char*)buf, len, &errLine);
|
||||||
if(naIsNil(code)) {
|
if(naIsNil(code)) {
|
||||||
SG_LOG(SG_NASAL, SG_ALERT,
|
std::ostringstream errorMessageStream;
|
||||||
"Nasal parse error: " << naGetError(ctx) <<
|
errorMessageStream << "Nasal parse error: " << naGetError(ctx) <<
|
||||||
" in "<< filename <<", line " << errLine);
|
" in "<< filename <<", line " << errLine;
|
||||||
|
errors = errorMessageStream.str();
|
||||||
|
SG_LOG(SG_NASAL, SG_ALERT, errors);
|
||||||
|
|
||||||
return naNil();
|
return naNil();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1281,7 +1304,8 @@ bool FGNasalSys::handleCommand( const char* moduleName,
|
||||||
SGPropertyNode* root)
|
SGPropertyNode* root)
|
||||||
{
|
{
|
||||||
naContext ctx = naNewContext();
|
naContext ctx = naNewContext();
|
||||||
naRef code = parse(ctx, fileName, src, strlen(src));
|
std::string errorMessage;
|
||||||
|
naRef code = parse(ctx, fileName, src, strlen(src), errorMessage);
|
||||||
if(naIsNil(code)) {
|
if(naIsNil(code)) {
|
||||||
naFreeContext(ctx);
|
naFreeContext(ctx);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -48,8 +48,12 @@ public:
|
||||||
// indicate successful execution. Does *not* return any Nasal
|
// indicate successful execution. Does *not* return any Nasal
|
||||||
// values, because handling garbage-collected objects from C space
|
// values, because handling garbage-collected objects from C space
|
||||||
// is deep voodoo and violates the "simple hook" idea.
|
// is deep voodoo and violates the "simple hook" idea.
|
||||||
bool parseAndRun(const char* sourceCode);
|
bool parseAndRun(const std::string& source);
|
||||||
|
|
||||||
|
bool parseAndRunWithOutput(const std::string& source,
|
||||||
|
std::string& output,
|
||||||
|
std::string& errors);
|
||||||
|
|
||||||
// Slightly more complicated hook to get a handle to a precompiled
|
// Slightly more complicated hook to get a handle to a precompiled
|
||||||
// Nasal script that can be invoked via a call() method. The
|
// Nasal script that can be invoked via a call() method. The
|
||||||
// caller is expected to delete the FGNasalScript returned from
|
// caller is expected to delete the FGNasalScript returned from
|
||||||
|
@ -191,7 +195,8 @@ private:
|
||||||
void loadScriptDirectory(simgear::Dir nasalDir);
|
void loadScriptDirectory(simgear::Dir nasalDir);
|
||||||
void addModule(std::string moduleName, simgear::PathList scripts);
|
void addModule(std::string moduleName, simgear::PathList scripts);
|
||||||
static void logError(naContext);
|
static void logError(naContext);
|
||||||
naRef parse(naContext ctx, const char* filename, const char* buf, int len);
|
naRef parse(naContext ctx, const char* filename, const char* buf, int len,
|
||||||
|
std::string& errors);
|
||||||
naRef genPropsModule();
|
naRef genPropsModule();
|
||||||
|
|
||||||
bool _inited;
|
bool _inited;
|
||||||
|
|
Loading…
Add table
Reference in a new issue