Line data Source code
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 :
36 : namespace MagAOX
37 : {
38 : namespace 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
44 : struct sshTunnel
45 : {
46 : std::string m_remoteHost;
47 : int m_localPort {0};
48 : };
49 :
50 : ///The map used to hold tunnel specifications.
51 : typedef 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 : */
58 : inline
59 10 : int 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 10 : std::vector<std::string> sections;
64 :
65 10 : config.unusedSections(sections);
66 :
67 10 : if( sections.size() == 0 )
68 : {
69 0 : return SSHTUNNEL_E_NOTUNNELS;
70 : }
71 :
72 10 : size_t matched = 0;
73 :
74 : //Now see if any sections match a tunnel specification
75 26 : for(size_t i=0; i< sections.size(); ++i)
76 : {
77 : //A tunnel as remoteHost, localPort, and remotePort.
78 48 : if( config.isSetUnused(mx::app::iniFile::makeKey(sections[i], "remoteHost" )) &&
79 96 : config.isSetUnused(mx::app::iniFile::makeKey(sections[i], "localPort" )) &&
80 48 : config.isSetUnused(mx::app::iniFile::makeKey(sections[i], "remotePort" )) )
81 : {
82 :
83 15 : std::string remoteHost;
84 15 : int localPort = 0;
85 15 : bool compress = false;
86 :
87 30 : config.configUnused( remoteHost, mx::app::iniFile::makeKey(sections[i], "remoteHost" ) );
88 45 : config.configUnused( localPort, mx::app::iniFile::makeKey(sections[i], "localPort" ) );
89 15 : config.configUnused( compress, mx::app::iniFile::makeKey(sections[i], "compress" ) );
90 :
91 15 : tmap[sections[i]] = sshTunnel({remoteHost, localPort});
92 :
93 15 : ++matched;
94 15 : }
95 : }
96 :
97 10 : if(matched == 0) return SSHTUNNEL_E_NOTUNNELS;
98 :
99 10 : return 0;
100 10 : }
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 : */
116 : class xindiserver : public MagAOXApp<false>
117 : {
118 :
119 : //Give the test harness access.
120 : friend class xindiserver_test;
121 :
122 : protected:
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 :
150 : public:
151 : /// Default c'tor.
152 : xindiserver();
153 :
154 : /// D'tor, declared and defined for noexcept.
155 27 : ~xindiserver() noexcept
156 27 : {}
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 :
233 : inline
234 81 : xindiserver::xindiserver() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
235 : {
236 : //Use the sshTunnels.conf config file
237 27 : m_configBase = "sshTunnels";
238 :
239 27 : return;
240 0 : }
241 :
242 : inline
243 0 : void xindiserver::setupConfig()
244 : {
245 0 : 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 0 : config.add("indiserver.N", "N", "", argType::True, "indiserver", "N", false, "bool", "indiserver: ignore /tmp/noindi. Capitalized to avoid conflict with --name");
247 0 : config.add("indiserver.p", "p", "", argType::Required, "indiserver", "p", false, "int", "indiserver: alternate IP port, default 7624");
248 0 : config.add("indiserver.v", "v", "", argType::True, "indiserver", "v", false, "int", "indiserver: log verbosity, -v, -vv or -vvv");
249 0 : config.add("indiserver.x", "x", "", argType::True, "indiserver", "x", false, "bool", "exit after last client disconnects -- FOR PROFILING ONLY");
250 :
251 0 : config.add("local.drivers","L", "local.drivers" , argType::Required, "local", "drivers", false, "vector string", "List of local drivers to start.");
252 0 : 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 0 : 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 0 : }
257 :
258 :
259 :
260 : inline
261 0 : void xindiserver::loadConfig()
262 : {
263 : //indiserver config:
264 0 : config(indiserver_m, "indiserver.m");
265 0 : config(indiserver_n, "indiserver.N");
266 0 : config(indiserver_p, "indiserver.p");
267 :
268 0 : indiserver_v = config.verbosity("indiserver.v");
269 :
270 0 : config(indiserver_x, "indiserver.x");
271 :
272 0 : config(m_local, "local.drivers");
273 0 : config(m_remote, "remote.drivers");
274 0 : config(m_remoteServers, "remote.servers");
275 :
276 0 : if(loadSSHTunnelConfigs(m_tunnels, config) < 0)
277 : {
278 0 : m_shutdown = true;
279 0 : return;
280 : }
281 :
282 0 : if( constructIndiserverCommand(m_indiserverCommand) < 0)
283 : {
284 0 : log<software_critical>({__FILE__, __LINE__});
285 0 : m_shutdown = true;
286 0 : return;
287 : }
288 :
289 0 : if( addLocalDrivers(m_indiserverCommand) < 0)
290 : {
291 0 : log<software_critical>({__FILE__, __LINE__});
292 0 : m_shutdown = true;
293 0 : return;
294 : }
295 :
296 0 : if( addRemoteDrivers(m_indiserverCommand) < 0)
297 : {
298 0 : log<software_critical>({__FILE__, __LINE__});
299 0 : m_shutdown = true;
300 0 : return;
301 : }
302 :
303 0 : if( addRemoteServers(m_indiserverCommand) < 0)
304 : {
305 0 : log<software_critical>({__FILE__, __LINE__});
306 0 : m_shutdown = true;
307 0 : return;
308 : }
309 :
310 :
311 : }
312 :
313 : inline
314 10 : int xindiserver::constructIndiserverCommand( std::vector<std::string> & indiserverCommand)
315 : {
316 : try
317 : {
318 10 : indiserverCommand.push_back("indiserver");
319 :
320 10 : if(indiserver_m > 0)
321 : {
322 2 : indiserverCommand.push_back("-m");
323 2 : indiserverCommand.push_back(std::format("{}", indiserver_m));
324 : }
325 :
326 14 : if(indiserver_n == true) indiserverCommand.push_back("-n");
327 :
328 10 : if(indiserver_p > 0)
329 : {
330 2 : indiserverCommand.push_back("-p");
331 2 : indiserverCommand.push_back(std::format("{}", indiserver_p));
332 : }
333 :
334 12 : if(indiserver_v == 1) indiserverCommand.push_back("-v");
335 :
336 14 : if(indiserver_v == 2) indiserverCommand.push_back("-vv");
337 :
338 14 : if(indiserver_v >= 3) indiserverCommand.push_back("-vvv");
339 :
340 14 : if(indiserver_x == true) indiserverCommand.push_back("-x");
341 : }
342 0 : catch(...)
343 : {
344 0 : log<software_critical>({"Exception thrown by std::vector."});
345 0 : return -1;
346 0 : }
347 :
348 10 : return 0;
349 : }
350 :
351 : inline
352 9 : int xindiserver::addLocalDrivers( std::vector<std::string> & driverArgs )
353 : {
354 9 : m_driverPath = MAGAOX_path;
355 9 : m_driverPath += "/";
356 9 : m_driverPath += MAGAOX_driverRelPath;
357 9 : m_driverPath += "/";
358 :
359 21 : for(size_t i=0; i< m_local.size(); ++i)
360 : {
361 17 : size_t bad = m_local[i].find_first_of("@:/", 0);
362 :
363 17 : if(bad != std::string::npos)
364 : {
365 3 : log<software_critical>("Local driver can't have host spec or path(@,:,/): " + m_local[i]);
366 :
367 5 : return XINDISERVER_E_BADDRIVERSPEC;
368 : }
369 :
370 14 : if( m_driverNames.count(m_local[i]) > 0)
371 : {
372 2 : log<software_critical>("Duplicate driver name: " + m_local[i]);
373 2 : return XINDISERVER_E_DUPLICATEDRIVER;
374 : }
375 :
376 12 : m_driverNames.insert(m_local[i]);
377 :
378 12 : std::string dname = m_driverPath + m_local[i];
379 :
380 : try
381 : {
382 12 : driverArgs.push_back(dname);
383 : }
384 0 : catch(...)
385 : {
386 0 : log<software_critical>({__FILE__, __LINE__, "Exception thrown by std::vector"});
387 0 : return XINDISERVER_E_VECTOREXCEPT;
388 0 : }
389 12 : }
390 :
391 4 : return 0;
392 : }
393 :
394 : inline
395 10 : int xindiserver::addRemoteDrivers( std::vector<std::string> & driverArgs )
396 : {
397 25 : for(size_t i=0; i < m_remote.size(); ++i)
398 : {
399 18 : std::string driver;
400 18 : std::string tunnel;
401 :
402 18 : size_t p = m_remote[i].find('@');
403 :
404 18 : if(p == 0 || p == std::string::npos)
405 : {
406 1 : log<software_critical>({__FILE__, __LINE__, "Error parsing remote driver specification: " + m_remote[i] + "\n"});
407 1 : return XINDISERVER_E_BADDRIVERSPEC;
408 : }
409 :
410 17 : driver = m_remote[i].substr(0, p);
411 17 : tunnel = m_remote[i].substr(p+1);
412 :
413 17 : if( m_driverNames.count(driver) > 0)
414 : {
415 1 : log<software_critical>({__FILE__, __LINE__, "Duplicate driver name: " + driver});
416 1 : return XINDISERVER_E_DUPLICATEDRIVER;
417 : }
418 :
419 16 : std::ostringstream oss;
420 :
421 16 : if(m_tunnels.size() == 0)
422 : {
423 0 : log<software_critical>({__FILE__, __LINE__, "No tunnels specified."});
424 0 : return XINDISERVER_E_NOTUNNELS;
425 : }
426 :
427 16 : if(m_tunnels.count(tunnel) != 1)
428 : {
429 1 : log<software_critical>({__FILE__, __LINE__, "Tunnel not found for: " + m_remote[i]});
430 1 : return XINDISERVER_E_TUNNELNOTFOUND;
431 : }
432 :
433 15 : m_driverNames.insert(driver);
434 :
435 15 : oss << driver << "@localhost:" << m_tunnels[tunnel].m_localPort;
436 :
437 : try
438 : {
439 15 : driverArgs.push_back(oss.str());
440 : }
441 0 : catch(...)
442 : {
443 0 : log<software_critical>({__FILE__, __LINE__, "Exception thrown by vector::push_back."});
444 0 : return XINDISERVER_E_VECTOREXCEPT;
445 0 : }
446 22 : }
447 :
448 7 : return 0;
449 :
450 : }
451 :
452 : inline
453 0 : int xindiserver::addRemoteServers( std::vector<std::string> & driverArgs )
454 : {
455 0 : for(size_t j=0; j < m_remoteServers.size(); ++j)
456 : {
457 0 : std::string server;
458 0 : std::string tunnel;
459 :
460 0 : size_t p = m_remoteServers[j].find('@');
461 :
462 0 : if(p == 0 || p == std::string::npos)
463 : {
464 0 : log<software_critical>({__FILE__, __LINE__, "Error parsing remote server specification: " + m_remote[j] + "\n"});
465 0 : return XINDISERVER_E_BADSERVERSPEC;
466 : }
467 :
468 0 : server = m_remoteServers[j].substr(0, p);
469 0 : tunnel = m_remoteServers[j].substr(p+1);
470 :
471 0 : if(m_tunnels.size() == 0)
472 : {
473 0 : log<software_critical>({__FILE__, __LINE__, "No tunnels specified.\n"});
474 0 : return XINDISERVER_E_NOTUNNELS;
475 : }
476 :
477 0 : if(m_tunnels.count(tunnel) != 1)
478 : {
479 0 : log<software_critical>({__FILE__, __LINE__, "Tunnel not found for: " + m_remote[j] + "\n"});
480 0 : return XINDISERVER_E_TUNNELNOTFOUND;
481 : }
482 :
483 : //Now we create a local app configurator, and read the other server's config file
484 0 : mx::app::appConfigurator rsconfig;
485 :
486 0 : rsconfig.add("local.drivers", "", "" , argType::Required, "local", "drivers", false, "", "");
487 :
488 0 : std::string rsconfigPath = m_configDir + "/" + server + ".conf";
489 :
490 0 : rsconfig.readConfig(rsconfigPath);
491 :
492 0 : std::vector<std::string> local;
493 :
494 0 : rsconfig(local, "local.drivers");
495 :
496 0 : for(size_t i=0; i < local.size(); ++i)
497 : {
498 0 : size_t bad = local[i].find_first_of("@:/", 0);
499 :
500 0 : if(bad != std::string::npos)
501 : {
502 0 : log<software_critical>({__FILE__, __LINE__, "Remote server's Local driver can't have host spec or path(@,:,/): " + local[i]});
503 :
504 0 : return XINDISERVER_E_BADDRIVERSPEC;
505 : }
506 :
507 0 : if( m_driverNames.count(local[i]) > 0)
508 : {
509 0 : log<software_critical>({__FILE__, __LINE__, "Duplicate driver name from remote server: " + local[i]});
510 0 : return XINDISERVER_E_DUPLICATEDRIVER;
511 : }
512 :
513 0 : m_driverNames.insert(local[i]);
514 :
515 0 : std::ostringstream oss;
516 :
517 0 : oss << local[i] << "@localhost:" << m_tunnels[tunnel].m_localPort;
518 :
519 : try
520 : {
521 0 : driverArgs.push_back(oss.str());
522 : }
523 0 : catch(...)
524 : {
525 0 : log<software_critical>({__FILE__, __LINE__, "Exception thrown by vector::push_back."});
526 0 : return XINDISERVER_E_VECTOREXCEPT;
527 0 : }
528 0 : }
529 :
530 0 : }
531 :
532 0 : return 0;
533 : }
534 :
535 : inline
536 0 : int xindiserver::forkIndiserver()
537 : {
538 :
539 0 : if(m_log.logLevel() >= logPrio::LOG_INFO)
540 : {
541 0 : std::string coml = "Starting indiserver with command: ";
542 0 : for(size_t i=0;i<m_indiserverCommand.size();++i)
543 : {
544 0 : coml += m_indiserverCommand[i];
545 0 : coml += " ";
546 : }
547 :
548 0 : log<text_log>(coml);
549 0 : std::cerr << coml << std::endl;
550 0 : }
551 :
552 : int filedes[2];
553 0 : if (pipe(filedes) == -1)
554 : {
555 0 : log<software_error>({__FILE__, __LINE__, errno});
556 0 : return -1;
557 : }
558 :
559 :
560 0 : m_isPID = fork();
561 :
562 0 : if(m_isPID < 0)
563 : {
564 0 : log<software_error>({__FILE__, __LINE__, errno, "fork failed"});
565 0 : return -1;
566 : }
567 :
568 :
569 0 : if(m_isPID == 0)
570 : {
571 : //Route STDERR of child to pipe input.
572 0 : while ((dup2(filedes[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
573 0 : close(filedes[1]);
574 0 : close(filedes[0]);
575 :
576 0 : const char ** drivers = new const char*[m_indiserverCommand.size()+1];
577 :
578 0 : for(size_t i=0; i< m_indiserverCommand.size(); ++i)
579 : {
580 0 : drivers[i] = (m_indiserverCommand[i].data());
581 : }
582 0 : drivers[m_indiserverCommand.size()] = NULL;
583 :
584 :
585 0 : execvp("indiserver", (char * const*) drivers);
586 :
587 0 : log<software_error>({__FILE__, __LINE__, errno, "execvp returned"});
588 :
589 0 : delete[] drivers;
590 :
591 0 : return -1;
592 : }
593 :
594 0 : m_isSTDERR = filedes[0];
595 0 : m_isSTDERR_input = filedes[1];
596 :
597 0 : if(m_log.logLevel() <= logPrio::LOG_INFO)
598 : {
599 0 : log<text_log>(std::format("indiserver started with PID {}", m_isPID));
600 : }
601 :
602 0 : return 0;
603 : }
604 :
605 : inline
606 0 : void xindiserver::_isLogThreadStart( xindiserver * l)
607 : {
608 0 : l->isLogThreadExec();
609 0 : }
610 :
611 : inline
612 0 : int xindiserver::isLogThreadStart()
613 : {
614 : try
615 : {
616 0 : m_isLogThread = std::thread( _isLogThreadStart, this);
617 : }
618 0 : catch( const std::exception & e )
619 : {
620 0 : log<software_error>({__FILE__,__LINE__, std::string("Exception on I.S. log thread start: ") + e.what()});
621 0 : return -1;
622 0 : }
623 0 : catch( ... )
624 : {
625 0 : log<software_error>({__FILE__,__LINE__, "Unkown exception on I.S. log thread start"});
626 0 : return -1;
627 0 : }
628 :
629 0 : if(!m_isLogThread.joinable())
630 : {
631 0 : log<software_error>({__FILE__, __LINE__, "I.S. log thread did not start"});
632 0 : return -1;
633 : }
634 :
635 : sched_param sp;
636 0 : sp.sched_priority = m_isLogThreadPrio;
637 :
638 0 : int rv = pthread_setschedparam( m_isLogThread.native_handle(), SCHED_OTHER, &sp);
639 :
640 0 : if(rv != 0)
641 : {
642 0 : log<software_error>({__FILE__, __LINE__, rv, "Error setting thread params."});
643 0 : return -1;
644 : }
645 :
646 0 : return 0;
647 :
648 : }
649 :
650 :
651 :
652 : inline
653 0 : void xindiserver::isLogThreadExec()
654 : {
655 : char buffer[4096];
656 :
657 0 : std::string logs;
658 0 : while(m_shutdown == 0)
659 : {
660 0 : ssize_t count = read(m_isSTDERR, buffer, sizeof(buffer)-1); //Make wure we always have room for \0
661 0 : if (count <= 0 || m_shutdown == 1)
662 : {
663 0 : continue;
664 : }
665 0 : else if(count > (ssize_t) sizeof(buffer)-1)
666 : {
667 0 : log<software_error>({__FILE__, __LINE__, "read returned too many bytes."});
668 0 : continue;
669 : }
670 : else
671 : {
672 0 : buffer[count] = '\0';
673 :
674 0 : logs += buffer;
675 :
676 : //Keep reading until \n found, then process.
677 0 : if(logs.back() == '\n')
678 : {
679 0 : size_t bol = 0;
680 0 : while(bol < logs.size())
681 : {
682 0 : size_t eol = logs.find('\n', bol);
683 0 : if(eol == std::string::npos) break;
684 :
685 0 : processISLog(logs.substr(bol, eol-bol));
686 0 : bol = eol + 1;
687 : }
688 0 : logs = "";
689 : }
690 : }
691 : }
692 :
693 0 : }
694 :
695 : inline
696 0 : int xindiserver::processISLog( std::string logs )
697 : {
698 0 : size_t st = 0;
699 : size_t ed;
700 :
701 0 : ed = logs.find(':', st);
702 0 : if(ed != std::string::npos) ed = logs.find(':', ed+1);
703 0 : if(ed != std::string::npos) ed = logs.find(':', ed+1);
704 :
705 0 : if(ed == std::string::npos)
706 : {
707 : //log<software_error>({__FILE__, __LINE__, "Did not find timestamp : in log entry"});
708 0 : log<text_log>(logs, logPrio::LOG_INFO);
709 0 : return 0;
710 : }
711 :
712 0 : std::string ts = logs.substr(st, ed-st);
713 :
714 : double dsec;
715 :
716 : tm bdt;
717 0 : mx::sys::ISO8601dateBreakdown(bdt.tm_year, bdt.tm_mon, bdt.tm_mday, bdt.tm_hour, bdt.tm_min, dsec, ts);
718 :
719 0 : bdt.tm_year -= 1900;
720 0 : bdt.tm_mon -= 1;
721 0 : bdt.tm_sec = (int) dsec;
722 0 : bdt.tm_isdst = 0;
723 0 : bdt.tm_gmtoff = 0;
724 :
725 0 : timespecX tsp;
726 :
727 0 : tsp.time_s = timegm(&bdt);
728 0 : tsp.time_ns = (nanosecT) ((dsec-bdt.tm_sec)*1e9 + 0.5);
729 :
730 0 : ++ed;
731 0 : st = logs.find_first_not_of(" ", ed);
732 :
733 0 : if(st == std::string::npos) st = ed;
734 0 : if(st == logs.size())
735 : {
736 0 : log<software_error>({__FILE__, __LINE__, "Did not find log entry."});
737 0 : return -1;
738 : }
739 :
740 0 : std::string logstr = logs.substr(st, logs.size()-st);
741 :
742 0 : logPrioT prio = logPrio::LOG_INFO;
743 :
744 : //Look for fatal errors
745 0 : if(logstr.find("xindidriver") != std::string::npos) //Errors from xindidriver
746 : {
747 0 : if(logstr.find("failed to lock") != std::string::npos)
748 : {
749 0 : prio = logPrio::LOG_CRITICAL;
750 : }
751 : }
752 0 : else if(logstr.find("bind: Address already in use") != std::string::npos) //Errors from indiserver
753 : {
754 0 : prio = logPrio::LOG_CRITICAL;
755 : }
756 :
757 0 : m_log.log<text_log>(tsp, "IS: " + logstr, prio);
758 :
759 0 : if(prio == logPrio::LOG_CRITICAL)
760 : {
761 0 : state(stateCodes::FAILURE);
762 0 : m_shutdown = true;
763 : }
764 :
765 0 : return 0;
766 0 : }
767 :
768 : inline
769 0 : int xindiserver::appStartup()
770 : {
771 :
772 : //--------------------
773 : //Make symlinks
774 : //--------------------
775 0 : std::string path1 = "/opt/MagAOX/bin/xindidriver";
776 0 : for(size_t i=0; i<m_local.size(); ++i)
777 : {
778 0 : elevatedPrivileges elPriv(this);
779 :
780 0 : std::cerr << "creating symlink " << path1 << " " << m_driverPath + m_local[i] << "\n";
781 :
782 0 : int rv = symlink(path1.c_str(), (m_driverPath + m_local[i]).c_str());
783 :
784 0 : if(rv < 0 && errno != EEXIST)
785 : {
786 0 : log<software_error>({__FILE__, __LINE__, errno});
787 0 : log<software_error>({__FILE__, __LINE__, "Failed to create symlink for driver: " + m_local[i] + ". Continuing."});
788 : }
789 0 : }
790 :
791 0 : m_local.clear();
792 0 : m_remote.clear();
793 0 : m_tunnels.clear();
794 :
795 : //--------------------
796 : //Now start indiserver
797 : //--------------------
798 0 : if(forkIndiserver() < 0)
799 : {
800 0 : log<software_critical>({__FILE__, __LINE__});
801 0 : return -1;
802 : }
803 :
804 0 : if(isLogThreadStart() < 0)
805 : {
806 0 : log<software_critical>({__FILE__, __LINE__});
807 0 : return -1;
808 : }
809 :
810 0 : return 0;
811 0 : }
812 :
813 : inline
814 0 : int xindiserver::appLogic()
815 : {
816 : int status;
817 0 : pid_t result = waitpid(m_isPID, &status, WNOHANG);
818 0 : if (result == 0)
819 : {
820 0 : state(stateCodes::CONNECTED);
821 : }
822 : else
823 : {
824 : //We don't care why. If indiserver is not alive while in this function then it's a fatal error.
825 0 : log<text_log>("indiserver has exited", logPrio::LOG_CRITICAL);
826 0 : state(stateCodes::FAILURE);
827 0 : return -1;
828 : }
829 :
830 :
831 :
832 0 : return 0;
833 : }
834 :
835 : inline
836 0 : int xindiserver::appShutdown()
837 : {
838 :
839 0 : if(m_isPID > 0)
840 : {
841 0 : kill(m_isPID, SIGTERM);
842 : }
843 :
844 0 : if(m_isSTDERR_input >= 0)
845 : {
846 0 : char w = '\0';
847 0 : ssize_t nwr = write(m_isSTDERR_input, &w, 1);
848 0 : if(nwr != 1)
849 : {
850 0 : log<software_error>({__FILE__, __LINE__, errno });
851 0 : log<software_error>({__FILE__, __LINE__, "Error on write to i.s. log thread. Sending SIGTERM."});
852 0 : pthread_kill(m_isLogThread.native_handle(), SIGTERM);
853 :
854 : }
855 : }
856 :
857 0 : if(m_isLogThread.joinable()) m_isLogThread.join();
858 0 : return 0;
859 : }
860 :
861 :
862 : } //namespace app
863 : } //namespace MagAOX
864 :
865 : #endif //xindiserver_hpp
866 :
867 :
868 :
869 :
870 :
871 :
872 :
873 :
874 :
875 :
876 :
877 :
878 :
879 :
880 :
881 :
882 :
883 :
884 :
885 :
886 :
887 :
888 :
889 :
890 :
891 :
892 :
|