implemented log file monitoring using Windows VSS snapshots
[public/netxms.git] / src / libnxlp / parser.cpp
CommitLineData
659bf814 1/*
5039dede
AK
2** NetXMS - Network Management System
3** Log Parsing Library
bc3e2810 4** Copyright (C) 2003-2017 Raden Solutions
5039dede
AK
5**
6** This program is free software; you can redistribute it and/or modify
65d2c384
VK
7** it under the terms of the GNU Lesser General Public License as published by
8** the Free Software Foundation; either version 3 of the License, or
5039dede
AK
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
65d2c384 16** You should have received a copy of the GNU Lesser General Public License
5039dede
AK
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** File: parser.cpp
21**
22**/
23
24#include "libnxlp.h"
5039dede 25#include <expat.h>
5039dede 26
c1482463
VK
27/**
28 * Context state texts
29 */
6646dca4 30static const TCHAR *s_states[] = { _T("MANUAL"), _T("AUTO"), _T("INACTIVE") };
5039dede 31
c1482463 32/**
48126778 33 * XML parser state codes
c1482463 34 */
073283c5
VK
35enum ParserState
36{
37 XML_STATE_INIT,
38 XML_STATE_END,
39 XML_STATE_ERROR,
40 XML_STATE_PARSER,
41 XML_STATE_RULES,
42 XML_STATE_RULE,
43 XML_STATE_MATCH,
44 XML_STATE_EVENT,
45 XML_STATE_FILE,
46 XML_STATE_ID,
47 XML_STATE_LEVEL,
48 XML_STATE_SOURCE,
49 XML_STATE_CONTEXT,
50 XML_STATE_MACROS,
51 XML_STATE_MACRO,
52 XML_STATE_DESCRIPTION,
53 XML_STATE_EXCLUSION_SCHEDULES,
54 XML_STATE_EXCLUSION_SCHEDULE
55};
5039dede 56
48126778
VK
57/**
58 * XML parser state for creating LogParser object from XML
59 */
565a8f28 60struct XML_PARSER_STATE
5039dede
AK
61{
62 LogParser *parser;
073283c5 63 ParserState state;
5039dede
AK
64 String regexp;
65 String event;
721fc691 66 String file;
565a8f28 67 StringList files;
bc3e2810
VK
68 IntegerArray<INT32> encodings;
69 IntegerArray<INT32> preallocFlags;
f4181253
VK
70 IntegerArray<INT32> snapshotFlags;
71 String id;
5039dede
AK
72 String level;
73 String source;
74 String context;
75 String description;
839931d3 76 String ruleName;
5039dede
AK
77 int contextAction;
78 String ruleContext;
79 int numEventParams;
80 String errorText;
81 String macroName;
82 String macro;
073283c5 83 String schedule;
6d738067
VK
84 bool invertedRule;
85 bool breakFlag;
659bf814 86 int repeatCount;
87 int repeatInterval;
88 bool resetRepeat;
63e99e56 89
f4181253 90 XML_PARSER_STATE() : encodings(4, 4), preallocFlags(4, 4), snapshotFlags(4, 4)
48126778 91 {
073283c5 92 state = XML_STATE_INIT;
48126778
VK
93 parser = NULL;
94 invertedRule = false;
95 breakFlag = false;
96 contextAction = CONTEXT_SET_AUTOMATIC;
97 numEventParams = 0;
659bf814 98 repeatCount = 0;
99 repeatInterval = 0;
100 resetRepeat = true;
48126778 101 }
565a8f28 102};
5039dede 103
66ee0102
VK
104/**
105 * Parser default constructor
106 */
1df332d8 107LogParser::LogParser()
5039dede 108{
1df332d8 109 m_rules = new ObjectArray<LogParserRule>(16, 16, true);
5039dede
AK
110 m_cb = NULL;
111 m_userArg = NULL;
e464b7dc 112 m_name = NULL;
5039dede 113 m_fileName = NULL;
6646dca4 114 m_fileEncoding = LP_FCP_ACP;
bc3e2810 115 m_preallocatedFile = false;
5039dede
AK
116 m_eventNameList = NULL;
117 m_eventResolver = NULL;
118 m_thread = INVALID_THREAD_HANDLE;
119 m_recordsProcessed = 0;
120 m_recordsMatched = 0;
25c5a016 121 m_processAllRules = false;
4d8c4cf3 122 m_suspended = false;
5039dede
AK
123 m_traceLevel = 0;
124 m_traceCallback = NULL;
268105f1 125 m_status = LPS_INIT;
18b96a67
VK
126#ifdef _WIN32
127 m_marker = NULL;
128#endif
5039dede
AK
129}
130
66ee0102
VK
131/**
132 * Parser copy constructor
133 */
dedeffb3 134LogParser::LogParser(const LogParser *src)
66ee0102 135{
1df332d8
VK
136 int count = src->m_rules->size();
137 m_rules = new ObjectArray<LogParserRule>(count, 16, true);
bc3e2810 138 for(int i = 0; i < count; i++)
1df332d8 139 m_rules->add(new LogParserRule(src->m_rules->get(i), this));
5039dede 140
dedeffb3
VK
141 m_macros.addAll(&src->m_macros);
142 m_contexts.addAll(&src->m_contexts);
143 m_exclusionSchedules.addAll(&src->m_exclusionSchedules);
144
66ee0102
VK
145 m_cb = src->m_cb;
146 m_userArg = src->m_userArg;
bc3e2810
VK
147 m_name = _tcsdup_ex(src->m_name);
148 m_fileName = _tcsdup_ex(src->m_fileName);
66ee0102 149 m_fileEncoding = src->m_fileEncoding;
bc3e2810 150 m_preallocatedFile = src->m_preallocatedFile;
5039dede 151
66ee0102
VK
152 if (src->m_eventNameList != NULL)
153 {
154 int count;
155 for(count = 0; src->m_eventNameList[count].text != NULL; count++);
156 m_eventNameList = (count > 0) ? (CODE_TO_TEXT *)nx_memdup(src->m_eventNameList, sizeof(CODE_TO_TEXT) * (count + 1)) : NULL;
157 }
158 else
159 {
160 m_eventNameList = NULL;
161 }
162
163 m_eventResolver = src->m_eventResolver;
164 m_thread = INVALID_THREAD_HANDLE;
165 m_recordsProcessed = 0;
166 m_recordsMatched = 0;
167 m_processAllRules = src->m_processAllRules;
4d8c4cf3 168 m_suspended = src->m_suspended;
66ee0102
VK
169 m_traceLevel = src->m_traceLevel;
170 m_traceCallback = src->m_traceCallback;
268105f1 171 m_status = LPS_INIT;
18b96a67
VK
172#ifdef _WIN32
173 m_marker = _tcsdup_ex(src->m_marker);
174#endif
66ee0102
VK
175}
176
177/**
178 * Destructor
179 */
5039dede
AK
180LogParser::~LogParser()
181{
1df332d8 182 delete m_rules;
e7912b5d
VK
183 free(m_name);
184 free(m_fileName);
99ebf084 185#ifdef _WIN32
e7912b5d 186 free(m_marker);
99ebf084 187#endif
5039dede
AK
188}
189
66ee0102
VK
190/**
191 * Trace
192 */
6646dca4 193void LogParser::trace(int level, const TCHAR *format, ...)
5039dede
AK
194{
195 va_list args;
196
197 if ((m_traceCallback == NULL) || (level > m_traceLevel))
198 return;
199
200 va_start(args, format);
bc3e2810 201 m_traceCallback(level, format, args);
5039dede
AK
202 va_end(args);
203}
204
29bb6817
VK
205/**
206 * Add rule
207 */
6d738067 208bool LogParser::addRule(LogParserRule *rule)
5039dede 209{
bc3e2810
VK
210 bool valid = rule->isValid();
211 if (valid)
5039dede 212 {
1df332d8 213 m_rules->add(rule);
5039dede
AK
214 }
215 else
216 {
217 delete rule;
218 }
bc3e2810 219 return valid;
5039dede
AK
220}
221
29bb6817
VK
222/**
223 * Create and add rule
224 */
659bf814 225bool LogParser::addRule(const TCHAR *regexp, UINT32 eventCode, const TCHAR *eventName, int numParams, int repeatInterval, int repeatCount, bool resetRepeat)
5039dede 226{
839931d3 227 return addRule(new LogParserRule(this, NULL, regexp, eventCode, eventName, numParams, repeatInterval, repeatCount, resetRepeat));
5039dede
AK
228}
229
29bb6817
VK
230/**
231 * Check context
232 */
6646dca4 233const TCHAR *LogParser::checkContext(LogParserRule *rule)
5039dede 234{
6646dca4 235 const TCHAR *state;
659bf814 236
6d738067 237 if (rule->getContext() == NULL)
32ec6391 238 {
6d738067
VK
239 trace(5, _T(" rule has no context"));
240 return s_states[CONTEXT_SET_MANUAL];
32ec6391 241 }
659bf814 242
fb986055 243 state = m_contexts.get(rule->getContext());
5039dede 244 if (state == NULL)
32ec6391 245 {
6d738067 246 trace(5, _T(" context '%s' inactive, rule should be skipped"), rule->getContext());
659bf814 247 return NULL; // Context inactive, don't use this rule
32ec6391 248 }
659bf814 249
6d738067 250 if (!_tcscmp(state, s_states[CONTEXT_CLEAR]))
32ec6391 251 {
6d738067 252 trace(5, _T(" context '%s' inactive, rule should be skipped"), rule->getContext());
32ec6391
VK
253 return NULL;
254 }
255 else
256 {
6d738067 257 trace(5, _T(" context '%s' active (mode=%s)"), rule->getContext(), state);
32ec6391
VK
258 return state;
259 }
5039dede
AK
260}
261
acc04d96
VK
262/**
263 * Match log record
264 */
da623f8b
VK
265bool LogParser::matchLogRecord(bool hasAttributes, const TCHAR *source, UINT32 eventId,
266 UINT32 level, const TCHAR *line, UINT32 objectId)
5039dede 267{
6646dca4 268 const TCHAR *state;
6d738067
VK
269 bool matched = false;
270
271 if (hasAttributes)
856711aa 272 trace(5, _T("Match event: source=\"%s\" id=%u level=%d text=\"%s\""), source, eventId, level, line);
6d738067 273 else
856711aa 274 trace(5, _T("Match line: \"%s\""), line);
5039dede 275
5039dede 276 m_recordsProcessed++;
bc3e2810 277 int i;
1df332d8 278 for(i = 0; i < m_rules->size(); i++)
5039dede 279 {
1df332d8 280 LogParserRule *rule = m_rules->get(i);
bc3e2810
VK
281 trace(6, _T("checking rule %d \"%s\""), i + 1, rule->getDescription());
282 if ((state = checkContext(rule)) != NULL)
5039dede 283 {
7c09239b 284 bool ruleMatched = hasAttributes ?
bc3e2810
VK
285 rule->matchEx(source, eventId, level, line, m_cb, objectId, m_userArg) :
286 rule->match(line, m_cb, objectId, m_userArg);
7c09239b 287 if (ruleMatched)
5039dede 288 {
bc3e2810 289 trace(5, _T("rule %d \"%s\" matched"), i + 1, rule->getDescription());
7c09239b
VK
290 if (!matched)
291 m_recordsMatched++;
659bf814 292
7c09239b 293 // Update context
bc3e2810 294 if (rule->getContextToChange() != NULL)
7c09239b 295 {
bc3e2810
VK
296 m_contexts.set(rule->getContextToChange(), s_states[rule->getContextAction()]);
297 trace(5, _T("rule %d \"%s\": context %s set to %s"), i + 1,
298 rule->getDescription(), rule->getContextToChange(), s_states[rule->getContextAction()]);
7c09239b 299 }
659bf814 300
7c09239b
VK
301 // Set context of this rule to inactive if rule context mode is "automatic reset"
302 if (!_tcscmp(state, s_states[CONTEXT_SET_AUTOMATIC]))
303 {
bc3e2810 304 m_contexts.set(rule->getContext(), s_states[CONTEXT_CLEAR]);
856711aa 305 trace(5, _T("rule %d \"%s\": context %s cleared because it was set to automatic reset mode"),
bc3e2810 306 i + 1, rule->getDescription(), rule->getContext());
7c09239b
VK
307 }
308 matched = true;
bc3e2810 309 if (!m_processAllRules || rule->getBreakFlag())
7c09239b 310 break;
5039dede 311 }
5039dede
AK
312 }
313 }
1df332d8 314 if (i < m_rules->size())
856711aa 315 trace(5, _T("processing stopped at rule %d \"%s\"; result = %s"), i + 1,
1df332d8 316 m_rules->get(i)->getDescription(), matched ? _T("true") : _T("false"));
5039dede 317 else
856711aa 318 trace(5, _T("Processing stopped at end of rules list; result = %s"), matched ? _T("true") : _T("false"));
5039dede
AK
319 return matched;
320}
321
acc04d96
VK
322/**
323 * Match simple log line
324 */
da623f8b 325bool LogParser::matchLine(const TCHAR *line, UINT32 objectId)
6d738067
VK
326{
327 return matchLogRecord(false, NULL, 0, 0, line, objectId);
328}
329
acc04d96
VK
330/**
331 * Match log event (text with additional attributes - source, severity, event id)
332 */
da623f8b 333bool LogParser::matchEvent(const TCHAR *source, UINT32 eventId, UINT32 level, const TCHAR *line, UINT32 objectId)
6d738067
VK
334{
335 return matchLogRecord(true, source, eventId, level, line, objectId);
336}
337
acc04d96
VK
338/**
339 * Set associated file name
340 */
6646dca4 341void LogParser::setFileName(const TCHAR *name)
5039dede 342{
bc3e2810 343 free(m_fileName);
5039dede 344 m_fileName = (name != NULL) ? _tcsdup(name) : NULL;
e464b7dc
VK
345 if (m_name == NULL)
346 m_name = _tcsdup(name); // Set parser name to file name
347}
348
29bb6817
VK
349/**
350 * Set parser name
351 */
6646dca4 352void LogParser::setName(const TCHAR *name)
e464b7dc
VK
353{
354 safe_free(m_name);
355 m_name = _tcsdup((name != NULL) ? name : CHECK_NULL(m_fileName));
5039dede
AK
356}
357
29bb6817
VK
358/**
359 * Add macro
360 */
6646dca4 361void LogParser::addMacro(const TCHAR *name, const TCHAR *value)
5039dede 362{
fb986055 363 m_macros.set(name, value);
5039dede
AK
364}
365
66ee0102
VK
366/**
367 * Get macro
368 */
6646dca4 369const TCHAR *LogParser::getMacro(const TCHAR *name)
5039dede
AK
370{
371 const TCHAR *value;
372
fb986055 373 value = m_macros.get(name);
5039dede
AK
374 return CHECK_NULL_EX(value);
375}
376
66ee0102
VK
377/**
378 * Create parser configuration from XML - callback for element start
379 */
5039dede
AK
380static void StartElement(void *userData, const char *name, const char **attrs)
381{
382 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
383
384 if (!strcmp(name, "parser"))
385 {
386 ps->state = XML_STATE_PARSER;
6d738067
VK
387 ps->parser->setProcessAllFlag(XMLGetAttrBoolean(attrs, "processAll", false));
388 ps->parser->setTraceLevel(XMLGetAttrInt(attrs, "trace", 0));
e464b7dc
VK
389 const char *name = XMLGetAttr(attrs, "name");
390 if (name != NULL)
6646dca4
VK
391 {
392#ifdef UNICODE
393 WCHAR *wname = WideStringFromUTF8String(name);
394 ps->parser->setName(wname);
395 free(wname);
396#else
e464b7dc 397 ps->parser->setName(name);
6646dca4
VK
398#endif
399 }
5039dede
AK
400 }
401 else if (!strcmp(name, "file"))
402 {
403 ps->state = XML_STATE_FILE;
6646dca4
VK
404 const char *encoding = XMLGetAttr(attrs, "encoding");
405 if (encoding != NULL)
406 {
0ea005dd
EJ
407 if ((*encoding == 0))
408 {
bc3e2810 409 ps->encodings.add(LP_FCP_AUTO);
0ea005dd
EJ
410 }
411 if (!stricmp(encoding, "acp"))
6646dca4 412 {
bc3e2810 413 ps->encodings.add(LP_FCP_ACP);
6646dca4
VK
414 }
415 else if (!stricmp(encoding, "utf8") || !stricmp(encoding, "utf-8"))
416 {
bc3e2810 417 ps->encodings.add(LP_FCP_UTF8);
6646dca4
VK
418 }
419 else if (!stricmp(encoding, "ucs2") || !stricmp(encoding, "ucs-2") || !stricmp(encoding, "utf-16"))
420 {
bc3e2810 421 ps->encodings.add(LP_FCP_UCS2);
6646dca4 422 }
0ea005dd
EJ
423 else if (!stricmp(encoding, "ucs2le") || !stricmp(encoding, "ucs-2le") || !stricmp(encoding, "utf-16le"))
424 {
bc3e2810 425 ps->encodings.add(LP_FCP_UCS2_LE);
0ea005dd
EJ
426 }
427 else if (!stricmp(encoding, "ucs2be") || !stricmp(encoding, "ucs-2be") || !stricmp(encoding, "utf-16be"))
428 {
bc3e2810 429 ps->encodings.add(LP_FCP_UCS2_BE);
0ea005dd 430 }
6646dca4
VK
431 else if (!stricmp(encoding, "ucs4") || !stricmp(encoding, "ucs-4") || !stricmp(encoding, "utf-32"))
432 {
bc3e2810 433 ps->encodings.add(LP_FCP_UCS4);
6646dca4 434 }
0ea005dd
EJ
435 else if (!stricmp(encoding, "ucs4le") || !stricmp(encoding, "ucs-4le") || !stricmp(encoding, "utf-32le"))
436 {
bc3e2810 437 ps->encodings.add(LP_FCP_UCS4_LE);
0ea005dd
EJ
438 }
439 else if (!stricmp(encoding, "ucs4be") || !stricmp(encoding, "ucs-4be") || !stricmp(encoding, "utf-32be"))
440 {
bc3e2810 441 ps->encodings.add(LP_FCP_UCS4_BE);
0ea005dd 442 }
6646dca4
VK
443 else
444 {
445 ps->errorText = _T("Invalid file encoding");
446 ps->state = XML_STATE_ERROR;
447 }
448 }
449 else
450 {
bc3e2810 451 ps->encodings.add(LP_FCP_AUTO);
6646dca4 452 }
bc3e2810 453 ps->preallocFlags.add(XMLGetAttrBoolean(attrs, "preallocated", false) ? 1 : 0);
f4181253
VK
454 ps->snapshotFlags.add(XMLGetAttrBoolean(attrs, "snapshot", false) ? 1 : 0);
455 }
5039dede
AK
456 else if (!strcmp(name, "macros"))
457 {
458 ps->state = XML_STATE_MACROS;
459 }
460 else if (!strcmp(name, "macro"))
461 {
462 const char *name;
463
464 ps->state = XML_STATE_MACRO;
465 name = XMLGetAttr(attrs, "name");
6646dca4
VK
466#ifdef UNICODE
467 ps->macroName = L"";
4e0e77e6 468 ps->macroName.appendMBString(name, strlen(name), CP_UTF8);
6646dca4 469#else
5039dede 470 ps->macroName = CHECK_NULL_A(name);
6646dca4 471#endif
5039dede
AK
472 ps->macro = NULL;
473 }
474 else if (!strcmp(name, "rules"))
475 {
476 ps->state = XML_STATE_RULES;
477 }
478 else if (!strcmp(name, "rule"))
479 {
480 ps->regexp = NULL;
6d738067 481 ps->invertedRule = false;
5039dede
AK
482 ps->event = NULL;
483 ps->context = NULL;
484 ps->contextAction = CONTEXT_SET_AUTOMATIC;
485 ps->description = NULL;
6d738067
VK
486 ps->id = NULL;
487 ps->source = NULL;
488 ps->level = NULL;
6646dca4 489#ifdef UNICODE
839931d3 490 ps->ruleContext.clear();
6646dca4
VK
491 const char *context = XMLGetAttr(attrs, "context");
492 if (context != NULL)
4e0e77e6 493 ps->ruleContext.appendMBString(context, strlen(context), CP_UTF8);
839931d3
VK
494 ps->ruleName.clear();
495 const char *name = XMLGetAttr(attrs, "name");
496 if (name != NULL)
497 ps->ruleName.appendMBString(name, strlen(name), CP_UTF8);
6646dca4 498#else
5039dede 499 ps->ruleContext = XMLGetAttr(attrs, "context");
839931d3 500 ps->ruleName = XMLGetAttr(attrs, "name");
6646dca4 501#endif
6d738067 502 ps->breakFlag = XMLGetAttrBoolean(attrs, "break", false);
5039dede 503 ps->state = XML_STATE_RULE;
9eb288e6 504 ps->numEventParams = 0;
5039dede
AK
505 }
506 else if (!strcmp(name, "match"))
507 {
508 ps->state = XML_STATE_MATCH;
6d738067 509 ps->invertedRule = XMLGetAttrBoolean(attrs, "invert", false);
659bf814 510 ps->resetRepeat = XMLGetAttrBoolean(attrs, "reset", true);
511 ps->repeatCount = XMLGetAttrInt(attrs, "repeatCount", 0);
512 ps->repeatInterval = XMLGetAttrInt(attrs, "repeatInterval", 0);
5039dede 513 }
d43aee56 514 else if (!strcmp(name, "id") || !strcmp(name, "facility"))
5039dede
AK
515 {
516 ps->state = XML_STATE_ID;
517 }
d43aee56 518 else if (!strcmp(name, "level") || !strcmp(name, "severity"))
5039dede
AK
519 {
520 ps->state = XML_STATE_LEVEL;
521 }
d43aee56 522 else if (!strcmp(name, "source") || !strcmp(name, "tag"))
5039dede
AK
523 {
524 ps->state = XML_STATE_SOURCE;
525 }
526 else if (!strcmp(name, "event"))
527 {
967893bb 528 ps->numEventParams = XMLGetAttrUINT32(attrs, "params", 0);
5039dede
AK
529 ps->state = XML_STATE_EVENT;
530 }
531 else if (!strcmp(name, "context"))
532 {
533 const char *action;
659bf814 534
5039dede 535 ps->state = XML_STATE_CONTEXT;
659bf814 536
5039dede
AK
537 action = XMLGetAttr(attrs, "action");
538 if (action == NULL)
539 action = "set";
659bf814 540
5039dede
AK
541 if (!strcmp(action, "set"))
542 {
543 const char *mode;
544
545 mode = XMLGetAttr(attrs, "reset");
546 if (mode == NULL)
547 mode = "auto";
548
549 if (!strcmp(mode, "auto"))
550 {
551 ps->contextAction = CONTEXT_SET_AUTOMATIC;
659bf814 552 }
5039dede
AK
553 else if (!strcmp(mode, "manual"))
554 {
555 ps->contextAction = CONTEXT_SET_MANUAL;
659bf814 556 }
5039dede
AK
557 else
558 {
559 ps->errorText = _T("Invalid context reset mode");
560 ps->state = XML_STATE_ERROR;
561 }
562 }
563 else if (!strcmp(action, "clear"))
564 {
565 ps->contextAction = CONTEXT_CLEAR;
566 }
567 else
568 {
569 ps->errorText = _T("Invalid context action");
570 ps->state = XML_STATE_ERROR;
571 }
572 }
573 else if (!strcmp(name, "description"))
574 {
575 ps->state = XML_STATE_DESCRIPTION;
576 }
073283c5
VK
577 else if (!strcmp(name, "exclusionSchedules"))
578 {
579 ps->state = XML_STATE_EXCLUSION_SCHEDULES;
580 }
581 else if (!strcmp(name, "schedule"))
582 {
583 ps->state = XML_STATE_EXCLUSION_SCHEDULE;
584 }
5039dede
AK
585 else
586 {
587 ps->state = XML_STATE_ERROR;
588 }
589}
590
66ee0102
VK
591/**
592 * Create parser configuration from XML - callback for element end
593 */
5039dede
AK
594static void EndElement(void *userData, const char *name)
595{
596 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
597
0ea005dd
EJ
598 if (ps->state == XML_STATE_ERROR)
599 return;
600
5039dede
AK
601 if (!strcmp(name, "parser"))
602 {
603 ps->state = XML_STATE_END;
604 }
605 else if (!strcmp(name, "file"))
606 {
721fc691 607 ps->files.add(ps->file);
dedeffb3 608 ps->file.clear();
5039dede
AK
609 ps->state = XML_STATE_PARSER;
610 }
611 else if (!strcmp(name, "macros"))
612 {
613 ps->state = XML_STATE_PARSER;
614 }
615 else if (!strcmp(name, "macro"))
616 {
6d738067 617 ps->parser->addMacro(ps->macroName, ps->macro);
dedeffb3
VK
618 ps->macroName.clear();
619 ps->macro.clear();
5039dede
AK
620 ps->state = XML_STATE_MACROS;
621 }
622 else if (!strcmp(name, "rules"))
623 {
624 ps->state = XML_STATE_PARSER;
625 }
626 else if (!strcmp(name, "rule"))
627 {
da623f8b 628 UINT32 eventCode;
2dd24569 629 const TCHAR *eventName = NULL;
6646dca4 630 TCHAR *eptr;
5039dede
AK
631 LogParserRule *rule;
632
0bbab9d3 633 ps->event.trim();
6646dca4 634 eventCode = _tcstoul(ps->event, &eptr, 0);
5039dede 635 if (*eptr != 0)
2dd24569
VK
636 {
637 eventCode = ps->parser->resolveEventName(ps->event, 0);
638 if (eventCode == 0)
639 {
6646dca4 640 eventName = (const TCHAR *)ps->event;
2dd24569
VK
641 }
642 }
659bf814 643
ce7565e7 644 if (ps->regexp.isEmpty())
d43aee56 645 ps->regexp = _T(".*");
839931d3 646 rule = new LogParserRule(ps->parser, (const TCHAR *)ps->ruleName, (const TCHAR *)ps->regexp, eventCode, eventName, ps->numEventParams, ps->repeatInterval, ps->repeatCount, ps->resetRepeat);
ce7565e7 647 if (!ps->ruleContext.isEmpty())
6d738067 648 rule->setContext(ps->ruleContext);
ce7565e7 649 if (!ps->context.isEmpty())
5039dede 650 {
6d738067
VK
651 rule->setContextToChange(ps->context);
652 rule->setContextAction(ps->contextAction);
5039dede 653 }
6d738067 654
ce7565e7 655 if (!ps->description.isEmpty())
6d738067 656 rule->setDescription(ps->description);
659bf814 657
ce7565e7 658 if (!ps->source.isEmpty())
6d738067
VK
659 rule->setSource(ps->source);
660
ce7565e7 661 if (!ps->level.isEmpty())
6d738067
VK
662 rule->setLevel(_tcstoul(ps->level, NULL, 0));
663
ce7565e7 664 if (!ps->id.isEmpty())
6d738067 665 {
da623f8b 666 UINT32 start, end;
6d738067
VK
667 TCHAR *eptr;
668
6646dca4 669 start = _tcstoul(ps->id, &eptr, 0);
6d738067
VK
670 if (*eptr == 0)
671 {
672 end = start;
673 }
674 else /* TODO: add better error handling */
675 {
676 while(!_istdigit(*eptr))
677 eptr++;
6646dca4 678 end = _tcstoul(eptr, NULL, 0);
6d738067
VK
679 }
680 rule->setIdRange(start, end);
681 }
682
683 rule->setInverted(ps->invertedRule);
684 rule->setBreakFlag(ps->breakFlag);
685
686 ps->parser->addRule(rule);
5039dede
AK
687 ps->state = XML_STATE_RULES;
688 }
689 else if (!strcmp(name, "match"))
690 {
691 ps->state = XML_STATE_RULE;
692 }
d43aee56 693 else if (!strcmp(name, "id") || !strcmp(name, "facility"))
5039dede
AK
694 {
695 ps->state = XML_STATE_RULE;
696 }
d43aee56 697 else if (!strcmp(name, "level") || !strcmp(name, "severity"))
5039dede
AK
698 {
699 ps->state = XML_STATE_RULE;
700 }
d43aee56 701 else if (!strcmp(name, "source") || !strcmp(name, "tag"))
5039dede
AK
702 {
703 ps->state = XML_STATE_RULE;
704 }
705 else if (!strcmp(name, "event"))
706 {
707 ps->state = XML_STATE_RULE;
708 }
709 else if (!strcmp(name, "context"))
710 {
711 ps->state = XML_STATE_RULE;
712 }
713 else if (!strcmp(name, "description"))
714 {
715 ps->state = XML_STATE_RULE;
716 }
073283c5
VK
717 else if (!strcmp(name, "exclusionSchedules"))
718 {
719 ps->state = XML_STATE_PARSER;
720 }
721 else if (!strcmp(name, "schedule"))
722 {
723 ps->parser->addExclusionSchedule(ps->schedule);
dedeffb3 724 ps->schedule.clear();
073283c5
VK
725 ps->state = XML_STATE_EXCLUSION_SCHEDULES;
726 }
5039dede
AK
727}
728
66ee0102
VK
729/**
730 * Create parser configuration from XML - callback for element data
731 */
5039dede
AK
732static void CharData(void *userData, const XML_Char *s, int len)
733{
734 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
735
736 switch(ps->state)
737 {
738 case XML_STATE_MATCH:
4e0e77e6 739 ps->regexp.appendMBString(s, len, CP_UTF8);
5039dede
AK
740 break;
741 case XML_STATE_ID:
4e0e77e6 742 ps->id.appendMBString(s, len, CP_UTF8);
5039dede
AK
743 break;
744 case XML_STATE_LEVEL:
4e0e77e6 745 ps->level.appendMBString(s, len, CP_UTF8);
5039dede
AK
746 break;
747 case XML_STATE_SOURCE:
4e0e77e6 748 ps->source.appendMBString(s, len, CP_UTF8);
5039dede
AK
749 break;
750 case XML_STATE_EVENT:
4e0e77e6 751 ps->event.appendMBString(s, len, CP_UTF8);
5039dede
AK
752 break;
753 case XML_STATE_FILE:
4e0e77e6 754 ps->file.appendMBString(s, len, CP_UTF8);
5039dede
AK
755 break;
756 case XML_STATE_CONTEXT:
4e0e77e6 757 ps->context.appendMBString(s, len, CP_UTF8);
5039dede
AK
758 break;
759 case XML_STATE_DESCRIPTION:
4e0e77e6 760 ps->description.appendMBString(s, len, CP_UTF8);
5039dede
AK
761 break;
762 case XML_STATE_MACRO:
4e0e77e6 763 ps->macro.appendMBString(s, len, CP_UTF8);
5039dede 764 break;
073283c5
VK
765 case XML_STATE_EXCLUSION_SCHEDULE:
766 ps->schedule.appendMBString(s, len, CP_UTF8);
767 break;
5039dede
AK
768 default:
769 break;
770 }
771}
772
66ee0102
VK
773/**
774 * Create parser configuration from XML. Returns array of identical parsers,
775 * one for each <file> tag in source XML. Resulting parsers only differs with file names.
776 */
da623f8b 777ObjectArray<LogParser> *LogParser::createFromXml(const char *xml, int xmlLen, TCHAR *errorText, int errBufSize, bool (*eventResolver)(const TCHAR *, UINT32 *))
5039dede 778{
271c3551 779 ObjectArray<LogParser> *parsers = NULL;
5039dede 780
66ee0102
VK
781 XML_Parser parser = XML_ParserCreate(NULL);
782 XML_PARSER_STATE state;
783 state.parser = new LogParser;
271c3551 784 state.parser->setEventNameResolver(eventResolver);
66ee0102
VK
785 XML_SetUserData(parser, &state);
786 XML_SetElementHandler(parser, StartElement, EndElement);
787 XML_SetCharacterDataHandler(parser, CharData);
2266fe84 788 bool success = (XML_Parse(parser, xml, (xmlLen == -1) ? (int)strlen(xml) : xmlLen, TRUE) != XML_STATUS_ERROR);
66ee0102
VK
789 if (!success && (errorText != NULL))
790 {
791 _sntprintf(errorText, errBufSize, _T("%hs at line %d"),
792 XML_ErrorString(XML_GetErrorCode(parser)),
793 (int)XML_GetCurrentLineNumber(parser));
794 }
795 XML_ParserFree(parser);
796 if (success && (state.state == XML_STATE_ERROR))
797 {
66ee0102 798 if (errorText != NULL)
271c3551 799 nx_strncpy(errorText, state.errorText, errBufSize);
66ee0102
VK
800 }
801 else if (success)
659bf814 802 {
271c3551 803 parsers = new ObjectArray<LogParser>;
a6312bd6 804 if (state.files.size() > 0)
565a8f28 805 {
a6312bd6 806 for(int i = 0; i < state.files.size(); i++)
271c3551
VK
807 {
808 LogParser *p = (i > 0) ? new LogParser(state.parser) : state.parser;
a6312bd6 809 p->setFileName(state.files.get(i));
bc3e2810
VK
810 p->m_fileEncoding = state.encodings.get(i);
811 p->m_preallocatedFile = (state.preallocFlags.get(i) != 0);
f4181253
VK
812#ifdef _WIN32
813 p->m_useSnapshot = (state.snapshotFlags.get(i) != 0);
814#endif
271c3551
VK
815 parsers->add(p);
816 }
817 }
818 else
819 {
820 // It is possible to have parser without <file> tag, syslog parser for example
821 parsers->add(state.parser);
565a8f28 822 }
5039dede 823 }
565a8f28
AK
824
825 return parsers;
5039dede
AK
826}
827
66ee0102
VK
828/**
829 * Resolve event name
830 */
da623f8b 831UINT32 LogParser::resolveEventName(const TCHAR *name, UINT32 defVal)
5039dede
AK
832{
833 if (m_eventNameList != NULL)
834 {
835 int i;
836
837 for(i = 0; m_eventNameList[i].text != NULL; i++)
838 if (!_tcsicmp(name, m_eventNameList[i].text))
839 return m_eventNameList[i].code;
840 }
841
842 if (m_eventResolver != NULL)
843 {
da623f8b 844 UINT32 val;
5039dede
AK
845
846 if (m_eventResolver(name, &val))
847 return val;
848 }
849
850 return defVal;
851}
839931d3
VK
852
853/**
854 * Find rule by name
855 */
856const LogParserRule *LogParser::findRuleByName(const TCHAR *name) const
857{
1df332d8 858 for(int i = 0; i < m_rules->size(); i++)
839931d3 859 {
1df332d8 860 LogParserRule *rule = m_rules->get(i);
bc3e2810
VK
861 if (!_tcsicmp(rule->getName(), name))
862 return rule;
839931d3
VK
863 }
864 return NULL;
865}
29b701c3
VK
866
867/**
868 * Restore counters from previous parser copy
869 */
870void LogParser::restoreCounters(const LogParser *parser)
871{
1df332d8 872 for(int i = 0; i < m_rules->size(); i++)
29b701c3 873 {
1df332d8 874 const LogParserRule *rule = parser->findRuleByName(m_rules->get(i)->getName());
29b701c3
VK
875 if (rule != NULL)
876 {
1df332d8 877 m_rules->get(i)->restoreCounters(rule);
29b701c3
VK
878 }
879 }
880}
bc63d017
VK
881
882/**
883 * Get character size in bytes for parser's encoding
884 */
885int LogParser::getCharSize() const
886{
887 switch(m_fileEncoding)
888 {
889 case LP_FCP_UCS4_BE:
890 case LP_FCP_UCS4_LE:
891 case LP_FCP_UCS4:
892 return 4;
893 case LP_FCP_UCS2_BE:
894 case LP_FCP_UCS2_LE:
895 case LP_FCP_UCS2:
896 return 2;
897 default:
898 return 1;
899 }
900}
073283c5
VK
901
902/**
903 * Check for exclusion period
904 */
905bool LogParser::isExclusionPeriod()
906{
4d8c4cf3
VK
907 if (m_suspended)
908 return true;
909
073283c5
VK
910 if (m_exclusionSchedules.isEmpty())
911 return false;
912
073283c5
VK
913 time_t now = time(NULL);
914 struct tm localTime;
915#if HAVE_LOCALTIME_R
916 localtime_r(&now, &localTime);
917#else
918 memcpy(&localTime, localtime(&now), sizeof(struct tm));
919#endif
920 for(int i = 0; i < m_exclusionSchedules.size(); i++)
921 {
922 if (MatchSchedule(m_exclusionSchedules.get(i), &localTime, now))
923 return true;
924 }
925 return false;
926}
4d8c4cf3
VK
927
928/**
929 * Suspend parser
930 */
931void LogParser::suspend()
932{
933 m_suspended = true;
934}
935
936/**
937 * Resume parser
938 */
939void LogParser::resume()
940{
941 m_suspended = false;
942}
268105f1
VK
943
944/**
945 * Get status text
946 */
947const TCHAR *LogParser::getStatusText() const
948{
949 static const TCHAR *texts[] = {
950 _T("INIT"),
951 _T("RUNNING"),
952 _T("FILE MISSING"),
953 _T("FILE OPEN ERROR"),
954 _T("SUSPENDED"),
955 _T("EVENT LOG SUBSCRIBE FAILED"),
956 _T("EVENT LOG READ ERROR"),
f4181253
VK
957 _T("EVENT LOG OPEN ERROR"),
958 _T("VSS FAILURE")
268105f1
VK
959 };
960 return texts[m_status];
961}