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 );
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -470,8 +490,7 @@ int fgMainInit( int argc, char **argv )
|
|||
}
|
||||
|
||||
std::string version(FLIGHTGEAR_VERSION);
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version "
|
||||
<< version );
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version " << version );
|
||||
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, "Jenkins number/ID " << HUDSON_BUILD_NUMBER << ":"
|
||||
|
@ -529,9 +548,11 @@ int fgMainInit( int argc, char **argv )
|
|||
}
|
||||
#else
|
||||
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
|
||||
|
||||
fgInitSecureMode();
|
||||
fgInitAircraftPaths(false);
|
||||
|
||||
configResult = fgInitAircraft(false);
|
||||
|
|
|
@ -1621,6 +1621,7 @@ struct OptionDesc {
|
|||
{"language", true, OPTION_IGNORE, "", false, "", 0 },
|
||||
{"console", false, OPTION_FUNC, "", false, "", fgOptConsole },
|
||||
{"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 },
|
||||
{"enable-rembrandt", false, OPTION_BOOL, "/sim/rendering/rembrandt/enabled", true, "", 0 },
|
||||
{"renderer", true, OPTION_STRING, "/sim/rendering/rembrandt/renderer", false, "", 0 },
|
||||
|
|
|
@ -40,9 +40,11 @@
|
|||
#include <iostream>
|
||||
#include <errno.h>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Viewer/viewmgr.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
#include <simgear/io/sg_netChat.hxx>
|
||||
|
||||
|
@ -73,13 +75,13 @@ class FGProps::PropsChannel : public simgear::NetChat, public SGPropertyChangeLi
|
|||
/**
|
||||
* Current property node name.
|
||||
*/
|
||||
string path;
|
||||
string path= "/";
|
||||
|
||||
enum Mode {
|
||||
PROMPT,
|
||||
DATA
|
||||
};
|
||||
Mode mode;
|
||||
Mode mode = PROMPT;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
@ -136,22 +138,23 @@ private:
|
|||
// callback implementations:
|
||||
void subscribe(const ParameterList &p);
|
||||
void unsubscribe(const ParameterList &p);
|
||||
void beginNasal(const ParameterList &p);
|
||||
|
||||
FGProps* _owner = nullptr;
|
||||
bool _colletingNasal = false;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
FGProps::PropsChannel::PropsChannel(FGProps* owner)
|
||||
: buffer(8192),
|
||||
path("/"),
|
||||
mode(PROMPT),
|
||||
_owner(owner)
|
||||
: buffer(8192)
|
||||
, _owner(owner)
|
||||
{
|
||||
setTerminator( "\r\n" );
|
||||
callback_map["subscribe"] = &PropsChannel::subscribe;
|
||||
callback_map["unsubscribe"] = &PropsChannel::unsubscribe;
|
||||
callback_map["nasal"] = &PropsChannel::beginNasal;
|
||||
}
|
||||
|
||||
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)
|
||||
void FGProps::PropsChannel::valueChanged(SGPropertyNode* ptr)
|
||||
|
@ -276,8 +291,35 @@ getValueTypeString( const SGPropertyNode *node )
|
|||
void
|
||||
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();
|
||||
SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
|
||||
SG_LOG( SG_IO, SG_DEBUG, "processing command = \"" << cmd << "\"" );
|
||||
|
||||
ParameterList tokens = simgear::strutils::split( cmd );
|
||||
|
||||
|
@ -506,12 +548,12 @@ FGProps::PropsChannel::foundTerminator()
|
|||
} else if ( command == "prompt" ) {
|
||||
mode = PROMPT;
|
||||
} else if (callback_map.find(command) != callback_map.end() ) {
|
||||
TelnetCallback t = callback_map[ command ];
|
||||
if (t)
|
||||
(this->*t) (tokens);
|
||||
else
|
||||
error("No matching callback found for command:"+command);
|
||||
}
|
||||
TelnetCallback t = callback_map[ command ];
|
||||
if (t)
|
||||
(this->*t) (tokens);
|
||||
else
|
||||
error("No matching callback found for command:"+command);
|
||||
}
|
||||
else if ( command == "seti" ) {
|
||||
string value, tmp;
|
||||
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\
|
||||
del <var> <nod> delete <nod> in <var>\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 );
|
||||
}
|
||||
}
|
||||
|
@ -615,7 +659,7 @@ unscubscribe <var> unscubscribe from property changes (var must be the property
|
|||
push( getTerminator() );
|
||||
}
|
||||
|
||||
if ( mode == PROMPT ) {
|
||||
if ( (mode == PROMPT) && !_colletingNasal) {
|
||||
string prompt = node->getPath();
|
||||
if (prompt.empty()) {
|
||||
prompt = "/";
|
||||
|
|
|
@ -49,7 +49,7 @@ private:
|
|||
/**
|
||||
* Server port to listen on.
|
||||
*/
|
||||
int port;
|
||||
int port = 5501;
|
||||
simgear::NetChannelPoller poller;
|
||||
|
||||
std::vector<PropsChannel*> _activeChannels;
|
||||
|
@ -69,22 +69,22 @@ public:
|
|||
/**
|
||||
* Start the telnet server.
|
||||
*/
|
||||
bool open();
|
||||
bool open() override;
|
||||
|
||||
/**
|
||||
* Process network activity.
|
||||
*/
|
||||
bool process();
|
||||
bool process() override;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool close();
|
||||
bool close() override;
|
||||
|
||||
/**
|
||||
* Accept a new client connection.
|
||||
*/
|
||||
void handleAccept();
|
||||
void handleAccept() override;
|
||||
|
||||
void removeChannel(PropsChannel* channel);
|
||||
};
|
||||
|
|
|
@ -298,20 +298,37 @@ FGNasalSys::~FGNasalSys()
|
|||
nasalSys = 0;
|
||||
}
|
||||
|
||||
bool FGNasalSys::parseAndRun(const char* sourceCode)
|
||||
bool FGNasalSys::parseAndRunWithOutput(const std::string& source, std::string& output, std::string& errors)
|
||||
{
|
||||
naContext ctx = naNewContext();
|
||||
naRef code = parse(ctx, "FGNasalSys::parseAndRun()", sourceCode,
|
||||
strlen(sourceCode));
|
||||
naRef code = parse(ctx, "FGNasalSys::parseAndRun()", source.c_str(),
|
||||
source.size(), errors);
|
||||
if(naIsNil(code)) {
|
||||
naFreeContext(ctx);
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FGNasalSys::parseAndRun(const std::string& source)
|
||||
{
|
||||
std::string errors;
|
||||
std::string output;
|
||||
return parseAndRunWithOutput(source, output, errors);
|
||||
}
|
||||
|
||||
#if 0
|
||||
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)
|
||||
{
|
||||
naContext ctx = naNewContext();
|
||||
naRef code = parse(ctx, fileName, src, len);
|
||||
std::string errors;
|
||||
naRef code = parse(ctx, fileName, src, len, errors);
|
||||
if(naIsNil(code)) {
|
||||
naFreeContext(ctx);
|
||||
return false;
|
||||
|
@ -1257,16 +1275,21 @@ naRef FGNasalSys::getModule(const char* moduleName)
|
|||
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;
|
||||
naRef srcfile = naNewString(ctx);
|
||||
naStr_fromdata(srcfile, (char*)filename, strlen(filename));
|
||||
naRef code = naParseCode(ctx, srcfile, 1, (char*)buf, len, &errLine);
|
||||
if(naIsNil(code)) {
|
||||
SG_LOG(SG_NASAL, SG_ALERT,
|
||||
"Nasal parse error: " << naGetError(ctx) <<
|
||||
" in "<< filename <<", line " << errLine);
|
||||
std::ostringstream errorMessageStream;
|
||||
errorMessageStream << "Nasal parse error: " << naGetError(ctx) <<
|
||||
" in "<< filename <<", line " << errLine;
|
||||
errors = errorMessageStream.str();
|
||||
SG_LOG(SG_NASAL, SG_ALERT, errors);
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
|
@ -1281,7 +1304,8 @@ bool FGNasalSys::handleCommand( const char* moduleName,
|
|||
SGPropertyNode* root)
|
||||
{
|
||||
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)) {
|
||||
naFreeContext(ctx);
|
||||
return false;
|
||||
|
|
|
@ -48,8 +48,12 @@ public:
|
|||
// indicate successful execution. Does *not* return any Nasal
|
||||
// values, because handling garbage-collected objects from C space
|
||||
// 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
|
||||
// Nasal script that can be invoked via a call() method. The
|
||||
// caller is expected to delete the FGNasalScript returned from
|
||||
|
@ -191,7 +195,8 @@ private:
|
|||
void loadScriptDirectory(simgear::Dir nasalDir);
|
||||
void addModule(std::string moduleName, simgear::PathList scripts);
|
||||
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();
|
||||
|
||||
bool _inited;
|
||||
|
|
Loading…
Reference in a new issue