implemented log file monitoring using Windows VSS snapshots
[public/netxms.git] / src / libnxlp / parser.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Log Parsing Library
4 ** Copyright (C) 2003-2017 Raden Solutions
5 **
6 ** This program is free software; you can redistribute it and/or modify
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
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 **
16 ** You should have received a copy of the GNU Lesser General Public License
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"
25 #include <expat.h>
26
27 /**
28 * Context state texts
29 */
30 static const TCHAR *s_states[] = { _T("MANUAL"), _T("AUTO"), _T("INACTIVE") };
31
32 /**
33 * XML parser state codes
34 */
35 enum 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 };
56
57 /**
58 * XML parser state for creating LogParser object from XML
59 */
60 struct XML_PARSER_STATE
61 {
62 LogParser *parser;
63 ParserState state;
64 String regexp;
65 String event;
66 String file;
67 StringList files;
68 IntegerArray<INT32> encodings;
69 IntegerArray<INT32> preallocFlags;
70 IntegerArray<INT32> snapshotFlags;
71 String id;
72 String level;
73 String source;
74 String context;
75 String description;
76 String ruleName;
77 int contextAction;
78 String ruleContext;
79 int numEventParams;
80 String errorText;
81 String macroName;
82 String macro;
83 String schedule;
84 bool invertedRule;
85 bool breakFlag;
86 int repeatCount;
87 int repeatInterval;
88 bool resetRepeat;
89
90 XML_PARSER_STATE() : encodings(4, 4), preallocFlags(4, 4), snapshotFlags(4, 4)
91 {
92 state = XML_STATE_INIT;
93 parser = NULL;
94 invertedRule = false;
95 breakFlag = false;
96 contextAction = CONTEXT_SET_AUTOMATIC;
97 numEventParams = 0;
98 repeatCount = 0;
99 repeatInterval = 0;
100 resetRepeat = true;
101 }
102 };
103
104 /**
105 * Parser default constructor
106 */
107 LogParser::LogParser()
108 {
109 m_rules = new ObjectArray<LogParserRule>(16, 16, true);
110 m_cb = NULL;
111 m_userArg = NULL;
112 m_name = NULL;
113 m_fileName = NULL;
114 m_fileEncoding = LP_FCP_ACP;
115 m_preallocatedFile = false;
116 m_eventNameList = NULL;
117 m_eventResolver = NULL;
118 m_thread = INVALID_THREAD_HANDLE;
119 m_recordsProcessed = 0;
120 m_recordsMatched = 0;
121 m_processAllRules = false;
122 m_suspended = false;
123 m_traceLevel = 0;
124 m_traceCallback = NULL;
125 m_status = LPS_INIT;
126 #ifdef _WIN32
127 m_marker = NULL;
128 #endif
129 }
130
131 /**
132 * Parser copy constructor
133 */
134 LogParser::LogParser(const LogParser *src)
135 {
136 int count = src->m_rules->size();
137 m_rules = new ObjectArray<LogParserRule>(count, 16, true);
138 for(int i = 0; i < count; i++)
139 m_rules->add(new LogParserRule(src->m_rules->get(i), this));
140
141 m_macros.addAll(&src->m_macros);
142 m_contexts.addAll(&src->m_contexts);
143 m_exclusionSchedules.addAll(&src->m_exclusionSchedules);
144
145 m_cb = src->m_cb;
146 m_userArg = src->m_userArg;
147 m_name = _tcsdup_ex(src->m_name);
148 m_fileName = _tcsdup_ex(src->m_fileName);
149 m_fileEncoding = src->m_fileEncoding;
150 m_preallocatedFile = src->m_preallocatedFile;
151
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;
168 m_suspended = src->m_suspended;
169 m_traceLevel = src->m_traceLevel;
170 m_traceCallback = src->m_traceCallback;
171 m_status = LPS_INIT;
172 #ifdef _WIN32
173 m_marker = _tcsdup_ex(src->m_marker);
174 #endif
175 }
176
177 /**
178 * Destructor
179 */
180 LogParser::~LogParser()
181 {
182 delete m_rules;
183 free(m_name);
184 free(m_fileName);
185 #ifdef _WIN32
186 free(m_marker);
187 #endif
188 }
189
190 /**
191 * Trace
192 */
193 void LogParser::trace(int level, const TCHAR *format, ...)
194 {
195 va_list args;
196
197 if ((m_traceCallback == NULL) || (level > m_traceLevel))
198 return;
199
200 va_start(args, format);
201 m_traceCallback(level, format, args);
202 va_end(args);
203 }
204
205 /**
206 * Add rule
207 */
208 bool LogParser::addRule(LogParserRule *rule)
209 {
210 bool valid = rule->isValid();
211 if (valid)
212 {
213 m_rules->add(rule);
214 }
215 else
216 {
217 delete rule;
218 }
219 return valid;
220 }
221
222 /**
223 * Create and add rule
224 */
225 bool LogParser::addRule(const TCHAR *regexp, UINT32 eventCode, const TCHAR *eventName, int numParams, int repeatInterval, int repeatCount, bool resetRepeat)
226 {
227 return addRule(new LogParserRule(this, NULL, regexp, eventCode, eventName, numParams, repeatInterval, repeatCount, resetRepeat));
228 }
229
230 /**
231 * Check context
232 */
233 const TCHAR *LogParser::checkContext(LogParserRule *rule)
234 {
235 const TCHAR *state;
236
237 if (rule->getContext() == NULL)
238 {
239 trace(5, _T(" rule has no context"));
240 return s_states[CONTEXT_SET_MANUAL];
241 }
242
243 state = m_contexts.get(rule->getContext());
244 if (state == NULL)
245 {
246 trace(5, _T(" context '%s' inactive, rule should be skipped"), rule->getContext());
247 return NULL; // Context inactive, don't use this rule
248 }
249
250 if (!_tcscmp(state, s_states[CONTEXT_CLEAR]))
251 {
252 trace(5, _T(" context '%s' inactive, rule should be skipped"), rule->getContext());
253 return NULL;
254 }
255 else
256 {
257 trace(5, _T(" context '%s' active (mode=%s)"), rule->getContext(), state);
258 return state;
259 }
260 }
261
262 /**
263 * Match log record
264 */
265 bool LogParser::matchLogRecord(bool hasAttributes, const TCHAR *source, UINT32 eventId,
266 UINT32 level, const TCHAR *line, UINT32 objectId)
267 {
268 const TCHAR *state;
269 bool matched = false;
270
271 if (hasAttributes)
272 trace(5, _T("Match event: source=\"%s\" id=%u level=%d text=\"%s\""), source, eventId, level, line);
273 else
274 trace(5, _T("Match line: \"%s\""), line);
275
276 m_recordsProcessed++;
277 int i;
278 for(i = 0; i < m_rules->size(); i++)
279 {
280 LogParserRule *rule = m_rules->get(i);
281 trace(6, _T("checking rule %d \"%s\""), i + 1, rule->getDescription());
282 if ((state = checkContext(rule)) != NULL)
283 {
284 bool ruleMatched = hasAttributes ?
285 rule->matchEx(source, eventId, level, line, m_cb, objectId, m_userArg) :
286 rule->match(line, m_cb, objectId, m_userArg);
287 if (ruleMatched)
288 {
289 trace(5, _T("rule %d \"%s\" matched"), i + 1, rule->getDescription());
290 if (!matched)
291 m_recordsMatched++;
292
293 // Update context
294 if (rule->getContextToChange() != NULL)
295 {
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()]);
299 }
300
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 {
304 m_contexts.set(rule->getContext(), s_states[CONTEXT_CLEAR]);
305 trace(5, _T("rule %d \"%s\": context %s cleared because it was set to automatic reset mode"),
306 i + 1, rule->getDescription(), rule->getContext());
307 }
308 matched = true;
309 if (!m_processAllRules || rule->getBreakFlag())
310 break;
311 }
312 }
313 }
314 if (i < m_rules->size())
315 trace(5, _T("processing stopped at rule %d \"%s\"; result = %s"), i + 1,
316 m_rules->get(i)->getDescription(), matched ? _T("true") : _T("false"));
317 else
318 trace(5, _T("Processing stopped at end of rules list; result = %s"), matched ? _T("true") : _T("false"));
319 return matched;
320 }
321
322 /**
323 * Match simple log line
324 */
325 bool LogParser::matchLine(const TCHAR *line, UINT32 objectId)
326 {
327 return matchLogRecord(false, NULL, 0, 0, line, objectId);
328 }
329
330 /**
331 * Match log event (text with additional attributes - source, severity, event id)
332 */
333 bool LogParser::matchEvent(const TCHAR *source, UINT32 eventId, UINT32 level, const TCHAR *line, UINT32 objectId)
334 {
335 return matchLogRecord(true, source, eventId, level, line, objectId);
336 }
337
338 /**
339 * Set associated file name
340 */
341 void LogParser::setFileName(const TCHAR *name)
342 {
343 free(m_fileName);
344 m_fileName = (name != NULL) ? _tcsdup(name) : NULL;
345 if (m_name == NULL)
346 m_name = _tcsdup(name); // Set parser name to file name
347 }
348
349 /**
350 * Set parser name
351 */
352 void LogParser::setName(const TCHAR *name)
353 {
354 safe_free(m_name);
355 m_name = _tcsdup((name != NULL) ? name : CHECK_NULL(m_fileName));
356 }
357
358 /**
359 * Add macro
360 */
361 void LogParser::addMacro(const TCHAR *name, const TCHAR *value)
362 {
363 m_macros.set(name, value);
364 }
365
366 /**
367 * Get macro
368 */
369 const TCHAR *LogParser::getMacro(const TCHAR *name)
370 {
371 const TCHAR *value;
372
373 value = m_macros.get(name);
374 return CHECK_NULL_EX(value);
375 }
376
377 /**
378 * Create parser configuration from XML - callback for element start
379 */
380 static 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;
387 ps->parser->setProcessAllFlag(XMLGetAttrBoolean(attrs, "processAll", false));
388 ps->parser->setTraceLevel(XMLGetAttrInt(attrs, "trace", 0));
389 const char *name = XMLGetAttr(attrs, "name");
390 if (name != NULL)
391 {
392 #ifdef UNICODE
393 WCHAR *wname = WideStringFromUTF8String(name);
394 ps->parser->setName(wname);
395 free(wname);
396 #else
397 ps->parser->setName(name);
398 #endif
399 }
400 }
401 else if (!strcmp(name, "file"))
402 {
403 ps->state = XML_STATE_FILE;
404 const char *encoding = XMLGetAttr(attrs, "encoding");
405 if (encoding != NULL)
406 {
407 if ((*encoding == 0))
408 {
409 ps->encodings.add(LP_FCP_AUTO);
410 }
411 if (!stricmp(encoding, "acp"))
412 {
413 ps->encodings.add(LP_FCP_ACP);
414 }
415 else if (!stricmp(encoding, "utf8") || !stricmp(encoding, "utf-8"))
416 {
417 ps->encodings.add(LP_FCP_UTF8);
418 }
419 else if (!stricmp(encoding, "ucs2") || !stricmp(encoding, "ucs-2") || !stricmp(encoding, "utf-16"))
420 {
421 ps->encodings.add(LP_FCP_UCS2);
422 }
423 else if (!stricmp(encoding, "ucs2le") || !stricmp(encoding, "ucs-2le") || !stricmp(encoding, "utf-16le"))
424 {
425 ps->encodings.add(LP_FCP_UCS2_LE);
426 }
427 else if (!stricmp(encoding, "ucs2be") || !stricmp(encoding, "ucs-2be") || !stricmp(encoding, "utf-16be"))
428 {
429 ps->encodings.add(LP_FCP_UCS2_BE);
430 }
431 else if (!stricmp(encoding, "ucs4") || !stricmp(encoding, "ucs-4") || !stricmp(encoding, "utf-32"))
432 {
433 ps->encodings.add(LP_FCP_UCS4);
434 }
435 else if (!stricmp(encoding, "ucs4le") || !stricmp(encoding, "ucs-4le") || !stricmp(encoding, "utf-32le"))
436 {
437 ps->encodings.add(LP_FCP_UCS4_LE);
438 }
439 else if (!stricmp(encoding, "ucs4be") || !stricmp(encoding, "ucs-4be") || !stricmp(encoding, "utf-32be"))
440 {
441 ps->encodings.add(LP_FCP_UCS4_BE);
442 }
443 else
444 {
445 ps->errorText = _T("Invalid file encoding");
446 ps->state = XML_STATE_ERROR;
447 }
448 }
449 else
450 {
451 ps->encodings.add(LP_FCP_AUTO);
452 }
453 ps->preallocFlags.add(XMLGetAttrBoolean(attrs, "preallocated", false) ? 1 : 0);
454 ps->snapshotFlags.add(XMLGetAttrBoolean(attrs, "snapshot", false) ? 1 : 0);
455 }
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");
466 #ifdef UNICODE
467 ps->macroName = L"";
468 ps->macroName.appendMBString(name, strlen(name), CP_UTF8);
469 #else
470 ps->macroName = CHECK_NULL_A(name);
471 #endif
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;
481 ps->invertedRule = false;
482 ps->event = NULL;
483 ps->context = NULL;
484 ps->contextAction = CONTEXT_SET_AUTOMATIC;
485 ps->description = NULL;
486 ps->id = NULL;
487 ps->source = NULL;
488 ps->level = NULL;
489 #ifdef UNICODE
490 ps->ruleContext.clear();
491 const char *context = XMLGetAttr(attrs, "context");
492 if (context != NULL)
493 ps->ruleContext.appendMBString(context, strlen(context), CP_UTF8);
494 ps->ruleName.clear();
495 const char *name = XMLGetAttr(attrs, "name");
496 if (name != NULL)
497 ps->ruleName.appendMBString(name, strlen(name), CP_UTF8);
498 #else
499 ps->ruleContext = XMLGetAttr(attrs, "context");
500 ps->ruleName = XMLGetAttr(attrs, "name");
501 #endif
502 ps->breakFlag = XMLGetAttrBoolean(attrs, "break", false);
503 ps->state = XML_STATE_RULE;
504 ps->numEventParams = 0;
505 }
506 else if (!strcmp(name, "match"))
507 {
508 ps->state = XML_STATE_MATCH;
509 ps->invertedRule = XMLGetAttrBoolean(attrs, "invert", false);
510 ps->resetRepeat = XMLGetAttrBoolean(attrs, "reset", true);
511 ps->repeatCount = XMLGetAttrInt(attrs, "repeatCount", 0);
512 ps->repeatInterval = XMLGetAttrInt(attrs, "repeatInterval", 0);
513 }
514 else if (!strcmp(name, "id") || !strcmp(name, "facility"))
515 {
516 ps->state = XML_STATE_ID;
517 }
518 else if (!strcmp(name, "level") || !strcmp(name, "severity"))
519 {
520 ps->state = XML_STATE_LEVEL;
521 }
522 else if (!strcmp(name, "source") || !strcmp(name, "tag"))
523 {
524 ps->state = XML_STATE_SOURCE;
525 }
526 else if (!strcmp(name, "event"))
527 {
528 ps->numEventParams = XMLGetAttrUINT32(attrs, "params", 0);
529 ps->state = XML_STATE_EVENT;
530 }
531 else if (!strcmp(name, "context"))
532 {
533 const char *action;
534
535 ps->state = XML_STATE_CONTEXT;
536
537 action = XMLGetAttr(attrs, "action");
538 if (action == NULL)
539 action = "set";
540
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;
552 }
553 else if (!strcmp(mode, "manual"))
554 {
555 ps->contextAction = CONTEXT_SET_MANUAL;
556 }
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 }
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 }
585 else
586 {
587 ps->state = XML_STATE_ERROR;
588 }
589 }
590
591 /**
592 * Create parser configuration from XML - callback for element end
593 */
594 static void EndElement(void *userData, const char *name)
595 {
596 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
597
598 if (ps->state == XML_STATE_ERROR)
599 return;
600
601 if (!strcmp(name, "parser"))
602 {
603 ps->state = XML_STATE_END;
604 }
605 else if (!strcmp(name, "file"))
606 {
607 ps->files.add(ps->file);
608 ps->file.clear();
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 {
617 ps->parser->addMacro(ps->macroName, ps->macro);
618 ps->macroName.clear();
619 ps->macro.clear();
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 {
628 UINT32 eventCode;
629 const TCHAR *eventName = NULL;
630 TCHAR *eptr;
631 LogParserRule *rule;
632
633 ps->event.trim();
634 eventCode = _tcstoul(ps->event, &eptr, 0);
635 if (*eptr != 0)
636 {
637 eventCode = ps->parser->resolveEventName(ps->event, 0);
638 if (eventCode == 0)
639 {
640 eventName = (const TCHAR *)ps->event;
641 }
642 }
643
644 if (ps->regexp.isEmpty())
645 ps->regexp = _T(".*");
646 rule = new LogParserRule(ps->parser, (const TCHAR *)ps->ruleName, (const TCHAR *)ps->regexp, eventCode, eventName, ps->numEventParams, ps->repeatInterval, ps->repeatCount, ps->resetRepeat);
647 if (!ps->ruleContext.isEmpty())
648 rule->setContext(ps->ruleContext);
649 if (!ps->context.isEmpty())
650 {
651 rule->setContextToChange(ps->context);
652 rule->setContextAction(ps->contextAction);
653 }
654
655 if (!ps->description.isEmpty())
656 rule->setDescription(ps->description);
657
658 if (!ps->source.isEmpty())
659 rule->setSource(ps->source);
660
661 if (!ps->level.isEmpty())
662 rule->setLevel(_tcstoul(ps->level, NULL, 0));
663
664 if (!ps->id.isEmpty())
665 {
666 UINT32 start, end;
667 TCHAR *eptr;
668
669 start = _tcstoul(ps->id, &eptr, 0);
670 if (*eptr == 0)
671 {
672 end = start;
673 }
674 else /* TODO: add better error handling */
675 {
676 while(!_istdigit(*eptr))
677 eptr++;
678 end = _tcstoul(eptr, NULL, 0);
679 }
680 rule->setIdRange(start, end);
681 }
682
683 rule->setInverted(ps->invertedRule);
684 rule->setBreakFlag(ps->breakFlag);
685
686 ps->parser->addRule(rule);
687 ps->state = XML_STATE_RULES;
688 }
689 else if (!strcmp(name, "match"))
690 {
691 ps->state = XML_STATE_RULE;
692 }
693 else if (!strcmp(name, "id") || !strcmp(name, "facility"))
694 {
695 ps->state = XML_STATE_RULE;
696 }
697 else if (!strcmp(name, "level") || !strcmp(name, "severity"))
698 {
699 ps->state = XML_STATE_RULE;
700 }
701 else if (!strcmp(name, "source") || !strcmp(name, "tag"))
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 }
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);
724 ps->schedule.clear();
725 ps->state = XML_STATE_EXCLUSION_SCHEDULES;
726 }
727 }
728
729 /**
730 * Create parser configuration from XML - callback for element data
731 */
732 static 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:
739 ps->regexp.appendMBString(s, len, CP_UTF8);
740 break;
741 case XML_STATE_ID:
742 ps->id.appendMBString(s, len, CP_UTF8);
743 break;
744 case XML_STATE_LEVEL:
745 ps->level.appendMBString(s, len, CP_UTF8);
746 break;
747 case XML_STATE_SOURCE:
748 ps->source.appendMBString(s, len, CP_UTF8);
749 break;
750 case XML_STATE_EVENT:
751 ps->event.appendMBString(s, len, CP_UTF8);
752 break;
753 case XML_STATE_FILE:
754 ps->file.appendMBString(s, len, CP_UTF8);
755 break;
756 case XML_STATE_CONTEXT:
757 ps->context.appendMBString(s, len, CP_UTF8);
758 break;
759 case XML_STATE_DESCRIPTION:
760 ps->description.appendMBString(s, len, CP_UTF8);
761 break;
762 case XML_STATE_MACRO:
763 ps->macro.appendMBString(s, len, CP_UTF8);
764 break;
765 case XML_STATE_EXCLUSION_SCHEDULE:
766 ps->schedule.appendMBString(s, len, CP_UTF8);
767 break;
768 default:
769 break;
770 }
771 }
772
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 */
777 ObjectArray<LogParser> *LogParser::createFromXml(const char *xml, int xmlLen, TCHAR *errorText, int errBufSize, bool (*eventResolver)(const TCHAR *, UINT32 *))
778 {
779 ObjectArray<LogParser> *parsers = NULL;
780
781 XML_Parser parser = XML_ParserCreate(NULL);
782 XML_PARSER_STATE state;
783 state.parser = new LogParser;
784 state.parser->setEventNameResolver(eventResolver);
785 XML_SetUserData(parser, &state);
786 XML_SetElementHandler(parser, StartElement, EndElement);
787 XML_SetCharacterDataHandler(parser, CharData);
788 bool success = (XML_Parse(parser, xml, (xmlLen == -1) ? (int)strlen(xml) : xmlLen, TRUE) != XML_STATUS_ERROR);
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 {
798 if (errorText != NULL)
799 nx_strncpy(errorText, state.errorText, errBufSize);
800 }
801 else if (success)
802 {
803 parsers = new ObjectArray<LogParser>;
804 if (state.files.size() > 0)
805 {
806 for(int i = 0; i < state.files.size(); i++)
807 {
808 LogParser *p = (i > 0) ? new LogParser(state.parser) : state.parser;
809 p->setFileName(state.files.get(i));
810 p->m_fileEncoding = state.encodings.get(i);
811 p->m_preallocatedFile = (state.preallocFlags.get(i) != 0);
812 #ifdef _WIN32
813 p->m_useSnapshot = (state.snapshotFlags.get(i) != 0);
814 #endif
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);
822 }
823 }
824
825 return parsers;
826 }
827
828 /**
829 * Resolve event name
830 */
831 UINT32 LogParser::resolveEventName(const TCHAR *name, UINT32 defVal)
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 {
844 UINT32 val;
845
846 if (m_eventResolver(name, &val))
847 return val;
848 }
849
850 return defVal;
851 }
852
853 /**
854 * Find rule by name
855 */
856 const LogParserRule *LogParser::findRuleByName(const TCHAR *name) const
857 {
858 for(int i = 0; i < m_rules->size(); i++)
859 {
860 LogParserRule *rule = m_rules->get(i);
861 if (!_tcsicmp(rule->getName(), name))
862 return rule;
863 }
864 return NULL;
865 }
866
867 /**
868 * Restore counters from previous parser copy
869 */
870 void LogParser::restoreCounters(const LogParser *parser)
871 {
872 for(int i = 0; i < m_rules->size(); i++)
873 {
874 const LogParserRule *rule = parser->findRuleByName(m_rules->get(i)->getName());
875 if (rule != NULL)
876 {
877 m_rules->get(i)->restoreCounters(rule);
878 }
879 }
880 }
881
882 /**
883 * Get character size in bytes for parser's encoding
884 */
885 int 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 }
901
902 /**
903 * Check for exclusion period
904 */
905 bool LogParser::isExclusionPeriod()
906 {
907 if (m_suspended)
908 return true;
909
910 if (m_exclusionSchedules.isEmpty())
911 return false;
912
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 }
927
928 /**
929 * Suspend parser
930 */
931 void LogParser::suspend()
932 {
933 m_suspended = true;
934 }
935
936 /**
937 * Resume parser
938 */
939 void LogParser::resume()
940 {
941 m_suspended = false;
942 }
943
944 /**
945 * Get status text
946 */
947 const 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"),
957 _T("EVENT LOG OPEN ERROR"),
958 _T("VSS FAILURE")
959 };
960 return texts[m_status];
961 }