LCOV - code coverage report
Current view: top level - apps/sshDigger - sshDigger.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 23.6 % 263 62
Test Date: 2026-01-03 21:03:39 Functions: 25.0 % 20 5

            Line data    Source code
       1              : /** \file sshDigger.hpp
       2              :  * \brief The MagAO-X SSH tunnel manager
       3              :  *
       4              :  * \ingroup sshDigger_files
       5              :  */
       6              : 
       7              : #ifndef sshDigger_hpp
       8              : #define sshDigger_hpp
       9              : 
      10              : #include <sys/wait.h>
      11              : 
      12              : #include <iostream>
      13              : 
      14              : #include <mx/sys/timeUtils.hpp>
      15              : 
      16              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      17              : #include "../../magaox_git_version.h"
      18              : 
      19              : /** \defgroup sshDigger SSH Tunnel Manager
      20              :  * \brief Manage the SSH tunnels for MagAO-X communications..
      21              :  *
      22              :  * <a href="../handbook/operating/software/apps/sshDigger.html">Application Documentation</a>
      23              :  *
      24              :  * \ingroup apps
      25              :  *
      26              :  */
      27              : 
      28              : /** \defgroup sshDigger_files SSH Tunnel Files
      29              :  * \ingroup sshDigger
      30              :  */
      31              : 
      32              : // Return codes, these are primarily for testing purposes
      33              : #define SSHDIGGER_E_NOTUNNELS ( -10 )
      34              : #define SSHDIGGER_E_NOTUNNELFOUND ( -11 )
      35              : #define SSHDIGGER_E_NOHOSTNAME ( -12 )
      36              : #define SSHDIGGER_E_NOLOCALPORT ( -13 )
      37              : #define SSHDIGGER_E_NOREMOTEPORT ( -14 )
      38              : 
      39              : namespace MagAOX
      40              : {
      41              : namespace app
      42              : {
      43              : 
      44              : /// The MagAO-X SSH tunnel manager
      45              : /** Each instance of this app manages one SSH tunnel to another computer.
      46              :  * These tunnels are opened via the `autossh` app, which itself calls `ssh`.
      47              :  *
      48              :  *
      49              :  * \todo add options for verboseness of ssh and autossh (loglevel)
      50              :  * \todo add options for ssh and autossh log thread priorities
      51              :  *
      52              :  * \ingroup sshDigger
      53              :  */
      54              : class sshDigger : public MagAOXApp<false>
      55              : {
      56              : 
      57              :     // Give the test harness access.
      58              :     friend class sshDigger_test;
      59              : 
      60              :   protected:
      61              :     /** \name Configurable Parameters
      62              :      *@{
      63              :      */
      64              :     std::string m_remoteHost;        ///< The name of the remote host
      65              :     int         m_localPort{ 0 };    ///< The local port to forward from
      66              :     int         m_remotePort{ 0 };   ///< The remote port to forward to
      67              :     int         m_monitorPort{ 0 };  ///< The monitor port
      68              :     bool        m_compress{ false }; ///< Control compression on this tunnel.  True is on, false is off.
      69              :     ///@}
      70              : 
      71              :     int m_tunnelPID{ 0 }; ///< The PID of the autossh process
      72              : 
      73              :     /** \name ssh log capture
      74              :      * @{
      75              :      */
      76              :     int m_sshSTDERR{ -1 }; ///< The output of stderr of the ssh process
      77              : 
      78              :     int m_sshSTDERR_input{ -1 }; ///< The input end of stderr, used to wake up the log thread on shutdown.
      79              : 
      80              :     int m_sshLogThreadPrio{ 0 }; ///< Priority of the ssh log capture thread, should normally be 0.
      81              : 
      82              :     std::thread m_sshLogThread; ///< A separate thread for capturing ssh logs
      83              : 
      84              :     std::string m_lastSSHLogs; ///< Used to prevent re-logging
      85              : 
      86              :     int m_sshError{ 0 }; ///< Flag to signal when ssh logs an error, and should be restarted via SIGUSR1 to autossh.
      87              : 
      88              :     ///@}
      89              : 
      90              :     /** \name autossh log capture
      91              :      * @{
      92              :      */
      93              :     std::string m_autosshLogFile;    ///< Name of the autossh logfile.
      94              :     int         m_autosshLogFD{ 0 }; ///< File descriptor of the autossh logfile.
      95              : 
      96              :     int m_autosshLogThreadPrio{ 0 }; ///< Priority of the autossh log capture thread, should normally be 0.
      97              : 
      98              :     std::thread m_autosshLogThread; ///< A separate thread for capturing autossh logs
      99              : 
     100              :     ///@}
     101              : 
     102              :   public:
     103              :     /// Default c'tor.
     104              :     sshDigger();
     105              : 
     106              :     /// D'tor, declared and defined for noexcept.
     107           17 :     ~sshDigger() noexcept
     108           17 :     {
     109           17 :     }
     110              : 
     111              :     virtual void setupConfig();
     112              : 
     113              :     /// Implementation of loadConfig logic, separated for testing.
     114              :     /** This is called by loadConfig().
     115              :      */
     116              :     int loadConfigImpl(
     117              :         mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
     118              : 
     119              :     virtual void loadConfig();
     120              : 
     121              :     /// Create the tunnel specification string, [localPort]:localhost:[remotePort].
     122              :     /**
     123              :      * \returns a string containing the tunnel specification.
     124              :      */
     125              :     std::string tunnelSpec();
     126              : 
     127              :     /// Generate the argv vector for the exec of autossh.
     128              :     void genArgsV( std::vector<std::string> &argsV /**< [out] will contain the argv vector for an autssh exec call */ );
     129              : 
     130              :     /// Generate the envp vector for the exec of autossh.
     131              :     void genEnvp( std::vector<std::string> &envp /**< [out] will contain the envp vector for an autssh exec call */ );
     132              : 
     133              :     ///  Creates the tunnel in a child process using exec.
     134              :     int execTunnel();
     135              : 
     136              :     /// Thread starter, called by sshLogThreadStart on thread construction.  Calls sshLogThreadExec.
     137              :     static void _sshLogThreadStart( sshDigger *s /**< [in] a pointer to an sshDigger instance (normally this) */ );
     138              : 
     139              :     /// Start the log capture.
     140              :     int sshLogThreadStart();
     141              : 
     142              :     /// Execute the log capture.
     143              :     void sshLogThreadExec();
     144              : 
     145              :     /// Process a log entry from indiserver, putting it into MagAO-X standard form
     146              :     int processSSHLog( const std::string &logs );
     147              : 
     148              :     /// Thread starter, called by sshLogThreadStart on thread construction.  Calls sshLogThreadExec.
     149              :     static void _autosshLogThreadStart( sshDigger *s /**< [in] a pointer to an sshDigger instance (normally this) */ );
     150              : 
     151              :     /// Start the log capture.
     152              :     int autosshLogThreadStart();
     153              : 
     154              :     /// Execute the log capture.
     155              :     void autosshLogThreadExec();
     156              : 
     157              :     /// Process a log entry from indiserver, putting it into MagAO-X standard form
     158              :     int processAutoSSHLog( const std::string &logs );
     159              : 
     160              :     /// Startup function
     161              :     /**
     162              :      *
     163              :      */
     164              :     virtual int appStartup();
     165              : 
     166              :     /// Implementation of the FSM for sshDigger.
     167              :     /** Monitors status of m_sshError flag, and sends a signal to autossh if an error is indicated.
     168              :      *
     169              :      * Checks that autossh is still alive, and if it has died restarts it.
     170              :      *
     171              :      * \returns 0 on no critical error
     172              :      * \returns -1 on an error requiring shutdown
     173              :      */
     174              :     virtual int appLogic();
     175              : 
     176              :     /// Shutdown the app.
     177              :     /** Sends SIGTERM to autossh.
     178              :      *
     179              :      * Tells the two logging threads to exit, and waits for them to complete.
     180              :      *
     181              :      */
     182              :     virtual int appShutdown();
     183              : };
     184              : 
     185           51 : sshDigger::sshDigger() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
     186              : {
     187              :     // Use the sshTunnels.conf config file
     188           17 :     m_configBase = "sshTunnels";
     189              : 
     190              :     // set mx::app::application flag to not report lack of config file for this app.
     191           17 :     m_requireConfigPathLocal = false;
     192              : 
     193           17 :     return;
     194            0 : }
     195              : 
     196            0 : void sshDigger::setupConfig()
     197              : {
     198            0 : }
     199              : 
     200           24 : int sshDigger::loadConfigImpl( mx::app::appConfigurator &_config )
     201              : {
     202              : 
     203           24 :     std::vector<std::string> sections;
     204              : 
     205           24 :     _config.unusedSections( sections );
     206              : 
     207           24 :     if( sections.size() == 0 )
     208              :     {
     209            1 :         log<text_log>( "No tunnels found in config.", logPrio::LOG_CRITICAL );
     210              : 
     211            1 :         return SSHDIGGER_E_NOTUNNELS;
     212              :     }
     213              : 
     214              :     // Now see if any sections match our m_configName
     215              : 
     216           23 :     bool found = false;
     217           68 :     for( size_t i = 0; i < sections.size(); ++i )
     218              :     {
     219           45 :         if( sections[i] == m_configName )
     220              :         {
     221           21 :             found = true;
     222              :         }
     223              : 
     224              :         // Here we go through and access each unused config just to avoid the critical error for unrecognized configs.
     225           45 :         if( sections[i] == "" )
     226            7 :             continue; // this is an error
     227              : 
     228           38 :         std::string rh;
     229           38 :         _config.configUnused( rh, mx::app::iniFile::makeKey( sections[i], "remoteHost" ) );
     230           38 :         if( rh == "" )
     231              :         {
     232            5 :             log<text_log>( "Config section [" + sections[i] +
     233              :                                "] may be an invalid tunnel configuration (no remoteHost).",
     234              :                            logPrio::LOG_WARNING );
     235              :         }
     236           38 :         int lp = 0;
     237           38 :         _config.configUnused( lp, mx::app::iniFile::makeKey( sections[i], "localPort" ) );
     238           38 :         if( lp == 0 )
     239              :         {
     240            5 :             log<text_log>( "Config section [" + sections[i] +
     241              :                                "] may be an invalid tunnel configuration (no localPort).",
     242              :                            logPrio::LOG_WARNING );
     243              :         }
     244           38 :         int rp = 0;
     245           38 :         _config.configUnused( rp, mx::app::iniFile::makeKey( sections[i], "remotePort" ) );
     246           38 :         if( rp == 0 )
     247              :         {
     248            5 :             log<text_log>( "Config section [" + sections[i] +
     249              :                                "] may be an invalid tunnel configuration (no remotePort).",
     250              :                            logPrio::LOG_WARNING );
     251              :         }
     252           38 :         int mp = 0;
     253           38 :         _config.configUnused( mp, mx::app::iniFile::makeKey( sections[i], "monitorPort" ) );
     254           38 :         bool cmp = false;
     255           38 :         _config.configUnused( cmp, mx::app::iniFile::makeKey( sections[i], "compress" ) );
     256           38 :     }
     257              : 
     258           23 :     if( found == false )
     259              :     {
     260            2 :         log<text_log>( "No matching tunnel configuration found.", logPrio::LOG_CRITICAL );
     261            2 :         return SSHDIGGER_E_NOTUNNELFOUND;
     262              :     }
     263              : 
     264              :     // Now we configure the tunnel.
     265              : 
     266           21 :     _config.configUnused( m_remoteHost, mx::app::iniFile::makeKey( m_configName, "remoteHost" ) );
     267           21 :     if( m_remoteHost == "" )
     268              :     {
     269            3 :         log<text_log>( "No remote host specified.", logPrio::LOG_CRITICAL );
     270            3 :         return SSHDIGGER_E_NOHOSTNAME;
     271              :     }
     272              : 
     273           18 :     _config.configUnused( m_localPort, mx::app::iniFile::makeKey( m_configName, "localPort" ) );
     274           18 :     if( m_localPort == 0 )
     275              :     {
     276            3 :         log<text_log>( "No local port specified.", logPrio::LOG_CRITICAL );
     277              : 
     278            3 :         return SSHDIGGER_E_NOLOCALPORT;
     279              :     }
     280              : 
     281           15 :     _config.configUnused( m_remotePort, mx::app::iniFile::makeKey( m_configName, "remotePort" ) );
     282           15 :     if( m_remotePort == 0 )
     283              :     {
     284            3 :         log<text_log>( "No remote port specified.", logPrio::LOG_CRITICAL );
     285              : 
     286            3 :         return SSHDIGGER_E_NOREMOTEPORT;
     287              :     }
     288              : 
     289           24 :     _config.configUnused( m_monitorPort, mx::app::iniFile::makeKey( m_configName, "monitorPort" ) );
     290              : 
     291           12 :     _config.configUnused( m_compress, mx::app::iniFile::makeKey( m_configName, "compress" ) );
     292              : 
     293           12 :     return 0;
     294           24 : }
     295              : 
     296            0 : void sshDigger::loadConfig()
     297              : {
     298            0 :     if( loadConfigImpl( config ) < 0 )
     299              :     {
     300            0 :         m_shutdown = true;
     301              : 
     302            0 :         log<text_log>( "configuration error(s) have occurred", logPrio::LOG_CRITICAL );
     303              :     };
     304            0 : }
     305              : 
     306            2 : std::string sshDigger::tunnelSpec()
     307              : {
     308            2 :     return std::format( "{}:localhost:{}", m_localPort, m_remotePort );
     309              : }
     310              : 
     311            1 : void sshDigger::genArgsV( std::vector<std::string> &argsV )
     312              : {
     313            1 :     std::string comp = "";
     314            1 :     if( m_compress )
     315              :     {
     316            0 :         comp = "-C";
     317              :     }
     318              : 
     319            7 :     argsV = { "autossh", "-M" + std::to_string( m_monitorPort ), comp, "-nNTL", tunnelSpec(), m_remoteHost };
     320            3 : }
     321              : 
     322            0 : void sshDigger::genEnvp( std::vector<std::string> &envp )
     323              : {
     324            0 :     std::string logenv = "AUTOSSH_LOGFILE=" + m_autosshLogFile;
     325              : 
     326            0 :     envp = { logenv };
     327            0 : }
     328              : 
     329            0 : int sshDigger::execTunnel()
     330              : {
     331            0 :     std::vector<std::string> argsV;
     332            0 :     genArgsV( argsV );
     333              : 
     334            0 :     std::vector<std::string> envps;
     335            0 :     genEnvp( envps );
     336              : 
     337            0 :     if( m_log.logLevel() <= logPrio::LOG_INFO )
     338              :     {
     339            0 :         std::string coml = "Starting autossh with command: ";
     340            0 :         for( size_t i = 0; i < argsV.size(); ++i )
     341              :         {
     342            0 :             coml += argsV[i];
     343            0 :             coml += " ";
     344              :         }
     345            0 :         log<text_log>( coml );
     346            0 :     }
     347              : 
     348              :     int filedes[2];
     349            0 :     if( pipe( filedes ) == -1 )
     350              :     {
     351            0 :         return log<software_error, -1>( { errno, "pipe failed" } );
     352              :     }
     353              : 
     354            0 :     m_tunnelPID = fork();
     355              : 
     356            0 :     if( m_tunnelPID < 0 )
     357              :     {
     358            0 :         return log<software_error, -1>( { errno, "fork failed" } );
     359              :     }
     360              : 
     361            0 :     if( m_tunnelPID == 0 )
     362              :     {
     363              :         // Route STDERR of child to pipe input.
     364            0 :         while( ( dup2( filedes[1], STDERR_FILENO ) == -1 ) && ( errno == EINTR ) )
     365              :         {
     366              :         }
     367            0 :         close( filedes[1] );
     368            0 :         close( filedes[0] );
     369              : 
     370            0 :         const char **args = new const char *[argsV.size() + 1];
     371            0 :         for( size_t i = 0; i < argsV.size(); ++i )
     372            0 :             args[i] = argsV[i].data();
     373            0 :         args[argsV.size()] = NULL;
     374              : 
     375            0 :         const char **envp = new const char *[envps.size() + 1];
     376            0 :         for( size_t i = 0; i < envps.size(); ++i )
     377            0 :             envp[i] = envps[i].data();
     378            0 :         envp[envps.size()] = NULL;
     379              : 
     380            0 :         execvpe( "autossh", (char *const *)args, (char *const *)envp );
     381              : 
     382            0 :         std::cerr << "returned\n";
     383            0 :         log<software_error>( { errno, std::string( "execvp returned: " ) + strerror( errno ) } );
     384              : 
     385            0 :         delete[] args;
     386              : 
     387            0 :         return -1;
     388              :     }
     389              : 
     390            0 :     m_sshSTDERR       = filedes[0];
     391            0 :     m_sshSTDERR_input = filedes[1];
     392              : 
     393            0 :     if( m_log.logLevel() <= logPrio::LOG_INFO )
     394              :     {
     395            0 :         log<text_log>( std::format( "autossh tunnel started with PID {}", m_tunnelPID ) );
     396              :     }
     397            0 :     return 0;
     398            0 : }
     399              : 
     400            0 : inline void sshDigger::_sshLogThreadStart( sshDigger *s )
     401              : {
     402            0 :     s->sshLogThreadExec();
     403            0 : }
     404              : 
     405            0 : inline int sshDigger::sshLogThreadStart()
     406              : {
     407              :     try
     408              :     {
     409            0 :         m_sshLogThread = std::thread( _sshLogThreadStart, this );
     410              :     }
     411            0 :     catch( const std::exception &e )
     412              :     {
     413            0 :         return log<software_error, -1>( { std::string( "Exception on ssh log thread start: " ) + e.what() } );
     414            0 :     }
     415            0 :     catch( ... )
     416              :     {
     417            0 :         return log<software_error, -1>( { "Unkown exception on ssh log thread start" } );
     418            0 :     }
     419              : 
     420            0 :     if( !m_sshLogThread.joinable() )
     421              :     {
     422            0 :         return log<software_error, -1>( { "ssh log thread did not start" } );
     423              :     }
     424              : 
     425              :     sched_param sp;
     426            0 :     sp.sched_priority = m_sshLogThreadPrio;
     427              : 
     428            0 :     int rv = pthread_setschedparam( m_sshLogThread.native_handle(), SCHED_OTHER, &sp );
     429              : 
     430            0 :     if( rv != 0 )
     431              :     {
     432            0 :         return log<software_error, -1>( { rv, "Error setting thread params." } );
     433              :     }
     434              : 
     435            0 :     return 0;
     436              : }
     437              : 
     438            0 : inline void sshDigger::sshLogThreadExec()
     439              : {
     440              :     char buffer[4096];
     441              : 
     442            0 :     std::string logs;
     443            0 :     while( m_shutdown == 0 && m_sshSTDERR > 0 )
     444              :     {
     445            0 :         ssize_t count = read( m_sshSTDERR, buffer, sizeof( buffer ) - 1 );
     446            0 :         if( count <= 0 )
     447              :         {
     448            0 :             continue;
     449              :         }
     450              :         else
     451              :         {
     452            0 :             buffer[count] = '\0';
     453              : 
     454            0 :             logs += buffer;
     455              : 
     456              :             // Keep reading until \n found, then process.
     457            0 :             if( logs.back() == '\n' )
     458              :             {
     459            0 :                 size_t bol = 0;
     460            0 :                 while( bol < logs.size() )
     461              :                 {
     462            0 :                     size_t eol = logs.find( '\n', bol );
     463            0 :                     if( eol == std::string::npos )
     464              :                     {
     465            0 :                         break;
     466              :                     }
     467              : 
     468            0 :                     processSSHLog( logs.substr( bol, eol - bol ) );
     469            0 :                     bol = eol + 1;
     470              :                 }
     471            0 :                 logs = "";
     472              :             }
     473              :         }
     474              :     }
     475            0 : }
     476              : 
     477            0 : inline int sshDigger::processSSHLog( const std::string &logs )
     478              : {
     479            0 :     logPrioT lp = logPrio::LOG_INFO;
     480              : 
     481            0 :     if( logs.find( "bind: Address already in use" ) != std::string::npos )
     482              :     {
     483            0 :         lp         = logPrio::LOG_ERROR;
     484            0 :         m_sshError = 1; // Means we'll need a restart
     485              :     }
     486              : 
     487            0 :     if( logs.find( "channel_setup_fwd_listener_tcpip: cannot listen to port:" ) != std::string::npos )
     488              :     {
     489            0 :         lp         = logPrio::LOG_ERROR;
     490            0 :         m_sshError = 1; // Means we'll need a restart
     491              :     }
     492              : 
     493            0 :     if( logs.find( "connect failed:" ) != std::string::npos )
     494              :     {
     495            0 :         lp         = logPrio::LOG_ERROR;
     496            0 :         m_sshError = 2;
     497              :     }
     498              : 
     499            0 :     if( logs != m_lastSSHLogs )
     500              :     {
     501            0 :         m_log.log<text_log>( { "SSH: " + logs }, lp );
     502            0 :         m_lastSSHLogs = logs;
     503              :     }
     504              : 
     505            0 :     return 0;
     506              : }
     507              : 
     508            0 : inline void sshDigger::_autosshLogThreadStart( sshDigger *s )
     509              : {
     510            0 :     s->autosshLogThreadExec();
     511            0 : }
     512              : 
     513            0 : inline int sshDigger::autosshLogThreadStart()
     514              : {
     515              :     try
     516              :     {
     517            0 :         m_autosshLogThread = std::thread( _autosshLogThreadStart, this );
     518              :     }
     519              : 
     520            0 :     catch( const std::exception &e )
     521              :     {
     522            0 :         return log<software_error, -1>( { std::string( "Exception on autossh log thread start: " ) + e.what() } );
     523            0 :     }
     524            0 :     catch( ... )
     525              :     {
     526            0 :         return log<software_error, -1>( { "Unkown exception on autossh log thread start" } );
     527            0 :     }
     528              : 
     529            0 :     if( !m_autosshLogThread.joinable() )
     530              :     {
     531            0 :         return log<software_error, -1>( { "autossh log thread did not start" } );
     532              :     }
     533              : 
     534              :     sched_param sp;
     535            0 :     sp.sched_priority = m_autosshLogThreadPrio;
     536              : 
     537            0 :     int rv = pthread_setschedparam( m_autosshLogThread.native_handle(), SCHED_OTHER, &sp );
     538              : 
     539            0 :     if( rv != 0 )
     540              :     {
     541            0 :         return log<software_error, -1>( { rv, "Error setting thread params." } );
     542              :     }
     543              : 
     544            0 :     return 0;
     545              : }
     546              : 
     547            0 : inline void sshDigger::autosshLogThreadExec()
     548              : {
     549              :     char buffer[4096];
     550              : 
     551              :     // Open the FIFO
     552            0 :     m_autosshLogFD = 0;
     553              : 
     554              :     // We currently aren't monitoring this thread status, so we might as well retry if there is an error
     555            0 :     while( m_autosshLogFD <= 0 )
     556              :     {
     557            0 :         m_autosshLogFD = open( m_autosshLogFile.c_str(), O_RDONLY );
     558              : 
     559            0 :         if( m_autosshLogFD < 0 )
     560              :         {
     561            0 :             log<software_error>( { errno, "unable to open auto ssh log fifo" } );
     562            0 :             mx::sys::sleep( 1 );
     563              :         }
     564              :     }
     565              : 
     566            0 :     std::string logs;
     567            0 :     while( m_shutdown == 0 )
     568              :     {
     569            0 :         ssize_t count = read( m_autosshLogFD, buffer, sizeof( buffer ) - 1 );
     570            0 :         if( count <= 0 )
     571              :         {
     572            0 :             continue;
     573              :         }
     574              :         else
     575              :         {
     576            0 :             buffer[count] = '\0';
     577              : 
     578            0 :             logs += buffer;
     579              : 
     580              :             // Keep reading until \n found, then process.
     581            0 :             if( logs.back() == '\n' )
     582              :             {
     583            0 :                 size_t bol = 0;
     584            0 :                 while( bol < logs.size() )
     585              :                 {
     586            0 :                     size_t eol = logs.find( '\n', bol );
     587            0 :                     if( eol == std::string::npos )
     588              :                     {
     589            0 :                         break;
     590              :                     }
     591              : 
     592            0 :                     processAutoSSHLog( logs.substr( bol, eol - bol ) );
     593            0 :                     bol = eol + 1;
     594              :                 }
     595            0 :                 logs = "";
     596              :             }
     597              :         }
     598              :     }
     599            0 : }
     600              : 
     601            0 : inline int sshDigger::processAutoSSHLog( const std::string &logs )
     602              : {
     603              :     ///\todo interpret logs, giving errors vs info vs debug, strip timestamps, etc.
     604              : 
     605            0 :     m_log.log<text_log>( { "AUTOSSH: " + logs } );
     606              : 
     607            0 :     return 0;
     608              : }
     609              : 
     610            0 : int sshDigger::appStartup()
     611              : {
     612            0 :     m_autosshLogFile = "/dev/shm/sshDigger_autossh_" + m_configName + "_" + std::to_string( m_pid );
     613              : 
     614            0 :     if( mkfifo( m_autosshLogFile.c_str(), S_IRUSR | S_IWUSR ) < 0 )
     615              :     {
     616            0 :         return log<software_critical, -1>( { errno, "unable to create autossh log fifo" } );
     617              :     }
     618              : 
     619            0 :     if( execTunnel() < 0 )
     620              :     {
     621            0 :         return log<software_critical, -1>( std::source_location::current() );
     622              :     }
     623              : 
     624            0 :     if( autosshLogThreadStart() < 0 )
     625              :     {
     626            0 :         return log<software_critical, -1>( std::source_location::current() );
     627              :     }
     628              : 
     629            0 :     if( sshLogThreadStart() < 0 )
     630              :     {
     631            0 :         return log<software_critical, -1>( std::source_location::current() );
     632              :     }
     633              : 
     634            0 :     return 0;
     635              : }
     636              : 
     637            0 : int sshDigger::appLogic()
     638              : {
     639            0 :     if( !m_sshError )
     640              :     {
     641            0 :         m_lastSSHLogs = "";
     642            0 :         state( stateCodes::CONNECTED );
     643              :     }
     644              : 
     645            0 :     if( m_sshError == 1 )
     646              :     {
     647            0 :         log<text_log>( "sending SIGUSR1 to restart ssh" );
     648            0 :         kill( m_tunnelPID, SIGUSR1 );
     649            0 :         state( stateCodes::NOTCONNECTED );
     650            0 :         m_sshError = 0;
     651              :     }
     652              : 
     653            0 :     if( m_sshError == 2 )
     654              :     {
     655            0 :         state( stateCodes::NOTCONNECTED );
     656            0 :         m_sshError = 0;
     657              :     }
     658              : 
     659              :     // Check if autossh has died for any reason
     660              :     int   status;
     661            0 :     pid_t exited = waitpid( m_tunnelPID, &status, WNOHANG );
     662              : 
     663            0 :     if( exited == m_tunnelPID )
     664              :     {
     665            0 :         log<text_log>( "autossh exited, restarting." );
     666            0 :         m_sshSTDERR = -1; // This tells the sshLogThread to exit
     667            0 :         char    w   = '\0';
     668            0 :         ssize_t nwr = write( m_sshSTDERR_input, &w, 1 ); // And this wakes it up from the blocking read
     669              : 
     670            0 :         if( m_sshLogThread.joinable() )
     671              :         {
     672            0 :             m_sshLogThread.join();
     673              :         }
     674              : 
     675            0 :         if( nwr != 1 )
     676              :         {
     677            0 :             return log<software_error, -1>( { errno, "Error on write to ssh log thread. restart failed." } );
     678              :         }
     679              : 
     680              :         // And now we can restart autossh
     681            0 :         if( execTunnel() < 0 )
     682              :         {
     683            0 :             return log<software_critical, -1>( { "restart of tunnel failed." } );
     684              :         }
     685              : 
     686              :         // And the ssh log thread.
     687            0 :         if( sshLogThreadStart() < 0 )
     688              :         {
     689            0 :             return log<software_critical, -1>( { "restart of ssh log thread failed." } );
     690              :         }
     691              : 
     692              :         // Don't need to restart the autossh log thread, becuase we'll give it the same file as log file.
     693              :     }
     694              : 
     695            0 :     return 0;
     696              : }
     697              : 
     698            0 : int sshDigger::appShutdown()
     699              : {
     700            0 :     kill( m_tunnelPID, SIGTERM );
     701            0 :     waitpid( m_tunnelPID, 0, 0 );
     702              : 
     703              :     // Write the the ssh stderr to wake up the ssh log thread, which will then see shutdown is set.
     704            0 :     char    w   = '\0';
     705            0 :     ssize_t nwr = write( m_sshSTDERR_input, &w, 1 );
     706            0 :     if( nwr != 1 )
     707              :     {
     708            0 :         log<software_error>( { errno, "Error on write to ssh log thread. Sending SIGTERM." } );
     709            0 :         pthread_kill( m_sshLogThread.native_handle(), SIGTERM );
     710              :     }
     711              : 
     712            0 :     if( m_sshLogThread.joinable() )
     713              :     {
     714            0 :         m_sshLogThread.join();
     715              :     }
     716              : 
     717              :     // Close the autossh FD to get that thread to shutdown.
     718            0 :     close( m_autosshLogFD );
     719            0 :     if( m_autosshLogThread.joinable() )
     720              :     {
     721            0 :         m_autosshLogThread.join();
     722              :     }
     723              : 
     724            0 :     return 0;
     725              : }
     726              : 
     727              : } // namespace app
     728              : } // namespace MagAOX
     729              : 
     730              : #endif // sshDigger_hpp
        

Generated by: LCOV version 2.0-1