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