Adapt Requires.private and Libs.private to make pkg-config output all neccessary...
[libi2ncommon] / src / logread.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 #include <stdlib.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <stdio.h>
25
26 #include <fstream>
27 #include <stdexcept>
28
29 #include <boost/scoped_array.hpp>
30
31 #include "logread.hxx"
32
33 using namespace std;
34
35 /// Constructor
36 logread::logread(string filename, bool _goto_end)
37 {
38     this->filename = filename;
39     reset();
40
41     if (_goto_end)
42         goto_end();
43 }
44
45 /// Reset line state engine
46 void logread::reset()
47 {
48     logsize = 0;
49     logsize_old = 0;
50
51     line.clear();
52     line_complete = false;
53 }
54
55 /// Skip initial content. Useful when parsing /var/log/messages
56 void logread::goto_end()
57 {
58     struct stat my_stat;
59     if (stat(filename.c_str(), &my_stat) == 0) {
60         logsize = my_stat.st_size;
61         logsize_old = logsize;
62     }
63 }
64
65 /// Main read loop
66 void logread::read()
67 {
68     // check if size changed
69     if (check_changed()) {
70         if (logsize > logsize_old) {
71             string logChanges = getLogChanges();
72
73             do {
74                 extractSingeLine (logChanges);
75                 line_complete == true ? processLine() : processUnfinishedLine();
76             } while (!logChanges.empty());
77         } else
78             reset();    // file got truncated
79
80         logsize_old = logsize;
81     }
82 }
83
84 /// Check if file is modified
85 bool logread::check_changed()
86 {
87     bool changed = false;
88
89     struct stat my_stat;
90     if (stat(filename.c_str(), &my_stat) == 0
91         && my_stat.st_size != logsize)
92     {
93         logsize = my_stat.st_size;
94         changed = true;
95     }
96
97     return changed;
98 }
99
100 /// Get changed content since last read
101 string logread::getLogChanges()
102 {
103     // open logfile
104     ifstream log( filename.c_str(), ifstream::in );
105     if ( !log.is_open() )
106         return "";
107
108     // read logfile
109     int size = logsize-logsize_old;
110
111     boost::scoped_array<char> read_buffer( new char[ size + 1 ] );
112     log.seekg( logsize_old, fstream::beg );
113     if ( log.rdstate() != fstream::goodbit )
114         throw runtime_error("can't seek in file " + filename);
115     log.read( read_buffer.get(), size );
116     if ( log.rdstate() != fstream::goodbit )
117         throw runtime_error("error reading from file " + filename);
118     log.close();
119
120     read_buffer[size] = 0;
121     string changes( read_buffer.get() );
122
123     return changes;
124 }
125
126 /// Extract a single line from input buffer
127 void logread::extractSingeLine(string &input)
128 {
129     string::size_type rtn_pos, copysize;
130
131     // go to next line
132     if (line_complete) {
133         line_complete = false;
134         line.clear();
135     }
136
137     rtn_pos = input.find("\n", 0);
138
139     // complete line or even multi-line
140     if (rtn_pos != string::npos)
141         copysize = rtn_pos;
142     else
143         copysize = input.length();
144
145     line.append (input.c_str(), copysize);
146
147     // Erase data from the input buffer
148     if (rtn_pos == string::npos)    // no return found
149         input.erase (0, copysize);
150     else {
151         // also erase the return
152         input.erase (0, copysize+1);
153         line_complete = true;
154     }
155 }
156
157 /** Short example
158
159 #include <iostream>
160 class logread_syslog : public logread
161 {
162 public:
163     logread_syslog() : logread("messages", false) {};
164
165     void processLine()
166     {
167         cout << "finished: " << line << endl;
168     }
169
170     void processUnfinishedLine()
171     {
172         cout << "unfinished: " << line << endl;
173     }
174 };
175
176 int main(void)
177 {
178     logread_syslog messages;
179
180     while (1) {
181         messages.read();
182         sleep(1);
183     }
184
185     return 0;
186 }
187 */