281 if (_colletingNasal) {
282 std::string nasalSource = buffer.getData();
283 _colletingNasal =
false;
284 setTerminator(
"\r\n");
287 if (
globals->get_props()->getBoolValue(
"sim/secure-flag",
true) ==
true) {
288 SG_LOG(SG_IO, SG_ALERT,
"Telnet connection trying to run Nasal, blocked it.\n"
289 "Run the simulator with --allow-nasal-from-sockets to allow this.");
290 error(
"Simulator running in secure mode, Nasal execution blocked.");
294 std::string errors, output;
295 bool ok =
nasal->parseAndRunWithOutput(nasalSource, output, errors);
297 error(
"Nasal error" + errors);
298 }
else if (!output.empty()) {
300 push(output.c_str());
308 const char* cmd = buffer.getData();
309 SG_LOG(SG_IO, SG_DEBUG,
"processing command = \"" << cmd <<
"\"");
311 ParameterList tokens = simgear::strutils::split(cmd);
313 SGPropertyNode* node =
globals->get_props()->getNode(path.c_str());
316 if (!tokens.empty()) {
317 std::string
command = tokens[0];
320 SGPropertyNode* dir = getLsDir(node, tokens);
322 for (
int i = 0;
i < dir->nChildren();
i++) {
323 SGPropertyNode* child = dir->getChild(
i);
324 std::string line = child->getDisplayName(
true);
326 if (child->nChildren() > 0) {
329 if (mode == PROMPT) {
330 std::string value = child->getStringValue();
331 value = simgear::strutils::replace(value,
"\n",
"\\n");
332 value = simgear::strutils::replace(value,
"'",
"\\'");
333 line +=
" =\t'" + value +
"'\t(";
339 line += getTerminator();
343 SGPropertyNode* dir = getLsDir(node, tokens);
345 int n = dir->nChildren();
346 for (
int i = 0;
i < n;
i++) {
347 SGPropertyNode* child = dir->getChild(
i);
348 std::ostringstream text;
350 << child->nChildren()
351 <<
' ' << child->getNameString()
352 <<
' ' << child->getIndex()
354 if (child->getType() == simgear::props::DOUBLE) {
356 text <<
' ' << std::setprecision(16) << child->getDoubleValue();
358 text <<
' ' << simgear::strutils::replace(child->getStringValue(),
"\n",
"\\n");
360 text << getTerminator();
362 push(text.str().c_str());
365 }
else if (
command ==
"about") {
366 stringstream aboutinfo;
367 aboutinfo <<
"/sim/version/flightgear: " <<
fgGetString(
"/sim/version/flightgear") << endl;
368 aboutinfo <<
"Sentry.io UUID: " <<
fgGetString(
"/sim/crashreport/sentry-user-id") << endl;
369 aboutinfo <<
"/sim/version/simgear: " <<
fgGetString(
"/sim/version/simgear") << endl;
370 aboutinfo <<
"/sim/version/openscenegraph: " <<
fgGetString(
"/sim/version/openscenegraph") << endl;
371 aboutinfo <<
"/sim/version/build-id: " <<
fgGetString(
"/sim/version/build-id") << endl;
372 aboutinfo <<
"/sim/version/build-number: " <<
fgGetString(
"/sim/version/build-number") << endl;
373 aboutinfo <<
"/sim/version/build-type: " <<
fgGetString(
"/sim/version/build-type") << endl;
374 aboutinfo <<
"/sim/version/revision: " <<
fgGetString(
"/sim/version/revision") << endl;
375 aboutinfo <<
"/sim/rendering/gl-vendor: " <<
fgGetString(
"/sim/rendering/gl-vendor") << endl;
376 aboutinfo <<
"/sim/rendering/gl-renderer: " <<
fgGetString(
"/sim/rendering/gl-renderer") << endl;
377 aboutinfo <<
"/sim/rendering/gl-version: " <<
fgGetString(
"/sim/rendering/gl-version") << endl;
378 aboutinfo <<
"/sim/rendering/gl-shading-language-version: " <<
fgGetString(
"/sim/rendering/gl-shading-language-version") << endl;
379 aboutinfo <<
"/sim/rendering/max-texture-size: " <<
fgGetString(
"/sim/rendering/max-texture-size") << endl;
380 aboutinfo <<
"/sim/rendering/depth-buffer-bits: " <<
fgGetString(
"/sim/rendering/depth-buffer-bits") << endl;
382 push(aboutinfo.str().c_str());
383 push(getTerminator());
384 }
else if (
command ==
"dump") {
386 if (tokens.size() <= 1) {
387 writeProperties(buf, node);
389 push(buf.str().c_str());
390 push(getTerminator());
392 SGPropertyNode* child = node->getNode(tokens[1].c_str());
394 writeProperties(buf, child);
396 push(buf.str().c_str());
397 push(getTerminator());
399 node_not_found_error(tokens[1]);
403 if (tokens.size() == 2) {
404 SGPropertyNode* child = node->getNode(tokens[1].c_str());
407 path = node->getPath();
409 node_not_found_error(tokens[1]);
413 std::string pwd = node->getPath();
419 push(getTerminator());
421 if (tokens.size() == 2) {
423 SGPropertyNode* n = node->getNode(tokens[1].c_str());
424 if (n && n->getType() == simgear::props::DOUBLE) {
426 std::ostringstream s;
427 s << std::setprecision(16) << n->getDoubleValue();
430 value = node->getStringValue(tokens[1].c_str(),
"");
433 if (mode == PROMPT) {
439 node->getNode(tokens[1].c_str()));
445 push(getTerminator());
448 if (tokens.size() >= 2) {
449 std::string value, tmp;
450 for (
unsigned int i = 2;
i < tokens.size();
i++) {
455 node->getNode(tokens[1].c_str(),
true)
456 ->setStringValue(value.c_str());
458 if (mode == PROMPT) {
461 value = node->getStringValue(tokens[1].c_str(),
"");
462 tmp = tokens[1] +
" = '" + value +
"' (";
466 push(getTerminator());
469 }
else if (
command ==
"reinit") {
470 if (tokens.size() == 2) {
473 for (
unsigned int i = 1;
i < tokens.size(); ++
i) {
474 cout <<
"props: adding subsystem = " << tokens[
i] << endl;
475 SGPropertyNode* node = args.getNode(
"subsystem",
i - 1,
true);
476 node->setStringValue(tokens[
i].c_str());
479 ->execute(
"reinit", &args,
nullptr)) {
480 SG_LOG(SG_NETWORK, SG_ALERT,
481 "Command " << tokens[1] <<
" failed.");
482 if (mode == PROMPT) {
485 push(getTerminator());
488 if (mode == PROMPT) {
489 tmp +=
"<completed>";
491 push(getTerminator());
497 if (tokens.size() >= 2) {
498 SGPropertyNode_ptr args(
new SGPropertyNode);
499 if (tokens[1] ==
"reinit") {
500 for (
unsigned int i = 2;
i < tokens.size(); ++
i) {
501 cout <<
"props: adding subsystem = " << tokens[
i]
503 SGPropertyNode* node = args->getNode(
"subsystem",
i - 2,
true);
504 node->setStringValue(tokens[
i].c_str());
506 }
else if (tokens[1] ==
"set-sea-level-air-temp-degc") {
507 for (
unsigned int i = 2;
i < tokens.size(); ++
i) {
508 cout <<
"props: set-sl command = " << tokens[
i]
510 SGPropertyNode* node = args->getNode(
"temp-degc",
i - 2,
true);
511 node->setStringValue(tokens[
i].c_str());
513 }
else if (tokens[1] ==
"set-outside-air-temp-degc") {
514 for (
unsigned int i = 2;
i < tokens.size(); ++
i) {
515 cout <<
"props: set-oat command = " << tokens[
i]
517 SGPropertyNode* node = args->getNode(
"temp-degc",
i - 2,
true);
518 node->setStringValue(tokens[
i].c_str());
520 }
else if (tokens[1] ==
"timeofday") {
521 for (
unsigned int i = 2;
i < tokens.size(); ++
i) {
522 cout <<
"props: time of day command = " << tokens[
i]
524 SGPropertyNode* node = args->getNode(
"timeofday",
i - 2,
true);
525 node->setStringValue(tokens[
i].c_str());
527 }
else if (tokens[1] ==
"play-audio-message") {
528 if (tokens.size() == 4) {
529 cout <<
"props: play audio message = " << tokens[2]
530 <<
" " << tokens[3] << endl;
531 SGPropertyNode* node;
532 node = args->getNode(
"path", 0,
true);
533 node->setStringValue(tokens[2].c_str());
534 node = args->getNode(
"file", 0,
true);
535 node->setStringValue(tokens[3].c_str());
539 for (
unsigned int i = 2;
i < tokens.size(); ++
i) {
540 const auto pieces = simgear::strutils::split(tokens.at(
i),
"=", 1);
541 if (pieces.size() != 2) {
542 SG_LOG(SG_NETWORK, SG_WARN,
"malformed argument to Props protocol run:" << tokens.at(
i));
546 SGPropertyNode_ptr node = args->getNode(pieces.at(0), 0,
true);
547 node->setStringValue(pieces.at(1));
551 if (!
globals->get_commands()->execute(tokens[1].c_str(), args,
nullptr)) {
552 SG_LOG(SG_NETWORK, SG_ALERT,
553 "Command " << tokens[1] <<
" failed.");
554 if (mode == PROMPT) {
557 push(getTerminator());
560 if (mode == PROMPT) {
561 tmp +=
"<completed>";
563 push(getTerminator());
567 if (mode == PROMPT) {
568 tmp +=
"no command specified";
570 push(getTerminator());
577 }
else if (
command ==
"data") {
579 }
else if (
command ==
"prompt") {
581 }
else if (callback_map.find(
command) != callback_map.end()) {
582 TelnetCallback t = callback_map[
command];
586 error(
"No matching callback found for command:" +
command);
587 }
else if (
command ==
"seti") {
588 std::string value, tmp;
589 if (tokens.size() == 3) {
590 node->getNode(tokens[1].c_str(),
true)
591 ->setIntValue(
atoi(tokens[2].c_str()));
593 if (mode == PROMPT) {
594 tmp = tokens[1].c_str();
595 tmp +=
" " + tokens[2];
600 push(getTerminator());
603 error(
"incorrect number of arguments for " +
command);
607 std::string value, tmp;
608 if (tokens.size() == 3) {
609 node->getNode(tokens[1].c_str(),
true)
610 ->setDoubleValue(
atof(tokens[2].c_str()));
612 if (mode == PROMPT) {
613 tmp = tokens[1].c_str();
615 tmp += tokens[2].c_str();
620 push(getTerminator());
623 error(
"incorrect number of arguments for " +
command);
625 }
else if (
command ==
"setb") {
626 std::string tmp, value;
627 if (tokens.size() == 3) {
628 if (tokens[2] ==
"false" || tokens[2] ==
"0") {
629 node->getNode(tokens[1].c_str(),
true)
630 ->setBoolValue(
false);
633 if (tokens[2] ==
"true" || tokens[2] ==
"1") {
634 node->getNode(tokens[1].c_str(),
true)
635 ->setBoolValue(
true);
638 if (mode == PROMPT) {
639 tmp = tokens[1].c_str();
645 push(getTerminator());
648 error(
"incorrect number of arguments for " +
command);
652 if (tokens.size() == 3) {
653 node->getNode(tokens[1].c_str(),
true)->removeChild(tokens[2].c_str(), 0);
655 if (mode == PROMPT) {
657 tmp += tokens[1].c_str();
660 push(getTerminator());
663 error(
"incorrect number of arguments for " +
command);
668Valid commands are:\r\n\
670about prints system and version information, useful for debugging\r\n\
671cd <dir> cd to a directory, '..' to move back\r\n\
672data switch to raw data mode\r\n\
673dump dump current state (in xml)\r\n\
674get <var> show the value of a parameter\r\n\
675help show this help message\r\n\
676ls [<dir>] list directory\r\n\
677ls2 [<dir>] list directory (machine-readable format: num_children name index type value)\r\n\
678prompt switch to interactive mode (default)\r\n\
679pwd display your current path\r\n\
680quit terminate connection\r\n\
681run <command> run built in command\r\n\
682set <var> <val> set String <var> to a new <val>\r\n\
683setb <var> <val> set Bool <var> to a new <val> only work with the following value 0, 1, true, false\r\n\
684setd <var> <val> set Double <var> to a new <val>\r\n\
685setf <var> <val> alias for setd\r\n\
686seti <var> <val> set Int <var> to a new <val>\r\n\
687del <var> <nod> delete <nod> in <var>\r\n\
688subscribe <var> subscribe to property changes \r\n\
689unsubscribe <var> unsubscribe from property changes (var must be the property name/path used by subscribe)\r\n\
690nasal [EOF <marker>] execute arbitrary Nasal code (simulator must be running with Nasal allowed from sockets)\r\n\
696 }
catch (
const std::string& msg) {
697 std::string error =
"-ERR \"" + msg +
"\"";
699 push(getTerminator());
702 if ((mode == PROMPT) && !_colletingNasal) {
703 std::string prompt = node->getPath();
704 if (prompt.empty()) {
708 push(prompt.c_str());