0
|
1 /*
|
|
2
|
|
3 Copyright (c) 2007 Carl Byington - 510 Software Group, released under
|
|
4 the GPL version 3 or any later version at your choice available at
|
|
5 http://www.gnu.org/licenses/gpl-3.0.txt
|
|
6
|
|
7 */
|
|
8
|
|
9 #include "includes.h"
|
|
10 #include <fcntl.h>
|
|
11 #include <sys/socket.h>
|
|
12 #include <netinet/in.h>
|
|
13 #include <arpa/inet.h>
|
|
14 #include <netdb.h>
|
|
15 #include <limits.h>
|
|
16
|
|
17 const char *token_context;
|
|
18 const char *token_file;
|
|
19 const char *token_include;
|
|
20 const char *token_lbrace;
|
|
21 const char *token_output;
|
|
22 const char *token_pattern;
|
|
23 const char *token_period;
|
|
24 const char *token_rbrace;
|
|
25 const char *token_semi;
|
|
26 const char *token_tempin;
|
|
27 const char *token_versions;
|
|
28 const char *token_wflogs;
|
|
29
|
|
30 string_set all_strings;// owns all the strings, only modified by the config loader thread
|
|
31 const int maxlen = 1000; // used for snprintf buffers
|
|
32
|
|
33
|
|
34
|
|
35 ////////////////////////////////////////////////
|
|
36 //
|
|
37 CONFIG::CONFIG() {
|
|
38 reference_count = 0;
|
|
39 fd = -1;
|
|
40 len = 0;
|
|
41 fdo = -1;
|
|
42 generation = 0;
|
|
43 load_time = 0;
|
|
44 period = 120;
|
|
45 versions = 3;
|
|
46 output = NULL;
|
|
47 tempin = NULL;
|
|
48 wflogs = NULL;
|
|
49 fn = NULL;
|
|
50 pattern = NULL;
|
|
51 }
|
|
52
|
|
53
|
|
54 CONFIG::~CONFIG() {
|
|
55 }
|
|
56
|
|
57
|
|
58 void CONFIG::sleep(int duration, time_t &previous) {
|
|
59 ::sleep(duration);
|
|
60 time_t now = time(NULL);
|
|
61 previous = now;
|
|
62 }
|
|
63
|
|
64
|
|
65 void CONFIG::free_all() {
|
|
66 regfree(&re);
|
|
67 close();
|
|
68 closeo();
|
|
69 }
|
|
70
|
|
71
|
|
72 void CONFIG::openo(bool msg) {
|
|
73 open_time = time(NULL);
|
|
74 fdo = ::creat(tempin, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
|
75 if (fdo == -1) {
|
|
76 if (msg) {
|
|
77 char buf[maxlen];
|
|
78 snprintf(buf, sizeof(buf), "wflogs tempin file %s not writeable", tempin);
|
|
79 tokp->token_error(buf);
|
|
80 }
|
|
81 }
|
|
82 }
|
|
83
|
|
84
|
|
85 void CONFIG::open(bool msg) {
|
|
86 fd = ::open(fn, O_RDONLY);
|
|
87 len = 0;
|
|
88 if (fd == -1) {
|
|
89 if (msg) {
|
|
90 char buf[maxlen];
|
|
91 snprintf(buf, sizeof(buf), "syslog file %s not readable", fn);
|
|
92 tokp->token_error(buf);
|
|
93 }
|
|
94 }
|
|
95 else {
|
|
96 if (debug_syslog > 1) {
|
|
97 snprintf(buf, sizeof(buf), "syslog file %s opened", fn);
|
|
98 my_syslog(buf);
|
|
99 }
|
|
100 if (msg) lseek(fd, 0, SEEK_END);
|
|
101 if (fstat(fd, &openfdstat)) {
|
|
102 close();
|
|
103 snprintf(buf, sizeof(buf), "syslog file %s cannot stat after open", fn);
|
|
104 tokp->token_error(buf);
|
|
105 }
|
|
106 // specify that this fd gets closed on exec, so that selinux
|
|
107 // won't complain about iptables trying to read log files.
|
|
108 int oldflags = fcntl(fd, F_GETFD, 0);
|
|
109 if (oldflags >= 0) {
|
|
110 fcntl(fd, F_SETFD, oldflags | FD_CLOEXEC);
|
|
111 }
|
|
112 }
|
|
113 }
|
|
114
|
|
115
|
|
116 bool CONFIG::write(char *p) {
|
|
117 // p points to \0 at end of buf, may be destroyed
|
|
118 if (failedo()) {
|
|
119 openo(false);
|
|
120 if (failedo()) return false;
|
|
121 }
|
|
122 *p = '\n';
|
|
123 ::write(fdo, buf, p-buf+1);
|
|
124 }
|
|
125
|
|
126
|
|
127 bool CONFIG::read() {
|
|
128 if (failed()) {
|
|
129 open(false);
|
|
130 if (failed()) return false;
|
|
131 }
|
|
132 int n = ::read(fd, buf+len, buflen-len);
|
|
133 bool have = (n > 0);
|
|
134 if (have) {
|
|
135 len += n;
|
|
136 while (true) {
|
|
137 char *p = (char*)memchr(buf, '\n', len);
|
|
138 if (!p) break;
|
|
139 n = p-buf;
|
|
140 *p = '\0';
|
|
141 process(p); // process null terminated string, but may destroy the null
|
|
142 len -= n+1;
|
|
143 memmove(buf, p+1, len);
|
|
144 }
|
|
145 // no <lf> in a full buffer
|
|
146 if (len == buflen) len = 0;
|
|
147 }
|
|
148 else {
|
|
149 // check for file close
|
|
150 struct stat filenamest;
|
|
151 if (0 == stat(fn, &filenamest)) {
|
|
152 if ((filenamest.st_dev != openfdstat.st_dev) ||
|
|
153 (filenamest.st_ino != openfdstat.st_ino)) {
|
|
154 close();
|
|
155 }
|
|
156 }
|
|
157 else {
|
|
158 // filename no longer exists
|
|
159 close();
|
|
160 }
|
|
161 }
|
|
162 check_wflog();
|
|
163 return have;
|
|
164 }
|
|
165
|
|
166
|
|
167 void CONFIG::closeo() {
|
|
168 if (fdo != -1) ::close(fdo);
|
|
169 fdo = -1;
|
|
170 }
|
|
171
|
|
172
|
|
173 void CONFIG::close() {
|
|
174 if (debug_syslog > 1) {
|
|
175 snprintf(buf, sizeof(buf), "syslog file %s closed", fn);
|
|
176 my_syslog(buf);
|
|
177 }
|
|
178 if (fd != -1) ::close(fd);
|
|
179 fd = -1;
|
|
180 }
|
|
181
|
|
182
|
|
183 void CONFIG::process(char *p) {
|
|
184 // p points to \0 at end of buf, may be destroyed
|
|
185 if (pattern) {
|
|
186 if (0 == regexec(&re, buf, 0, NULL, 0)) {
|
|
187 if (debug_syslog > 2) {
|
|
188 my_syslog(buf); // show lines with matches
|
|
189 }
|
|
190 write(p);
|
|
191 }
|
|
192 }
|
|
193 }
|
|
194
|
|
195
|
|
196 void CONFIG::check_wflog() {
|
|
197 time_t now = time(NULL);
|
|
198 if ((fdo != -1) && (open_time + period < now)) {
|
|
199 closeo();
|
|
200 // rename previous wflog html output files
|
|
201 char buf[maxlen];
|
|
202 char fn1[maxlen];
|
|
203 char fn2[maxlen];
|
|
204 for (int i=1; i<versions; i++) {
|
|
205 int j = versions - 1 - i;
|
|
206 int k = j + 1;
|
|
207 snprintf(fn1, maxlen, output, j);
|
|
208 snprintf(fn2, maxlen, output, k);
|
|
209 snprintf(buf, maxlen, "mv \"%s\" \"%s\"", fn1, fn2);
|
|
210 system(buf);
|
|
211 }
|
|
212 snprintf(buf, maxlen, wflogs, fn1);
|
|
213 system(buf);
|
|
214 openo(false);
|
|
215 }
|
|
216 }
|
|
217
|
|
218
|
|
219 void CONFIG::dump() {
|
|
220 int level = 0;
|
|
221 char indent[maxlen];
|
|
222 int i = min(maxlen-1, level*4);
|
|
223 memset(indent, ' ', i);
|
|
224 indent[i] = '\0';
|
|
225 printf("%s period %d; \n", indent, period);
|
|
226 printf("%s versions %d; \n", indent, versions);
|
|
227 printf("%s output \"%s\";\n", indent, output);
|
|
228 printf("%s tempin \"%s\";\n", indent, tempin);
|
|
229 printf("%s wflogs \"%s\";\n", indent, wflogs);
|
|
230 printf("%s file \"%s\";\n", indent, fn);
|
|
231 printf("%s pattern \"%s\";\n", indent, pattern);
|
|
232 }
|
|
233
|
|
234
|
|
235 ////////////////////////////////////////////////
|
|
236 // helper to discard the strings held by a string_set
|
|
237 //
|
|
238 void discard(string_set &s) {
|
|
239 for (string_set::iterator i=s.begin(); i!=s.end(); i++) {
|
|
240 free((void*)*i);
|
|
241 }
|
|
242 s.clear();
|
|
243 }
|
|
244
|
|
245
|
|
246 ////////////////////////////////////////////////
|
|
247 // helper to register a string in a string set
|
|
248 //
|
|
249 const char* register_string(string_set &s, const char *name) {
|
|
250 string_set::const_iterator i = s.find(name);
|
|
251 if (i != s.end()) return *i;
|
|
252 char *x = strdup(name);
|
|
253 s.insert(x);
|
|
254 return x;
|
|
255 }
|
|
256
|
|
257
|
|
258 ////////////////////////////////////////////////
|
|
259 // register a global string
|
|
260 //
|
|
261 const char* register_string(const char *name) {
|
|
262 return register_string(all_strings, name);
|
|
263 }
|
|
264
|
|
265
|
|
266 ////////////////////////////////////////////////
|
|
267 // clear all global strings, helper for valgrind checking
|
|
268 //
|
|
269 void clear_strings() {
|
|
270 discard(all_strings);
|
|
271 }
|
|
272
|
|
273
|
|
274 ////////////////////////////////////////////////
|
|
275 //
|
|
276 bool tsa(TOKEN &tok, const char *token);
|
|
277 bool tsa(TOKEN &tok, const char *token) {
|
|
278 const char *have = tok.next();
|
|
279 if (have == token) return true;
|
|
280 tok.token_error(token, have);
|
|
281 return false;
|
|
282 }
|
|
283
|
|
284
|
|
285 ////////////////////////////////////////////////
|
|
286 // parse a config file
|
|
287 //
|
|
288 bool load_conf(CONFIG &dc, const char *fn) {
|
|
289 TOKEN tok(fn, &dc.config_files);
|
|
290 dc.set_token(tok);
|
|
291 while (true) {
|
|
292 const char *have = tok.next();
|
|
293 if (!have) break;
|
|
294 if (have == token_period) {
|
|
295 have = tok.next();
|
|
296 dc.set_period(atoi(have));
|
|
297 if (!tsa(tok, token_semi)) return false;
|
|
298 }
|
|
299 else if (have == token_versions) {
|
|
300 have = tok.next();
|
|
301 dc.set_versions(atoi(have));
|
|
302 if (!tsa(tok, token_semi)) return false;
|
|
303 }
|
|
304 else if (have == token_output) {
|
|
305 dc.set_output(tok.next());
|
|
306 if (!tsa(tok, token_semi)) return false;
|
|
307 }
|
|
308 else if (have == token_tempin) {
|
|
309 dc.set_tempin(tok.next());
|
|
310 if (!tsa(tok, token_semi)) return false;
|
|
311 }
|
|
312 else if (have == token_wflogs) {
|
|
313 dc.set_wflogs(tok.next());
|
|
314 if (!tsa(tok, token_semi)) return false;
|
|
315 }
|
|
316 else if (have == token_file) {
|
|
317 dc.set_file(tok.next());
|
|
318 if (!tsa(tok, token_semi)) return false;
|
|
319 }
|
|
320 else if (have == token_pattern) {
|
|
321 dc.pattern = tok.next();
|
|
322 int rc = regcomp(&dc.re, dc.pattern, REG_ICASE | REG_EXTENDED);
|
|
323 if (rc) {
|
|
324 char bu[maxlen];
|
|
325 regerror(rc, &dc.re, bu, maxlen);
|
|
326 char buf[maxlen];
|
|
327 snprintf(buf, sizeof(buf), "pattern %s not valid - %s", dc.pattern, bu);
|
|
328 tok.token_error(buf);
|
|
329 dc.pattern = NULL;
|
|
330 }
|
|
331
|
|
332 if (!tsa(tok, token_semi)) return false;
|
|
333 }
|
|
334 else {
|
|
335 tok.token_error("statement", have);
|
|
336 return false;
|
|
337 }
|
|
338 }
|
|
339 return true;
|
|
340 }
|
|
341
|
|
342
|
|
343 ////////////////////////////////////////////////
|
|
344 // init the tokens
|
|
345 //
|
|
346 void token_init() {
|
|
347 token_file = register_string("file");
|
|
348 token_include = register_string("include");
|
|
349 token_lbrace = register_string("{");
|
|
350 token_output = register_string("output");
|
|
351 token_pattern = register_string("pattern");
|
|
352 token_period = register_string("period");
|
|
353 token_rbrace = register_string("}");
|
|
354 token_semi = register_string(";");
|
|
355 token_tempin = register_string("tempin");
|
|
356 token_versions = register_string("versions");
|
|
357 token_wflogs = register_string("wflogs");
|
|
358 }
|