API
 
Loading...
Searching...
No Matches
xindiserver.hpp
Go to the documentation of this file.
1/** \file xindiserver.hpp
2 * \brief The MagAO-X INDI Server wrapper header.
3 *
4 * \ingroup xindiserver_files
5 */
6
7#ifndef xindiserver_hpp
8#define xindiserver_hpp
9
10#include <sys/wait.h>
11
12#include <iostream>
13#include <vector>
14#include <string>
15#include <map>
16#include <unordered_set>
17
18#include <mx/ioutils/fileUtils.hpp>
19
20#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
21#include "../../magaox_git_version.h"
22
23/** \defgroup xindiserver INDI Server wrapper.
24 * \brief Manages INDI server in the MagAO-X context.
25 *
26 * <a href="../handbook/operating/software/apps/network.html#xindiserver">Application Documentation</a>
27 *
28 * \ingroup apps
29 *
30 */
31
32/** \defgroup xindiserver_files xindiserver Files
33 * \ingroup xindiserver
34 */
35
36namespace MagAOX
37{
38namespace app
39{
40
41#define SSHTUNNEL_E_NOTUNNELS (-10)
42
43/// Structure to hold an sshTunnel specification, used for created command line args for indiserver
44struct sshTunnel
45{
46 std::string m_remoteHost;
47 int m_localPort {0};
48};
49
50///The map used to hold tunnel specifications.
51typedef std::unordered_map<std::string, sshTunnel> tunnelMapT;
52
53/// Create the tunnel map from a configurator
54/**
55 * \returns 0 on success
56 * \returns SSHTUNNEL_E_NOTUNNELS if no tunnels are found (< 0).
57 */
58inline
59int loadSSHTunnelConfigs( tunnelMapT & tmap, ///< [out] the tunnel map which will be populated
60 mx::app::appConfigurator & config ///< [in] the configurator which contains tunnel specifications.
61 )
62{
63 std::vector<std::string> sections;
64
65 config.unusedSections(sections);
66
67 if( sections.size() == 0 )
68 {
70 }
71
72 size_t matched = 0;
73
74 //Now see if any sections match a tunnel specification
75 for(size_t i=0; i< sections.size(); ++i)
76 {
77 //A tunnel as remoteHost, localPort, and remotePort.
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" )) )
81 {
82
83 std::string remoteHost;
84 int localPort = 0;
85 bool compress = false;
86
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" ) );
90
91 tmap[sections[i]] = sshTunnel({remoteHost, localPort});
92
93 ++matched;
94 }
95 }
96
97 if(matched == 0) return SSHTUNNEL_E_NOTUNNELS;
98
99 return 0;
100}
101
102
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)
109
110
111/** The INDI Server wrapper application class.
112 *
113 * \ingroup xindiserver
114 *
115 */
116class xindiserver : public MagAOXApp<false>
117{
118
119 //Give the test harness access.
120 friend class xindiserver_test;
121
122protected:
123
124 int indiserver_m {-1}; ///< The indiserver MB behind setting (passed to indiserver)
125 bool indiserver_n {false}; ///< The indiserver ignore /tmp/noindi flag (passed to indiserver)
126 int indiserver_p {-1}; ///< The indiserver port (passed to indiserver)
127 int indiserver_v {-1}; ///< The indiserver verbosity (passed to indiserver)
128 bool indiserver_x {false}; ///< The indiserver terminate after last exit flag (passed to indiserver)
129
130 std::string m_driverPath; ///< The path to the local drivers
131 std::vector<std::string> m_local; ///< List of local drivers passed in by config
132 std::vector<std::string> m_remote; ///< List of remote drivers passed in by config
133 std::unordered_set<std::string> m_driverNames; ///< List of driver names processed for command line, used to prevent duplication.
134
135 std::vector<std::string> m_remoteServers; ///< List of other INDI server config files to read remote drivers from.
136
137 tunnelMapT m_tunnels; ///< Map of the ssh tunnels, used for processing the remote drivers in m_remote.
138
139 std::vector<std::string> m_indiserverCommand; ///< The command line arguments to indiserver
140
141 pid_t m_isPID {0}; ///< The PID of the indiserver process
142
143 int m_isSTDERR {-1}; ///< The output of stderr of the indiserver process
144 int m_isSTDERR_input {-1}; ///< The input end of stderr, used to wake up the log thread on shutdown.
145
146 int m_isLogThreadPrio {0}; ///< Priority of the indiserver log capture thread, should normally be 0.
147
148 std::thread m_isLogThread; ///< A separate thread for capturing indiserver logs
149
150public:
151 /// Default c'tor.
152 xindiserver();
153
154 /// D'tor, declared and defined for noexcept.
157
158 virtual void setupConfig();
159
160 virtual void loadConfig();
161
162 ///Construct the vector of indiserver arguments for exec.
163 /** The first entry is argv[0], that is "indiserver".
164 *
165 * \returns 0 on success.
166 * \returns -1 on error, including if an exception is caught.
167 */
168 int constructIndiserverCommand(std::vector<std::string> & indiserverCommand /**< [out] the vector of command line arguments for exec */);
169
170 ///Validate the local driver strings, and append them to the indi server command line arguments.
171 /** Checks that the local driver specs don't contain @,:, or /. Then prepends the MagAO-X standard
172 * driver path, and then appends to the driverArgs vector passed in.
173 *
174 * \returns 0 on success.
175 * \returns -1 on error, either from failed validation or an exception in std::vector.
176 */
177 int addLocalDrivers( std::vector<std::string> & driverArgs /**< [out] the vector of command line arguments for exec*/);
178
179
180 ///Validate the remote driver entries, and append them to the indi server command line arguments.
181 /** Parses the remote driver specs, then
182 * constructs the command line arguments and appends them to the driverArgs vector passed in.
183 *
184 * \returns 0 on success.
185 * \returns -1 on error, either from failed validation or an exception in std::vector.
186 */
187 int addRemoteDrivers( std::vector<std::string> & driverArgs /**< [out] the vector of command line arguments for exec*/);
188
189 ///Validate the remote server entries, read the associated config files for local drivers, and append them to the indi server command line arguments as remote ddrivers.
190 /** Parses the remote server specs, then reads the remote server config files, and then
191 * constructs the command line arguments and appends them to the driverArgs vector passed in.
192 *
193 * \returns 0 on success.
194 * \returns -1 on error, either from failed validation or an exception in std::vector.
195 */
196 int addRemoteServers( std::vector<std::string> & driverArgs /**< [out] the vector of command line arguments for exec*/);
197
198 ///Forks and exec's the indiserver process with the command constructed from local, remote, and hosts.
199 /** Also saves the PID and stderr pipe file descriptors for log capture.
200 *
201 * \returns 0 on success
202 * \returns -1 on error (fatal)
203 */
204 int forkIndiserver();
205
206 ///Thread starter, called by isLogThreadStart on thread construction. Calls isLogThreadExec.
207 static void _isLogThreadStart( xindiserver * l /**< [in] a pointer to a xindiserver instance (normally this) */);
208
209 /// Start the log capture.
210 int isLogThreadStart();
211
212 /// Execute the log capture.
213 void isLogThreadExec();
214
215 /// Process a log entry from indiserver, putting it into MagAO-X standard form
216 int processISLog( std::string logs );
217
218 /// Startup functions
219 /**
220 * Forks and execs the actual indiserver. Captures its stderr output for logging.
221 */
222 virtual int appStartup();
223
224 /// Implementation of the FSM for xindiserver.
225 virtual int appLogic();
226
227 /// Kills indiserver, and wakes up the log capture thread.
228 virtual int appShutdown();
229
230
231};
232
233inline
234xindiserver::xindiserver() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
235{
236 //Use the sshTunnels.conf config file
237 m_configBase = "sshTunnels";
238
239 return;
240}
241
242inline
244{
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");
250
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.");
253
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.");
255
256}
257
258
259
260inline
262{
263 //indiserver config:
264 config(indiserver_m, "indiserver.m");
265 config(indiserver_n, "indiserver.N");
266 config(indiserver_p, "indiserver.p");
267
268 indiserver_v = config.verbosity("indiserver.v");
269
270 config(indiserver_x, "indiserver.x");
271
272 config(m_local, "local.drivers");
273 config(m_remote, "remote.drivers");
274 config(m_remoteServers, "remote.servers");
275
277}
278
279inline
280int xindiserver::constructIndiserverCommand( std::vector<std::string> & indiserverCommand)
281{
282 try
283 {
284 indiserverCommand.push_back("indiserver");
285
286 if(indiserver_m > 0)
287 {
288 indiserverCommand.push_back("-m");
289 indiserverCommand.push_back(mx::ioutils::convertToString(indiserver_m));
290 }
291
292 if(indiserver_n == true) indiserverCommand.push_back("-n");
293
294 if(indiserver_p > 0)
295 {
296 indiserverCommand.push_back("-p");
297 indiserverCommand.push_back(mx::ioutils::convertToString(indiserver_p));
298 }
299
300 if(indiserver_v == 1) indiserverCommand.push_back("-v");
301
302 if(indiserver_v == 2) indiserverCommand.push_back("-vv");
303
304 if(indiserver_v >= 3) indiserverCommand.push_back("-vvv");
305
306 if(indiserver_x == true) indiserverCommand.push_back("-x");
307 }
308 catch(...)
309 {
310 log<software_critical>(software_log::messageT(__FILE__, __LINE__, "Exception thrown by std::vector."));
311 return -1;
312 }
313
314 return 0;
315}
316
317inline
318int xindiserver::addLocalDrivers( std::vector<std::string> & driverArgs )
319{
321 m_driverPath += "/";
323 m_driverPath += "/";
324
325 for(size_t i=0; i< m_local.size(); ++i)
326 {
327 size_t bad = m_local[i].find_first_of("@:/", 0);
328
329 if(bad != std::string::npos)
330 {
331 log<software_critical>({__FILE__, __LINE__, "Local driver can't have host spec or path(@,:,/): " + m_local[i]});
332
334 }
335
336 if( m_driverNames.count(m_local[i]) > 0)
337 {
338 log<software_critical>({__FILE__, __LINE__, "Duplicate driver name: " + m_local[i]});
340 }
341
342 m_driverNames.insert(m_local[i]);
343
344 std::string dname = m_driverPath + m_local[i];
345
346 try
347 {
348 driverArgs.push_back(dname);
349 }
350 catch(...)
351 {
352 log<software_critical>({__FILE__, __LINE__, "Exception thrown by std::vector"});
354 }
355 }
356
357 return 0;
358}
359
360inline
361int xindiserver::addRemoteDrivers( std::vector<std::string> & driverArgs )
362{
363 for(size_t i=0; i < m_remote.size(); ++i)
364 {
365 std::string driver;
366 std::string tunnel;
367
368 size_t p = m_remote[i].find('@');
369
370 if(p == 0 || p == std::string::npos)
371 {
372 log<software_critical>({__FILE__, __LINE__, "Error parsing remote driver specification: " + m_remote[i] + "\n"});
374 }
375
376 driver = m_remote[i].substr(0, p);
377 tunnel = m_remote[i].substr(p+1);
378
379 if( m_driverNames.count(driver) > 0)
380 {
381 log<software_critical>({__FILE__, __LINE__, "Duplicate driver name: " + driver});
383 }
384
385 std::ostringstream oss;
386
387 if(m_tunnels.size() == 0)
388 {
389 log<software_critical>({__FILE__, __LINE__, "No tunnels specified.\n"});
391 }
392
393 if(m_tunnels.count(tunnel) != 1)
394 {
395 log<software_critical>({__FILE__, __LINE__, "Tunnel not found for: " + m_remote[i] + "\n"});
397 }
398
399 m_driverNames.insert(driver);
400
401 oss << driver << "@localhost:" << m_tunnels[tunnel].m_localPort;
402
403 try
404 {
405 driverArgs.push_back(oss.str());
406 }
407 catch(...)
408 {
409 log<software_critical>({__FILE__, __LINE__, "Exception thrown by vector::push_back."});
411 }
412 }
413
414 return 0;
415
416}
417
418inline
419int xindiserver::addRemoteServers( std::vector<std::string> & driverArgs )
420{
421 for(size_t j=0; j < m_remoteServers.size(); ++j)
422 {
423 std::string server;
424 std::string tunnel;
425
426 size_t p = m_remoteServers[j].find('@');
427
428 if(p == 0 || p == std::string::npos)
429 {
430 log<software_critical>({__FILE__, __LINE__, "Error parsing remote server specification: " + m_remote[j] + "\n"});
432 }
433
434 server = m_remoteServers[j].substr(0, p);
435 tunnel = m_remoteServers[j].substr(p+1);
436
437 if(m_tunnels.size() == 0)
438 {
439 log<software_critical>({__FILE__, __LINE__, "No tunnels specified.\n"});
441 }
442
443 if(m_tunnels.count(tunnel) != 1)
444 {
445 log<software_critical>({__FILE__, __LINE__, "Tunnel not found for: " + m_remote[j] + "\n"});
447 }
448
449 //Now we create a local app configurator, and read the other server's config file
450 mx::app::appConfigurator rsconfig;
451
452 rsconfig.add("local.drivers", "", "" , argType::Required, "local", "drivers", false, "", "");
453
454 std::string rsconfigPath = m_configDir + "/" + server + ".conf";
455
456 rsconfig.readConfig(rsconfigPath);
457
458 std::vector<std::string> local;
459
460 rsconfig(local, "local.drivers");
461
462 for(size_t i=0; i < local.size(); ++i)
463 {
464 size_t bad = local[i].find_first_of("@:/", 0);
465
466 if(bad != std::string::npos)
467 {
468 log<software_critical>({__FILE__, __LINE__, "Remote server's Local driver can't have host spec or path(@,:,/): " + local[i]});
469
471 }
472
473 if( m_driverNames.count(local[i]) > 0)
474 {
475 log<software_critical>({__FILE__, __LINE__, "Duplicate driver name from remote server: " + local[i]});
477 }
478
479 m_driverNames.insert(local[i]);
480
481 std::ostringstream oss;
482
483 oss << local[i] << "@localhost:" << m_tunnels[tunnel].m_localPort;
484
485 try
486 {
487 driverArgs.push_back(oss.str());
488 }
489 catch(...)
490 {
491 log<software_critical>({__FILE__, __LINE__, "Exception thrown by vector::push_back."});
493 }
494 }
495
496 }
497
498 return 0;
499}
500
501inline
503{
504
506 {
507 std::string coml = "Starting indiserver with command: ";
508 for(size_t i=0;i<m_indiserverCommand.size();++i)
509 {
511 coml += " ";
512 }
513
515 std::cerr << coml << std::endl;
516 }
517
518 int filedes[2];
519 if (pipe(filedes) == -1)
520 {
522 return -1;
523 }
524
525
526 m_isPID = fork();
527
528 if(m_isPID < 0)
529 {
530 log<software_error>({__FILE__, __LINE__, errno, "fork failed"});
531 return -1;
532 }
533
534
535 if(m_isPID == 0)
536 {
537 //Route STDERR of child to pipe input.
538 while ((dup2(filedes[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
539 close(filedes[1]);
540 close(filedes[0]);
541
542 const char ** drivers = new const char*[m_indiserverCommand.size()+1];
543
544 for(size_t i=0; i< m_indiserverCommand.size(); ++i)
545 {
546 drivers[i] = (m_indiserverCommand[i].data());
547 }
549
550
551 execvp("indiserver", (char * const*) drivers);
552
553 log<software_error>({__FILE__, __LINE__, errno, "execvp returned"});
554
555 delete[] drivers;
556
557 return -1;
558 }
559
560 m_isSTDERR = filedes[0];
562
564 {
565 std::string coml = "indiserver started with PID " + mx::ioutils::convertToString(m_isPID);
567 }
568
569 return 0;
570}
571
572inline
574{
575 l->isLogThreadExec();
576}
577
578inline
580{
581 try
582 {
583 m_isLogThread = std::thread( _isLogThreadStart, this);
584 }
585 catch( const std::exception & e )
586 {
587 log<software_error>({__FILE__,__LINE__, std::string("Exception on I.S. log thread start: ") + e.what()});
588 return -1;
589 }
590 catch( ... )
591 {
592 log<software_error>({__FILE__,__LINE__, "Unkown exception on I.S. log thread start"});
593 return -1;
594 }
595
596 if(!m_isLogThread.joinable())
597 {
598 log<software_error>({__FILE__, __LINE__, "I.S. log thread did not start"});
599 return -1;
600 }
601
603 sp.sched_priority = m_isLogThreadPrio;
604
605 int rv = pthread_setschedparam( m_isLogThread.native_handle(), SCHED_OTHER, &sp);
606
607 if(rv != 0)
608 {
609 log<software_error>({__FILE__, __LINE__, rv, "Error setting thread params."});
610 return -1;
611 }
612
613 return 0;
614
615}
616
617
618
619inline
621{
622 char buffer[4096];
623
624 std::string logs;
625 while(m_shutdown == 0)
626 {
627 ssize_t count = read(m_isSTDERR, buffer, sizeof(buffer)-1); //Make wure we always have room for \0
628 if (count <= 0 || m_shutdown == 1)
629 {
630 continue;
631 }
632 else if(count > (ssize_t) sizeof(buffer)-1)
633 {
634 log<software_error>({__FILE__, __LINE__, "read returned too many bytes."});
635 continue;
636 }
637 else
638 {
639 buffer[count] = '\0';
640
641 logs += buffer;
642
643 //Keep reading until \n found, then process.
644 if(logs.back() == '\n')
645 {
646 size_t bol = 0;
647 while(bol < logs.size())
648 {
649 size_t eol = logs.find('\n', bol);
650 if(eol == std::string::npos) break;
651
652 processISLog(logs.substr(bol, eol-bol));
653 bol = eol + 1;
654 }
655 logs = "";
656 }
657 }
658 }
659
660}
661
662inline
663int xindiserver::processISLog( std::string logs )
664{
665 size_t st = 0;
666 size_t ed;
667
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);
671
672 if(ed == std::string::npos)
673 {
674 //log<software_error>({__FILE__, __LINE__, "Did not find timestamp : in log entry"});
676 return 0;
677 }
678
679 std::string ts = logs.substr(st, ed-st);
680
681 double dsec;
682
683 tm bdt;
684 mx::sys::ISO8601dateBreakdown(bdt.tm_year, bdt.tm_mon, bdt.tm_mday, bdt.tm_hour, bdt.tm_min, dsec, ts);
685
686 bdt.tm_year -= 1900;
687 bdt.tm_mon -= 1;
688 bdt.tm_sec = (int) dsec;
689 bdt.tm_isdst = 0;
690 bdt.tm_gmtoff = 0;
691
693
694 tsp.time_s = timegm(&bdt);
695 tsp.time_ns = (nanosecT) ((dsec-bdt.tm_sec)*1e9 + 0.5);
696
697 ++ed;
698 st = logs.find_first_not_of(" ", ed);
699
700 if(st == std::string::npos) st = ed;
701 if(st == logs.size())
702 {
703 log<software_error>({__FILE__, __LINE__, "Did not find log entry."});
704 return -1;
705 }
706
707 std::string logstr = logs.substr(st, logs.size()-st);
708
710
711 //Look for fatal errors
712 if(logstr.find("xindidriver") != std::string::npos) //Errors from xindidriver
713 {
714 if(logstr.find("failed to lock") != std::string::npos)
715 {
717 }
718 }
719 else if(logstr.find("bind: Address already in use") != std::string::npos) //Errors from indiserver
720 {
722 }
723
724 m_log.log<text_log>(tsp, "IS: " + logstr, prio);
725
727 {
729 m_shutdown = true;
730 }
731
732 return 0;
733}
734
735inline
737{
739 {
741 return -1;
742 }
743
745 {
747 return -1;
748 }
749
751 {
753 return -1;
754 }
755
757 {
759 return -1;
760 }
761
762 //--------------------
763 //Make symlinks
764 //--------------------
765 std::string path1 = "/opt/MagAOX/bin/xindidriver";
766 for(size_t i=0; i<m_local.size(); ++i)
767 {
768 elevatedPrivileges elPriv(this);
769
770 std::cerr << "creating symlink " << path1 << " " << m_driverPath + m_local[i] << "\n";
771
772 int rv = symlink(path1.c_str(), (m_driverPath + m_local[i]).c_str());
773
774 if(rv < 0 && errno != EEXIST)
775 {
777 log<software_error>({__FILE__, __LINE__, "Failed to create symlink for driver: " + m_local[i] + ". Continuing."});
778 }
779 }
780
781 m_local.clear();
782 m_remote.clear();
783 m_tunnels.clear();
784
785 //--------------------
786 //Now start indiserver
787 //--------------------
788 if(forkIndiserver() < 0)
789 {
791 return -1;
792 }
793
794 if(isLogThreadStart() < 0)
795 {
797 return -1;
798 }
799
800 return 0;
801}
802
803inline
805{
806 int status;
808 if (result == 0)
809 {
811 }
812 else
813 {
814 //We don't care why. If indiserver is not alive while in this function then it's a fatal error.
815 log<text_log>("indiserver has exited", logPrio::LOG_CRITICAL);
817 return -1;
818 }
819
820
821
822 return 0;
823}
824
825inline
827{
828
829 if(m_isPID > 0)
830 {
832 }
833
834 if(m_isSTDERR_input >= 0)
835 {
836 char w = '\0';
837 ssize_t nwr = write(m_isSTDERR_input, &w, 1);
838 if(nwr != 1)
839 {
841 log<software_error>({__FILE__, __LINE__, "Error on write to i.s. log thread. Sending SIGTERM."});
842 pthread_kill(m_isLogThread.native_handle(), SIGTERM);
843
844 }
845 }
846
847 if(m_isLogThread.joinable()) m_isLogThread.join();
848 return 0;
849}
850
851
852} //namespace app
853} //namespace MagAOX
854
855#endif //xindiserver_hpp
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
The base-class for MagAO-X applications.
Definition MagAOXApp.hpp:73
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.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
std::string m_configDir
The path to configuration files for MagAOX.
Definition MagAOXApp.hpp:85
std::string m_configBase
The name of a base config class for this app (minus .conf).
Definition MagAOXApp.hpp:87
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 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)
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.
Definition paths.hpp:22
#define MAGAOX_driverRelPath
The relative path to the INDI drivers.
Definition paths.hpp:78
int8_t logPrioT
The type of the log priority code.
Definition logDefs.hpp:21
@ 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.
Definition dm.hpp:24
static constexpr logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
static constexpr logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
uint32_t nanosecT
The type used for nanoseconds.
Definition logDefs.hpp:34
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.
Definition text_log.hpp:24
A fixed-width timespec structure.
Definition timespecX.hpp:35
secT time_s
Time since the Unix epoch.
Definition timespecX.hpp:36
#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