7 #ifndef xindiserver_hpp
8 #define xindiserver_hpp
16 #include <unordered_set>
18 #include <mx/ioutils/fileUtils.hpp>
20 #include "../../libMagAOX/libMagAOX.hpp"
21 #include "../../magaox_git_version.h"
41 #define SSHTUNNEL_E_NOTUNNELS (-10)
51 typedef std::unordered_map<std::string, sshTunnel>
tunnelMapT;
60 mx::app::appConfigurator & config
63 std::vector<std::string> sections;
65 config.unusedSections(sections);
67 if( sections.size() == 0 )
75 for(
size_t i=0; i< sections.size(); ++i)
78 if( config.isSetUnused(mx::app::iniFile::makeKey(sections[i],
"remoteHost" )) &&
79 config.isSetUnused(mx::app::iniFile::makeKey(sections[i],
"localPort" )) &&
80 config.isSetUnused(mx::app::iniFile::makeKey(sections[i],
"remotePort" )) )
83 std::string remoteHost;
85 bool compress =
false;
87 config.configUnused( remoteHost, mx::app::iniFile::makeKey(sections[i],
"remoteHost" ) );
88 config.configUnused( localPort, mx::app::iniFile::makeKey(sections[i],
"localPort" ) );
89 config.configUnused( compress, mx::app::iniFile::makeKey(sections[i],
"compress" ) );
91 tmap[sections[i]] =
sshTunnel({remoteHost, localPort});
103 #define XINDISERVER_E_BADDRIVERSPEC (-100)
104 #define XINDISERVER_E_DUPLICATEDRIVER (-101)
105 #define XINDISERVER_E_VECTOREXCEPT (-102)
106 #define XINDISERVER_E_NOTUNNELS (-103)
107 #define XINDISERVER_E_TUNNELNOTFOUND (-104)
108 #define XINDISERVER_E_BADSERVERSPEC (-110)
245 config.add(
"indiserver.m",
"m",
"", argType::Required,
"indiserver",
"m",
false,
"int",
"indiserver kills client if it gets more than this many MB behind, default 50");
246 config.add(
"indiserver.N",
"N",
"", argType::True,
"indiserver",
"N",
false,
"bool",
"indiserver: ignore /tmp/noindi. Capitalized to avoid conflict with --name");
247 config.add(
"indiserver.p",
"p",
"", argType::Required,
"indiserver",
"p",
false,
"int",
"indiserver: alternate IP port, default 7624");
248 config.add(
"indiserver.v",
"v",
"", argType::True,
"indiserver",
"v",
false,
"int",
"indiserver: log verbosity, -v, -vv or -vvv");
249 config.add(
"indiserver.x",
"x",
"", argType::True,
"indiserver",
"x",
false,
"bool",
"exit after last client disconnects -- FOR PROFILING ONLY");
251 config.add(
"local.drivers",
"L",
"local.drivers" , argType::Required,
"local",
"drivers",
false,
"vector string",
"List of local drivers to start.");
252 config.add(
"remote.drivers",
"R",
"remote.drivers" , argType::Required,
"remote",
"drivers",
false,
"vector string",
"List of remote drivers to start, in the form of name@tunnel, where tunnel is the name of a tunnel specified in sshTunnels.conf.");
254 config.add(
"remote.servers",
"",
"remote.servers" , argType::Required,
"remote",
"servers",
false,
"vector string",
"List of servers to load remote drivers for, in the form of name@tunnel. Name is used to load the name.conf configuration file, and tunnel is the name of a tunnel specified in sshTunnels.conf.");
272 config(
m_local,
"local.drivers");
284 indiserverCommand.push_back(
"indiserver");
288 indiserverCommand.push_back(
"-m");
289 indiserverCommand.push_back(mx::ioutils::convertToString(
indiserver_m));
292 if(
indiserver_n ==
true) indiserverCommand.push_back(
"-n");
296 indiserverCommand.push_back(
"-p");
297 indiserverCommand.push_back(mx::ioutils::convertToString(
indiserver_p));
300 if(
indiserver_v == 1) indiserverCommand.push_back(
"-v");
302 if(
indiserver_v == 2) indiserverCommand.push_back(
"-vv");
304 if(
indiserver_v >= 3) indiserverCommand.push_back(
"-vvv");
306 if(
indiserver_x ==
true) indiserverCommand.push_back(
"-x");
325 for(
size_t i=0; i<
m_local.size(); ++i)
327 size_t bad =
m_local[i].find_first_of(
"@:/", 0);
329 if(bad != std::string::npos)
331 log<software_critical>({__FILE__, __LINE__,
"Local driver can't have host spec or path(@,:,/): " +
m_local[i]});
338 log<software_critical>({__FILE__, __LINE__,
"Duplicate driver name: " +
m_local[i]});
348 driverArgs.push_back(dname);
352 log<software_critical>({__FILE__, __LINE__,
"Exception thrown by std::vector"});
363 for(
size_t i=0; i <
m_remote.size(); ++i)
370 if(p == 0 || p == std::string::npos)
372 log<software_critical>({__FILE__, __LINE__,
"Error parsing remote driver specification: " +
m_remote[i] +
"\n"});
381 log<software_critical>({__FILE__, __LINE__,
"Duplicate driver name: " + driver});
385 std::ostringstream oss;
389 log<software_critical>({__FILE__, __LINE__,
"No tunnels specified.\n"});
395 log<software_critical>({__FILE__, __LINE__,
"Tunnel not found for: " +
m_remote[i] +
"\n"});
401 oss << driver <<
"@localhost:" <<
m_tunnels[tunnel].m_localPort;
405 driverArgs.push_back(oss.str());
409 log<software_critical>({__FILE__, __LINE__,
"Exception thrown by vector::push_back."});
428 if(p == 0 || p == std::string::npos)
430 log<software_critical>({__FILE__, __LINE__,
"Error parsing remote server specification: " +
m_remote[j] +
"\n"});
439 log<software_critical>({__FILE__, __LINE__,
"No tunnels specified.\n"});
445 log<software_critical>({__FILE__, __LINE__,
"Tunnel not found for: " +
m_remote[j] +
"\n"});
450 mx::app::appConfigurator rsconfig;
452 rsconfig.add(
"local.drivers",
"",
"" , argType::Required,
"local",
"drivers",
false,
"",
"");
454 std::string rsconfigPath =
m_configDir +
"/" + server +
".conf";
456 rsconfig.readConfig(rsconfigPath);
458 std::vector<std::string> local;
460 rsconfig(local,
"local.drivers");
462 for(
size_t i=0; i < local.size(); ++i)
464 size_t bad = local[i].find_first_of(
"@:/", 0);
466 if(bad != std::string::npos)
468 log<software_critical>({__FILE__, __LINE__,
"Remote server's Local driver can't have host spec or path(@,:,/): " + local[i]});
475 log<software_critical>({__FILE__, __LINE__,
"Duplicate driver name from remote server: " + local[i]});
481 std::ostringstream oss;
483 oss << local[i] <<
"@localhost:" <<
m_tunnels[tunnel].m_localPort;
487 driverArgs.push_back(oss.str());
491 log<software_critical>({__FILE__, __LINE__,
"Exception thrown by vector::push_back."});
507 std::string coml =
"Starting indiserver with command: ";
519 if (pipe(filedes) == -1)
521 log<software_error>({__FILE__, __LINE__, errno});
530 log<software_error>({__FILE__, __LINE__, errno,
"fork failed"});
538 while ((dup2(filedes[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
551 execvp(
"indiserver", (
char *
const*) drivers);
553 log<software_error>({__FILE__, __LINE__, errno,
"execvp returned"});
565 std::string coml =
"indiserver started with PID " + mx::ioutils::convertToString(
m_isPID);
585 catch(
const std::exception & e )
587 log<software_error>({__FILE__,__LINE__, std::string(
"Exception on I.S. log thread start: ") + e.what()});
592 log<software_error>({__FILE__,__LINE__,
"Unkown exception on I.S. log thread start"});
598 log<software_error>({__FILE__, __LINE__,
"I.S. log thread did not start"});
605 int rv = pthread_setschedparam(
m_isLogThread.native_handle(), SCHED_OTHER, &sp);
609 log<software_error>({__FILE__, __LINE__, rv,
"Error setting thread params."});
627 ssize_t count = read(
m_isSTDERR, buffer,
sizeof(buffer)-1);
632 else if(count > (ssize_t)
sizeof(buffer)-1)
634 log<software_error>({__FILE__, __LINE__,
"read returned too many bytes."});
639 buffer[count] =
'\0';
644 if(logs.back() ==
'\n')
647 while(bol < logs.size())
649 size_t eol = logs.find(
'\n', bol);
650 if(eol == std::string::npos)
break;
668 ed = logs.find(
':', st);
669 if(ed != std::string::npos) ed = logs.find(
':', ed+1);
670 if(ed != std::string::npos) ed = logs.find(
':', ed+1);
672 if(ed == std::string::npos)
679 std::string ts = logs.substr(st, ed-st);
684 mx::sys::ISO8601dateBreakdown(bdt.tm_year, bdt.tm_mon, bdt.tm_mday, bdt.tm_hour, bdt.tm_min, dsec, ts);
688 bdt.tm_sec = (int) dsec;
694 tsp.
time_s = timegm(&bdt);
698 st = logs.find_first_not_of(
" ", ed);
700 if(st == std::string::npos) st = ed;
701 if(st == logs.size())
703 log<software_error>({__FILE__, __LINE__,
"Did not find log entry."});
707 std::string logstr = logs.substr(st, logs.size()-st);
712 if(logstr.find(
"xindidriver") != std::string::npos)
714 if(logstr.find(
"failed to lock") != std::string::npos)
719 else if(logstr.find(
"bind: Address already in use") != std::string::npos)
740 log<software_critical>({__FILE__, __LINE__});
746 log<software_critical>({__FILE__, __LINE__});
752 log<software_critical>({__FILE__, __LINE__});
758 log<software_critical>({__FILE__, __LINE__});
765 std::string path1 =
"/opt/MagAOX/bin/xindidriver";
766 for(
size_t i=0; i<
m_local.size(); ++i)
768 elevatedPrivileges elPriv(
this);
774 if(rv < 0 && errno != EEXIST)
776 log<software_error>({__FILE__, __LINE__, errno});
777 log<software_error>({__FILE__, __LINE__,
"Failed to create symlink for driver: " +
m_local[i] +
". Continuing."});
790 log<software_critical>({__FILE__, __LINE__});
796 log<software_critical>({__FILE__, __LINE__});
807 pid_t result = waitpid(
m_isPID, &status, WNOHANG);
840 log<software_error>({__FILE__, __LINE__, errno });
841 log<software_error>({__FILE__, __LINE__,
"Error on write to i.s. log thread. Sending SIGTERM."});
The base-class for MagAO-X applications.
stateCodes::stateCodeT state()
Get the current state code.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
std::string m_configDir
The path to configuration files for MagAOX.
std::string m_configBase
The name of a base config class for this app (minus .conf).
int addRemoteServers(std::vector< std::string > &driverArgs)
Validate the remote server entries, read the associated config files for local drivers,...
std::vector< std::string > m_indiserverCommand
The command line arguments to indiserver.
int isLogThreadStart()
Start the log capture.
int indiserver_m
The indiserver MB behind setting (passed to indiserver)
int m_isSTDERR
The output of stderr of the indiserver process.
virtual int appLogic()
Implementation of the FSM for xindiserver.
int addRemoteDrivers(std::vector< std::string > &driverArgs)
Validate the remote driver entries, and append them to the indi server command line arguments.
std::vector< std::string > m_local
List of local drivers passed in by config.
void isLogThreadExec()
Execute the log capture.
virtual void setupConfig()
virtual int appShutdown()
Kills indiserver, and wakes up the log capture thread.
~xindiserver() noexcept
D'tor, declared and defined for noexcept.
static void _isLogThreadStart(xindiserver *l)
Thread starter, called by isLogThreadStart on thread construction. Calls isLogThreadExec.
pid_t m_isPID
The PID of the indiserver process.
xindiserver()
Default c'tor.
int forkIndiserver()
Forks and exec's the indiserver process with the command constructed from local, remote,...
bool indiserver_n
The indiserver ignore /tmp/noindi flag (passed to indiserver)
int addLocalDrivers(std::vector< std::string > &driverArgs)
Validate the local driver strings, and append them to the indi server command line arguments.
bool indiserver_x
The indiserver terminate after last exit flag (passed to indiserver)
std::string m_driverPath
The path to the local drivers.
std::vector< std::string > m_remote
List of remote drivers passed in by config.
std::vector< std::string > m_remoteServers
List of other INDI server config files to read remote drivers from.
std::unordered_set< std::string > m_driverNames
List of driver names processed for command line, used to prevent duplication.
int m_isSTDERR_input
The input end of stderr, used to wake up the log thread on shutdown.
std::thread m_isLogThread
A separate thread for capturing indiserver logs.
int m_isLogThreadPrio
Priority of the indiserver log capture thread, should normally be 0.
tunnelMapT m_tunnels
Map of the ssh tunnels, used for processing the remote drivers in m_remote.
int indiserver_v
The indiserver verbosity (passed to indiserver)
virtual int appStartup()
Startup functions.
int indiserver_p
The indiserver port (passed to indiserver)
virtual void loadConfig()
int constructIndiserverCommand(std::vector< std::string > &indiserverCommand)
Construct the vector of indiserver arguments for exec.
int processISLog(std::string logs)
Process a log entry from indiserver, putting it into MagAO-X standard form.
#define MAGAOX_path
The path to the MagAO-X system files.
#define MAGAOX_driverRelPath
The relative path to the INDI drivers.
int8_t logPrioT
The type of the log priority code.
@ FAILURE
The application has failed, should be used when m_shutdown is set for an error.
@ CONNECTED
The application has connected to the device or service.
std::unordered_map< std::string, sshTunnel > tunnelMapT
The map used to hold tunnel specifications.
int loadSSHTunnelConfigs(tunnelMapT &tmap, mx::app::appConfigurator &config)
Create the tunnel map from a configurator.
Structure to hold an sshTunnel specification, used for created command line args for indiserver.
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
constexpr static logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
uint32_t nanosecT
The type used for nanoseconds.
void log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry, including a message.
int logLevel(logPrioT newLev)
Set a new value of logLevel.
A simple text log, a string-type log.
A fixed-width timespec structure.
nanosecT time_ns
Nanoseconds.
secT time_s
Time since the Unix epoch.
#define XINDISERVER_E_BADDRIVERSPEC
#define XINDISERVER_E_NOTUNNELS
#define XINDISERVER_E_DUPLICATEDRIVER
#define XINDISERVER_E_BADSERVERSPEC
#define XINDISERVER_E_VECTOREXCEPT
#define SSHTUNNEL_E_NOTUNNELS
#define XINDISERVER_E_TUNNELNOTFOUND