Commit | Line | Data |
---|---|---|
0e23f538 TJ |
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 | */ | |
7113c4f0 TJ |
20 | #include <stdlib.h> |
21 | #include <sys/types.h> | |
22 | #include <sys/stat.h> | |
23 | #include <unistd.h> | |
a74046f1 | 24 | #include <stdio.h> |
7113c4f0 TJ |
25 | |
26 | #include <stdexcept> | |
27 | ||
28 | #include "logread.hxx" | |
29 | ||
30 | using namespace std; | |
31 | ||
32 | /// Constructor | |
33 | logread::logread(string filename, bool _goto_end) | |
34 | { | |
35 | this->filename = filename; | |
36 | reset(); | |
37 | ||
38 | if (_goto_end) | |
39 | goto_end(); | |
40 | } | |
41 | ||
42 | /// Reset line state engine | |
43 | void logread::reset() | |
44 | { | |
45 | logsize = 0; | |
46 | logsize_old = 0; | |
47 | ||
48 | line.clear(); | |
49 | line_complete = false; | |
50 | } | |
51 | ||
52 | /// Skip initial content. Useful when parsing /var/log/messages | |
53 | void logread::goto_end() | |
54 | { | |
55 | struct stat my_stat; | |
56 | if (stat(filename.c_str(), &my_stat) == 0) { | |
57 | logsize = my_stat.st_size; | |
58 | logsize_old = logsize; | |
59 | } | |
60 | } | |
61 | ||
62 | /// Main read loop | |
63 | void logread::read() | |
64 | { | |
65 | // check if size changed | |
66 | if (check_changed()) { | |
67 | if (logsize > logsize_old) { | |
68 | string logChanges = getLogChanges(); | |
69 | ||
70 | do { | |
71 | extractSingeLine (logChanges); | |
72 | line_complete == true ? processLine() : processUnfinishedLine(); | |
73 | } while (!logChanges.empty()); | |
74 | } else | |
75 | reset(); // file got truncated | |
76 | ||
77 | logsize_old = logsize; | |
78 | } | |
79 | } | |
80 | ||
81 | /// Check if file is modified | |
82 | bool logread::check_changed() | |
83 | { | |
84 | bool changed = false; | |
85 | ||
86 | struct stat my_stat; | |
87 | if (stat(filename.c_str(), &my_stat) == 0 | |
88 | && my_stat.st_size != logsize) | |
89 | { | |
90 | logsize = my_stat.st_size; | |
91 | changed = true; | |
92 | } | |
93 | ||
94 | return changed; | |
95 | } | |
96 | ||
97 | /// Get changed content since last read | |
98 | string logread::getLogChanges() | |
99 | { | |
100 | // open logfile | |
101 | FILE *log = fopen (filename.c_str(), "r"); | |
102 | if (!log) | |
103 | return ""; | |
104 | ||
105 | // read logfile | |
106 | int size = logsize-logsize_old; | |
107 | ||
108 | char *read_buffer = new char [size+1]; | |
109 | if (fseek (log, logsize_old, SEEK_SET) != 0) | |
110 | throw runtime_error("can't seek in file " + filename); | |
111 | fread (read_buffer, size, 1, log); | |
112 | if (ferror(log)) | |
113 | throw runtime_error("error reading from file " + filename); | |
114 | fclose (log); | |
115 | ||
116 | read_buffer[size] = 0; | |
117 | string changes(read_buffer); | |
118 | delete[] read_buffer; | |
119 | ||
120 | return changes; | |
121 | } | |
122 | ||
123 | /// Extract a single line from input buffer | |
124 | void logread::extractSingeLine(string &input) | |
125 | { | |
126 | string::size_type rtn_pos, copysize; | |
127 | ||
128 | // go to next line | |
129 | if (line_complete) { | |
130 | line_complete = false; | |
131 | line.clear(); | |
132 | } | |
133 | ||
134 | rtn_pos = input.find("\n", 0); | |
135 | ||
136 | // complete line or even multi-line | |
137 | if (rtn_pos != string::npos) | |
138 | copysize = rtn_pos; | |
139 | else | |
140 | copysize = input.length(); | |
141 | ||
142 | line.append (input.c_str(), copysize); | |
143 | ||
144 | // Erase data from the input buffer | |
145 | if (rtn_pos == string::npos) // no return found | |
146 | input.erase (0, copysize); | |
147 | else { | |
148 | // also erase the return | |
149 | input.erase (0, copysize+1); | |
150 | line_complete = true; | |
151 | } | |
152 | } | |
153 | ||
154 | /** Short example | |
155 | ||
156 | #include <iostream> | |
157 | class logread_syslog : public logread | |
158 | { | |
159 | public: | |
160 | logread_syslog() : logread("messages", false) {}; | |
161 | ||
162 | void processLine() | |
163 | { | |
164 | cout << "finished: " << line << endl; | |
165 | } | |
166 | ||
167 | void processUnfinishedLine() | |
168 | { | |
169 | cout << "unfinished: " << line << endl; | |
170 | } | |
171 | }; | |
172 | ||
173 | int main(void) | |
174 | { | |
175 | logread_syslog messages; | |
176 | ||
177 | while (1) { | |
178 | messages.read(); | |
179 | sleep(1); | |
180 | } | |
181 | ||
182 | return 0; | |
183 | } | |
184 | */ |