/* The software in this package is distributed under the GNU General Public License version 2 (with a special exception described below). A copy of GNU General Public License (GPL) is included in this distribution, in the file COPYING.GPL. As a special exception, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other works to produce a work based on this file, this file does not by itself cause the resulting work to be covered by the GNU General Public License. However the source code for this file must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. */ #include #include #include #include #include #include #include #include #include "logread.hxx" using namespace std; /// Constructor logread::logread(string filename, bool _goto_end) { this->filename = filename; reset(); if (_goto_end) goto_end(); } /// Reset line state engine void logread::reset() { logsize = 0; logsize_old = 0; line.clear(); line_complete = false; } /// Skip initial content. Useful when parsing /var/log/messages void logread::goto_end() { struct stat my_stat; if (stat(filename.c_str(), &my_stat) == 0) { logsize = my_stat.st_size; logsize_old = logsize; } } /// Main read loop void logread::read() { // check if size changed if (check_changed()) { if (logsize > logsize_old) { string logChanges = getLogChanges(); do { extractSingeLine (logChanges); line_complete == true ? processLine() : processUnfinishedLine(); } while (!logChanges.empty()); } else reset(); // file got truncated logsize_old = logsize; } } /// Check if file is modified bool logread::check_changed() { bool changed = false; struct stat my_stat; if (stat(filename.c_str(), &my_stat) == 0 && my_stat.st_size != logsize) { logsize = my_stat.st_size; changed = true; } return changed; } /// Get changed content since last read string logread::getLogChanges() { // open logfile ifstream log( filename.c_str(), ifstream::in ); if ( !log.is_open() ) return ""; // read logfile int size = logsize-logsize_old; boost::scoped_array read_buffer( new char[ size + 1 ] ); log.seekg( logsize_old, fstream::beg ); if ( log.rdstate() != fstream::goodbit ) throw runtime_error("can't seek in file " + filename); log.read( read_buffer.get(), size ); if ( log.rdstate() != fstream::goodbit ) throw runtime_error("error reading from file " + filename); log.close(); read_buffer[size] = 0; string changes( read_buffer.get() ); return changes; } /// Extract a single line from input buffer void logread::extractSingeLine(string &input) { string::size_type rtn_pos, copysize; // go to next line if (line_complete) { line_complete = false; line.clear(); } rtn_pos = input.find("\n", 0); // complete line or even multi-line if (rtn_pos != string::npos) copysize = rtn_pos; else copysize = input.length(); line.append (input.c_str(), copysize); // Erase data from the input buffer if (rtn_pos == string::npos) // no return found input.erase (0, copysize); else { // also erase the return input.erase (0, copysize+1); line_complete = true; } } /** Short example #include class logread_syslog : public logread { public: logread_syslog() : logread("messages", false) {}; void processLine() { cout << "finished: " << line << endl; } void processUnfinishedLine() { cout << "unfinished: " << line << endl; } }; int main(void) { logread_syslog messages; while (1) { messages.read(); sleep(1); } return 0; } */