7bdf350c94aae3d66aa8ac79b77f9696a1f0de1d
[public/netxms.git] / src / libnxlp / rule.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Log Parsing Library
4 ** Copyright (C) 2003-2016 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: rule.cpp
21 **
22 **/
23
24 #include "libnxlp.h"
25
26 /**
27 * Constructor
28 */
29 LogParserRule::LogParserRule(LogParser *parser, const TCHAR *regexp, UINT32 eventCode, const TCHAR *eventName,
30 int numParams, int repeatInterval, int repeatCount, bool resetRepeat, const TCHAR *source,
31 UINT32 level, UINT32 idStart, UINT32 idEnd)
32 {
33 String expandedRegexp;
34
35 m_parser = parser;
36 expandMacros(regexp, expandedRegexp);
37 m_regexp = _tcsdup(expandedRegexp);
38 m_isValid = (_tregcomp(&m_preg, expandedRegexp, REG_EXTENDED | REG_ICASE) == 0);
39 m_eventCode = eventCode;
40 m_eventName = (eventName != NULL) ? _tcsdup(eventName) : NULL;
41 m_numParams = numParams;
42 m_pmatch = (numParams > 0) ? (regmatch_t *)malloc(sizeof(regmatch_t) * (numParams + 1)) : NULL;
43 m_source = (source != NULL) ? _tcsdup(source) : NULL;
44 m_level = level;
45 m_idStart = idStart;
46 m_idEnd = idEnd;
47 m_context = NULL;
48 m_contextAction = 0;
49 m_contextToChange = NULL;
50 m_isInverted = FALSE;
51 m_breakOnMatch = FALSE;
52 m_description = NULL;
53 m_repeatInterval = repeatInterval;
54 m_repeatCount = repeatCount;
55 m_matchArray = new IntegerArray<time_t>();
56 m_resetRepeat = resetRepeat;
57 }
58
59 /**
60 * Copy constructor
61 */
62 LogParserRule::LogParserRule(LogParserRule *src, LogParser *parser)
63 {
64 m_parser = parser;
65 m_regexp = _tcsdup(src->m_regexp);
66 m_isValid = (_tregcomp(&m_preg, m_regexp, REG_EXTENDED | REG_ICASE) == 0);
67 m_eventCode = src->m_eventCode;
68 m_eventName = (src->m_eventName != NULL) ? _tcsdup(src->m_eventName) : NULL;
69 m_numParams = src->m_numParams;
70 m_pmatch = (m_numParams > 0) ? (regmatch_t *)malloc(sizeof(regmatch_t) * (m_numParams + 1)) : NULL;
71 m_source = (src->m_source != NULL) ? _tcsdup(src->m_source) : NULL;
72 m_level = src->m_level;
73 m_idStart = src->m_idStart;
74 m_idEnd = src->m_idEnd;
75 m_context = (src->m_context != NULL) ? _tcsdup(src->m_context) : NULL;
76 m_contextAction = src->m_contextAction;
77 m_contextToChange = (src->m_contextToChange != NULL) ? _tcsdup(src->m_contextToChange) : NULL;
78 m_isInverted = src->m_isInverted;
79 m_breakOnMatch = src->m_breakOnMatch;
80 m_description = (src->m_description != NULL) ? _tcsdup(src->m_description) : NULL;
81 m_repeatInterval = src->m_repeatInterval;
82 m_repeatCount = src->m_repeatCount;
83 m_resetRepeat = src->m_resetRepeat;
84 if (src->m_matchArray != NULL)
85 {
86 m_matchArray = new IntegerArray<time_t>(src->m_matchArray->size(), 16);
87 for(int i = 0; i < src->m_matchArray->size(); i++)
88 m_matchArray->add(src->m_matchArray->get(i));
89 }
90 else
91 {
92 m_matchArray = new IntegerArray<time_t>();
93 }
94 }
95
96 /**
97 * Destructor
98 */
99 LogParserRule::~LogParserRule()
100 {
101 if (m_isValid)
102 regfree(&m_preg);
103 safe_free(m_pmatch);
104 safe_free(m_description);
105 safe_free(m_source);
106 safe_free(m_regexp);
107 safe_free(m_eventName);
108 safe_free(m_context);
109 safe_free(m_contextToChange);
110 delete m_matchArray;
111 }
112
113 /**
114 * Match line
115 */
116 bool LogParserRule::matchInternal(bool extMode, const TCHAR *source, UINT32 eventId, UINT32 level,
117 const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
118 {
119 if (extMode)
120 {
121 if (m_source != NULL)
122 {
123 m_parser->trace(6, _T(" matching source \"%s\" against pattern \"%s\""), source, m_source);
124 if (!MatchString(m_source, source, FALSE))
125 {
126 m_parser->trace(6, _T(" source: no match"));
127 return false;
128 }
129 }
130
131 if ((eventId < m_idStart) || (eventId > m_idEnd))
132 {
133 m_parser->trace(6, _T(" event id 0x%08x not in range 0x%08x - 0x%08x"), eventId, m_idStart, m_idEnd);
134 return false;
135 }
136
137 if (!(m_level & level))
138 {
139 m_parser->trace(6, _T(" severity level 0x%04x not match mask 0x%04x"), level, m_level);
140 return false;
141 }
142 }
143
144 if (!m_isValid)
145 {
146 m_parser->trace(6, _T(" regexp is invalid: %s"), m_regexp);
147 return false;
148 }
149
150 if (m_isInverted)
151 {
152 m_parser->trace(6, _T(" negated matching against regexp %s"), m_regexp);
153 if ((_tregexec(&m_preg, line, 0, NULL, 0) != 0) && matchRepeatCount())
154 {
155 m_parser->trace(6, _T(" matched"));
156 if ((cb != NULL) && ((m_eventCode != 0) || (m_eventName != NULL)))
157 cb(m_eventCode, m_eventName, line, source, eventId, level, 0, NULL, objectId,
158 ((m_repeatCount > 0) && (m_repeatInterval > 0)) ? m_matchArray->size() : 1, userArg);
159 return true;
160 }
161 }
162 else
163 {
164 m_parser->trace(6, _T(" matching against regexp %s"), m_regexp);
165 if ((_tregexec(&m_preg, line, (m_numParams > 0) ? m_numParams + 1 : 0, m_pmatch, 0) == 0) && matchRepeatCount())
166 {
167 m_parser->trace(6, _T(" matched"));
168 if ((cb != NULL) && ((m_eventCode != 0) || (m_eventName != NULL)))
169 {
170 TCHAR **params = NULL;
171 int i, len;
172
173 if (m_numParams > 0)
174 {
175 #if HAVE_ALLOCA
176 params = (TCHAR **)alloca(sizeof(TCHAR *) * m_numParams);
177 #else
178 params = (TCHAR **)malloc(sizeof(TCHAR *) * m_numParams);
179 #endif
180 for(i = 0; i < m_numParams; i++)
181 {
182 if (m_pmatch[i + 1].rm_so != -1)
183 {
184 len = m_pmatch[i + 1].rm_eo - m_pmatch[i + 1].rm_so;
185 params[i] = (TCHAR *)malloc((len + 1) * sizeof(TCHAR));
186 memcpy(params[i], &line[m_pmatch[i + 1].rm_so], len * sizeof(TCHAR));
187 params[i][len] = 0;
188 }
189 else
190 {
191 params[i] = _tcsdup(_T(""));
192 }
193 }
194 }
195
196 cb(m_eventCode, m_eventName, line, source, eventId, level, m_numParams, params, objectId,
197 ((m_repeatCount > 0) && (m_repeatInterval > 0)) ? m_matchArray->size() : 1, userArg);
198
199 for(i = 0; i < m_numParams; i++)
200 free(params[i]);
201 #if !HAVE_ALLOCA
202 free(params);
203 #endif
204 }
205 return true;
206 }
207 }
208
209 m_parser->trace(6, _T(" no match"));
210 return false; // no match
211 }
212
213 /**
214 * Match line
215 */
216 bool LogParserRule::match(const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
217 {
218 return matchInternal(false, NULL, 0, 0, line, cb, objectId, userArg);
219 }
220
221 /**
222 * Match event
223 */
224 bool LogParserRule::matchEx(const TCHAR *source, UINT32 eventId, UINT32 level,
225 const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
226 {
227 return matchInternal(true, source, eventId, level, line, cb, objectId, userArg);
228 }
229
230 /**
231 * Expand macros in regexp
232 */
233 void LogParserRule::expandMacros(const TCHAR *regexp, String &out)
234 {
235 const TCHAR *curr, *prev;
236 TCHAR name[256];
237
238 for(curr = prev = regexp; *curr != 0; curr++)
239 {
240 if (*curr == _T('@'))
241 {
242 // Check for escape
243 if ((curr != regexp) && (*(curr - 1) == _T('\\')))
244 {
245 out.append(prev, (size_t)(curr - prev - 1));
246 out += _T("@");
247 }
248 else
249 {
250 // { should follow @
251 if (*(curr + 1) == _T('{'))
252 {
253 int i;
254
255 out.append(prev, (size_t)(curr - prev));
256 curr += 2;
257 for(i = 0; (*curr != 0) && (*curr != '}'); i++)
258 name[i] = *curr++;
259 name[i] = 0;
260 out += m_parser->getMacro(name);
261 }
262 else
263 {
264 out.append(prev, (size_t)(curr - prev + 1));
265 }
266 }
267 prev = curr + 1;
268 }
269 }
270 out.append(prev, (size_t)(curr - prev));
271 }
272
273 /**
274 * Match repeat count
275 */
276 bool LogParserRule::matchRepeatCount()
277 {
278 if ((m_repeatCount == 0) || (m_repeatInterval == 0))
279 return true;
280
281 // remove expired matches
282 time_t now = time(NULL);
283 for(int i = 0; i < m_matchArray->size(); i++)
284 {
285 if (m_matchArray->get(i) >= (now - m_repeatInterval))
286 break;
287 m_matchArray->remove(i);
288 i--;
289 }
290
291 m_matchArray->add(now);
292 bool match = m_matchArray->size() >= m_repeatCount;
293 if (m_resetRepeat && match)
294 m_matchArray->clear();
295 return match;
296 }