c1c8b1825acaaf62c7d33df3d1d52a0e94875886
[public/netxms.git] / src / libnxlp / rule.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Log Parsing Library
4 ** Copyright (C) 2003-2013 Victor Kirhenshtein
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 matchArrayHousekeeper();
120 if (extMode)
121 {
122 if (m_source != NULL)
123 {
124 m_parser->trace(6, _T(" matching source \"%s\" against pattern \"%s\""), source, m_source);
125 if (!MatchString(m_source, source, FALSE))
126 {
127 m_parser->trace(6, _T(" source: no match"));
128 return false;
129 }
130 }
131
132 if ((eventId < m_idStart) || (eventId > m_idEnd))
133 {
134 m_parser->trace(6, _T(" event id 0x%08x not in range 0x%08x - 0x%08x"), eventId, m_idStart, m_idEnd);
135 return false;
136 }
137
138 if (!(m_level & level))
139 {
140 m_parser->trace(6, _T(" severity level 0x%04x not match mask 0x%04x"), level, m_level);
141 return false;
142 }
143 }
144
145 if (!m_isValid)
146 {
147 m_parser->trace(6, _T(" regexp is invalid: %s"), m_regexp);
148 return false;
149 }
150
151 if (m_isInverted)
152 {
153 m_parser->trace(6, _T(" negated matching against regexp %s"), m_regexp);
154 if (_tregexec(&m_preg, line, 0, NULL, 0) != 0 && processMatch())
155 {
156 m_parser->trace(6, _T(" matched"));
157 if ((cb != NULL) && ((m_eventCode != 0) || (m_eventName != NULL)))
158 cb(m_eventCode, m_eventName, line, source, eventId, level, 0, NULL, objectId, userArg, getAppearanceCount());
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 && processMatch())
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, userArg, getAppearanceCount());
197
198 for(i = 0; i < m_numParams; i++)
199 free(params[i]);
200 #if !HAVE_ALLOCA
201 free(params);
202 #endif
203 }
204 return true;
205 }
206 }
207
208 m_parser->trace(6, _T(" no match"));
209 return false; // no match
210 }
211
212 /**
213 * Match line
214 */
215 bool LogParserRule::match(const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
216 {
217 return matchInternal(false, NULL, 0, 0, line, cb, objectId, userArg);
218 }
219
220 /**
221 * Match event
222 */
223 bool LogParserRule::matchEx(const TCHAR *source, UINT32 eventId, UINT32 level,
224 const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
225 {
226 return matchInternal(true, source, eventId, level, line, cb, objectId, userArg);
227 }
228
229 /**
230 * Expand macros in regexp
231 */
232 void LogParserRule::expandMacros(const TCHAR *regexp, String &out)
233 {
234 const TCHAR *curr, *prev;
235 TCHAR name[256];
236
237 for(curr = prev = regexp; *curr != 0; curr++)
238 {
239 if (*curr == _T('@'))
240 {
241 // Check for escape
242 if ((curr != regexp) && (*(curr - 1) == _T('\\')))
243 {
244 out.append(prev, (size_t)(curr - prev - 1));
245 out += _T("@");
246 }
247 else
248 {
249 // { should follow @
250 if (*(curr + 1) == _T('{'))
251 {
252 int i;
253
254 out.append(prev, (size_t)(curr - prev));
255 curr += 2;
256 for(i = 0; (*curr != 0) && (*curr != '}'); i++)
257 name[i] = *curr++;
258 name[i] = 0;
259 out += m_parser->getMacro(name);
260 }
261 else
262 {
263 out.append(prev, (size_t)(curr - prev + 1));
264 }
265 }
266 prev = curr + 1;
267 }
268 }
269 out.append(prev, (size_t)(curr - prev));
270 }
271
272 void LogParserRule::matchArrayHousekeeper()
273 {
274 if(m_matchArray->size() == 0 || m_repeatCount == 0 || m_repeatInterval == 0)
275 return;
276
277 time_t now = time(NULL);
278 for(int i = 0; i < m_matchArray->size(); i++)
279 {
280 if(m_matchArray->get(i) < (now - m_repeatInterval))
281 m_matchArray->remove(i);
282 else
283 break;
284 }
285 }
286
287 bool LogParserRule::processMatch()
288 {
289 if(m_repeatCount == 0 || m_repeatInterval == 0)
290 return true;
291
292 m_matchArray->add(time(NULL));
293 bool match = m_matchArray->size() >= m_repeatCount;
294 if(m_resetRepeat && match) m_matchArray->clear();
295 return match;
296 }