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
|