LCOV - code coverage report
Current view: top level - utils/logsurgeon - logsurgeon.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 16.9 % 136 23
Test Date: 2026-01-03 21:03:39 Functions: 100.0 % 5 5

            Line data    Source code
       1              : /** \file logsurgeon.hpp
       2              :  * \brief A utility to fix corrupted MagAO-X binary logs.
       3              :  *
       4              :  * \ingroup files
       5              :  */
       6              : 
       7              : #ifndef hpp
       8              : #define hpp
       9              : 
      10              : #include <iostream>
      11              : #include <cstring>
      12              : 
      13              : #include <mx/ioutils/fileUtils.hpp>
      14              : 
      15              : #include "../../libMagAOX/libMagAOX.hpp"
      16              : using namespace MagAOX::logger;
      17              : 
      18              : using namespace flatlogs;
      19              : 
      20              : /** \defgroup logsurgeon logsurgeon: MagAO-X Log Corrector
      21              :  * \brief Read a MagAO-X binary log file and remove corrupted bytes.
      22              :  *
      23              :  * <a href="../handbook/utils/logsurgeon.html">Utility Documentation</a>
      24              :  *
      25              :  * \ingroup utils
      26              :  *
      27              :  */
      28              : 
      29              : /** \defgroup files logsurgeon Files
      30              :  * \ingroup logsurgeon
      31              :  */
      32              : 
      33              : /// An application to fix corrupted MagAO-X binary logs.
      34              : /** \todo document this
      35              :  *
      36              :  * \ingroup logsurgeon
      37              :  */
      38              : class logsurgeon : public mx::app::application
      39              : {
      40              :   protected:
      41              :     std::string m_fname; ///< The full path to the file to check
      42              : 
      43              :     bool m_checkOnly{ false }; /** If true then no modification to files on disk occurs, exit code
      44              :                                    0 indicates successful verification.  Default is false.*/
      45              : 
      46              :   public:
      47              :     enum returnVals
      48              :     {
      49              :         noerror            = 0, ///< no errors occurred
      50              :         file_not_specified = -1, ///< no file wa specified
      51              :         file_not_found     = -2, ///< the file was found (or an error occurred opening it)
      52              :         errors_found       = -100, ///< errors were found in the file in checkOnly mode
      53              :         error              = -9999 ///< other errors were found
      54              :     };
      55              : 
      56              :     virtual void setupConfig();
      57              : 
      58              :     virtual void loadConfig();
      59              : 
      60              :     virtual int execute();
      61              : 
      62              :     const std::string &fname();
      63              : 
      64              :     bool checkOnly();
      65              : };
      66              : 
      67           10 : void logsurgeon::setupConfig()
      68              : {
      69          140 :     config.add( "file",
      70              :                 "F",
      71              :                 "file",
      72              :                 argType::Required,
      73              :                 "",
      74              :                 "file",
      75              :                 true,
      76              :                 "string",
      77              :                 "The single file to process.  If no / are found in name it will look in the specified "
      78              :                 "directory (or MagAO-X default)." );
      79              : 
      80          140 :     config.add( "check",
      81              :                 "",
      82              :                 "",
      83              :                 argType::Required,
      84              :                 "",
      85              :                 "check-only",
      86              :                 false,
      87              :                 "bool",
      88              :                 "Check-only mode config file setting. If true then no modification to files on disk occurs, "
      89              :                 "exit code 0 indicates successful verification.  Default is false." );
      90              : 
      91          140 :     config.add( "checkCL",
      92              :                 "C",
      93              :                 "check-only",
      94              :                 argType::True,
      95              :                 "",
      96              :                 "",
      97              :                 false,
      98              :                 "bool",
      99              :                 "Check-only mode command-line flag. If true then no modification to files on disk occurs, "
     100              :                 "exit code 0 indicates successful verification. Overrides config file.  Default is false." );
     101           10 : }
     102              : 
     103           10 : void logsurgeon::loadConfig()
     104              : {
     105           20 :     config( m_fname, "file" );
     106           20 :     config( m_checkOnly, "check" );
     107              : 
     108              :     // Command line always wins
     109           30 :     if( config.isSet( "checkCL" ) )
     110              :     {
     111            4 :         m_checkOnly = true;
     112              :     }
     113           10 : }
     114              : 
     115           10 : int logsurgeon::execute()
     116              : {
     117           10 :     if( m_fname == "" )
     118              :     {
     119            1 :         std::cerr << "Must specify filename with -F option.\n";
     120            1 :         return file_not_specified;
     121              :     }
     122              : 
     123              :     FILE *fin;
     124            9 :     fin = fopen( m_fname.c_str(), "rb" );
     125              : 
     126            9 :     if( !fin )
     127              :     {
     128            9 :         std::cerr << "Error opening file " << m_fname << "\n";
     129            9 :         return file_not_found;
     130              :     }
     131              : 
     132            0 :     ssize_t fsz = mx::ioutils::fileSize( fin );
     133              : 
     134            0 :     char *buff = new char[fsz];
     135              : 
     136            0 :     ssize_t nrd = fread( buff, 1, fsz, fin );
     137            0 :     fclose( fin );
     138              : 
     139            0 :     if( nrd != fsz )
     140              :     {
     141            0 :         std::cerr << __FILE__ << " " << __LINE__ << " did not read complete file.\n";
     142            0 :         delete[] buff;
     143              : 
     144            0 :         return error;
     145              :     }
     146              : 
     147            0 :     ssize_t gcurr      = 0;
     148            0 :     bool    inbad      = false;
     149            0 :     ssize_t lastGoodSt = 0;
     150            0 :     ssize_t lastGoodSz = 0;
     151              : 
     152            0 :     ssize_t totBad = 0;
     153            0 :     ssize_t badSt  = 0;
     154            0 :     ssize_t kpt    = sizeof( logPrioT );
     155              : 
     156            0 :     char *gbuff = new char[fsz];
     157              : 
     158              :     // Now check each byte to see if it is a valid eventCode,
     159              :     // which makes it a potential start of a valid log
     160            0 :     while( kpt < fsz )
     161              :     {
     162            0 :         eventCodeT ec = *( (eventCodeT *)( &buff[kpt] ) );
     163              : 
     164            0 :         if( logCodeValid( ec ) )
     165              :         {
     166            0 :             char *buffst = &buff[kpt - sizeof( logPrioT )];
     167              : 
     168            0 :             msgLenT len    = logHeader::msgLen( buffst );
     169            0 :             msgLenT totLen = len + logHeader::headerSize( buffst );
     170              : 
     171              :             // Basic check if size isn't too big (i.e. would extend past end of file)
     172            0 :             ssize_t endpt = kpt - static_cast<ssize_t>( sizeof( logPrioT ) ) + static_cast<ssize_t>( totLen );
     173            0 :             if( endpt < static_cast<ssize_t>( fsz ) )
     174              :             {
     175              :                 // Now we use the flatlogs verifier.
     176            0 :                 char *nbuff = (char *)::operator new( totLen * sizeof( char ) );
     177              : 
     178            0 :                 memcpy( nbuff, buffst, totLen );
     179              : 
     180            0 :                 bufferPtrT buffPtr = bufferPtrT( nbuff );
     181              : 
     182              :                 // true means good
     183            0 :                 if( logVerify( ec, buffPtr, len ) )
     184              :                 {
     185              :                     // if we pass we check if we're currently in a bad section
     186            0 :                     if( inbad )
     187              :                     {
     188              :                         // if we were in a bad section we record the end of the bad section
     189            0 :                         inbad = false;
     190              : 
     191            0 :                         char *lastGBuff = (char *)::operator new( lastGoodSz * sizeof( char ) );
     192              : 
     193            0 :                         memcpy( lastGBuff, &buff[lastGoodSt], lastGoodSz );
     194            0 :                         bufferPtrT lgBuffPtr = bufferPtrT( lastGBuff );
     195              : 
     196            0 :                         std::cerr << "Found corrupt section: \n";
     197            0 :                         std::cerr << "   Before: ";
     198            0 :                         logStdFormat( std::cerr, lgBuffPtr );
     199            0 :                         std::cerr << "\n";
     200              : 
     201              :                         // printLogBuff(lglvl, lgec, logHeader::msgLen(lastGBuff), lgBuffPtr);
     202              : 
     203            0 :                         std::cerr << "   Corrupt: " << badSt << " - " << kpt << " (" << kpt - badSt << " bytes)\n";
     204            0 :                         totBad += kpt - badSt;
     205              : 
     206            0 :                         std::cerr << "   After:  ";
     207            0 :                         logStdFormat( std::cerr, buffPtr );
     208            0 :                         std::cerr << "\n";
     209            0 :                     }
     210              : 
     211              :                     // It's good so we write it to the good buffer
     212            0 :                     memcpy( &gbuff[gcurr], &buff[kpt - sizeof( logPrioT )], totLen );
     213              : 
     214            0 :                     lastGoodSt = kpt - sizeof( logPrioT );
     215            0 :                     lastGoodSz = totLen;
     216              : 
     217            0 :                     gcurr += totLen;
     218            0 :                     kpt += totLen;
     219              : 
     220            0 :                     continue;
     221            0 :                 }
     222            0 :             }
     223              :         }
     224              : 
     225              :         // If here the one of the checks has failed
     226            0 :         if( inbad == false )
     227              :         {
     228              :             // a new bad section has started
     229            0 :             badSt = kpt;
     230            0 :             inbad = true;
     231              :         }
     232              : 
     233            0 :         ++kpt;
     234              :     }
     235              : 
     236            0 :     std::cerr << "--------------------------------------------------------\n";
     237            0 :     std::cerr << "Found " << totBad << " bad bytes ( " << ( 100.0 * totBad ) / fsz << "% bad) \n";
     238            0 :     std::cerr << "Found " << gcurr << " good bytes ( " << ( 100.0 * gcurr ) / fsz << "% good)\n";
     239              : 
     240            0 :     if( totBad == 0 )
     241              :     {
     242            0 :         std::cerr << "Taking no action on good file.\n";
     243              :     }
     244            0 :     else if( m_checkOnly )
     245              :     {
     246            0 :         std::cerr << "Check-only mode set, exiting with error status to indicate failed verification\n";
     247            0 :         delete[] buff;
     248            0 :         delete[] gbuff;
     249            0 :         return errors_found;
     250              :     }
     251              :     else
     252              :     {
     253            0 :         std::string bupPath = m_fname + ".corrupted";
     254              : 
     255              :         FILE *fout;
     256            0 :         fout = fopen( bupPath.c_str(), "wb" );
     257              : 
     258            0 :         if( !fout )
     259              :         {
     260            0 :             std::cerr << "Error opening corrupted file for writing (" __FILE__ << " " << __LINE__ << ")\n";
     261            0 :             std::cerr << "No further action taken\n";
     262            0 :             delete[] buff;
     263            0 :             delete[] gbuff;
     264            0 :             return error;
     265              :         }
     266              : 
     267            0 :         ssize_t fwr = fwrite( buff, sizeof( char ), fsz, fout );
     268              : 
     269            0 :         int fcst = fclose( fout );
     270              : 
     271            0 :         if( fwr != fsz )
     272              :         {
     273            0 :             std::cerr << "Error writing backup corrupted file (" __FILE__ << " " << __LINE__ << ")\n";
     274            0 :             std::cerr << "No further action taken\n";
     275            0 :             delete[] buff;
     276            0 :             delete[] gbuff;
     277            0 :             return error;
     278              :         }
     279              : 
     280            0 :         if( fcst != 0 )
     281              :         {
     282            0 :             std::cerr << "Error closing backup corrupted file (" __FILE__ << " " << __LINE__ << ")\n";
     283            0 :             std::cerr << "No further action taken\n";
     284            0 :             delete[] buff;
     285            0 :             delete[] gbuff;
     286            0 :             return error;
     287              :         }
     288              : 
     289            0 :         std::cerr << "Wrote original file to: " << bupPath << "\n";
     290              : 
     291            0 :         fout = fopen( m_fname.c_str(), "wb" );
     292              : 
     293            0 :         if( !fout )
     294              :         {
     295            0 :             std::cerr << "Error opening existing file for writing (" __FILE__ << " " << __LINE__ << ")\n";
     296            0 :             std::cerr << "No further action taken\n";
     297              : 
     298            0 :             delete[] buff;
     299            0 :             delete[] gbuff;
     300            0 :             return error;
     301              :         }
     302              : 
     303            0 :         fwr = fwrite( gbuff, sizeof( char ), gcurr, fout );
     304              : 
     305            0 :         fcst = fclose( fout );
     306              : 
     307            0 :         if( fwr != gcurr )
     308              :         {
     309            0 :             std::cerr << "Error writing corrected file (" __FILE__ << " " << __LINE__ << ")\n";
     310            0 :             delete[] buff;
     311            0 :             delete[] gbuff;
     312            0 :             return error;
     313              :         }
     314              : 
     315            0 :         if( fcst != 0 )
     316              :         {
     317            0 :             std::cerr << "Error closing corrected file (" __FILE__ << " " << __LINE__ << ")\n";
     318            0 :             delete[] buff;
     319            0 :             delete[] gbuff;
     320            0 :             return error;
     321              :         }
     322              : 
     323            0 :         std::cerr << "Wrote corrected file to: " << m_fname << "\n";
     324              : 
     325            0 :         std::cerr << "Surgery Complete\n";
     326            0 :     }
     327            0 :     delete[] buff;
     328            0 :     delete[] gbuff;
     329              : 
     330            0 :     return noerror;
     331              : }
     332              : 
     333           11 : const std::string &logsurgeon::fname()
     334              : {
     335           11 :     return m_fname;
     336              : }
     337              : 
     338           11 : bool logsurgeon::checkOnly()
     339              : {
     340           11 :     return m_checkOnly;
     341              : }
     342              : 
     343              : #endif // hpp
        

Generated by: LCOV version 2.0-1