5064cfcac550baffd44f04ff706539f46781d5c4
[public/netxms.git] / src / server / core / events.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2017 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: events.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Global variables
27 */
28 Queue *g_pEventQueue = NULL;
29 EventPolicy *g_pEventPolicy = NULL;
30
31 /**
32 * Static data
33 */
34 static RefCountHashMap<UINT32, EventTemplate> m_eventTemplates(true);
35 static RWLOCK m_rwlockTemplateAccess;
36
37 /**
38 * Create event template from DB record
39 */
40 EventTemplate::EventTemplate(DB_RESULT hResult, int row)
41 {
42 m_code = DBGetFieldULong(hResult, row, 0);
43 m_severity = DBGetFieldLong(hResult, row, 1);
44 m_flags = DBGetFieldLong(hResult, row, 2);
45 m_messageTemplate = DBGetField(hResult, row, 3, NULL, 0);
46 m_description = DBGetField(hResult, row, 4, NULL, 0);
47 DBGetField(hResult, row, 5, m_name, MAX_EVENT_NAME);
48 m_guid = DBGetFieldGUID(hResult, row, 6);
49 }
50
51 /**
52 * Event template destructor
53 */
54 EventTemplate::~EventTemplate()
55 {
56 free(m_messageTemplate);
57 free(m_description);
58 }
59
60 /**
61 * Convert event template to JSON
62 */
63 json_t *EventTemplate::toJson() const
64 {
65 json_t *root = json_object();
66 json_object_set_new(root, "code", json_integer(m_code));
67 char guidText[64];
68 json_object_set_new(root, "guid", json_string(m_guid.toStringA(guidText)));
69 json_object_set_new(root, "severity", json_integer(m_severity));
70 json_object_set_new(root, "flags", json_integer(m_flags));
71 json_object_set_new(root, "message", json_string_t(m_messageTemplate));
72 json_object_set_new(root, "description", json_string_t(m_description));
73 return root;
74 }
75
76 /**
77 * Default constructor for event
78 */
79 Event::Event()
80 {
81 m_id = 0;
82 m_name[0] = 0;
83 m_rootId = 0;
84 m_code = 0;
85 m_severity = SEVERITY_NORMAL;
86 m_sourceId = 0;
87 m_dciId = 0;
88 m_flags = 0;
89 m_messageText = NULL;
90 m_messageTemplate = NULL;
91 m_timeStamp = 0;
92 m_userTag = NULL;
93 m_customMessage = NULL;
94 m_parameters.setOwner(true);
95 }
96
97 /**
98 * Copy constructor for event
99 */
100 Event::Event(const Event *src)
101 {
102 m_id = src->m_id;
103 _tcscpy(m_name, src->m_name);
104 m_rootId = src->m_rootId;
105 m_code = src->m_code;
106 m_severity = src->m_severity;
107 m_sourceId = src->m_sourceId;
108 m_dciId = src->m_dciId;
109 m_flags = src->m_flags;
110 m_messageText = _tcsdup_ex(src->m_messageText);
111 m_messageTemplate = _tcsdup_ex(src->m_messageTemplate);
112 m_timeStamp = src->m_timeStamp;
113 m_userTag = _tcsdup_ex(src->m_userTag);
114 m_customMessage = _tcsdup_ex(src->m_customMessage);
115 m_parameters.setOwner(true);
116 for(int i = 0; i < src->m_parameters.size(); i++)
117 {
118 m_parameters.add(_tcsdup_ex((TCHAR *)src->m_parameters.get(i)));
119 }
120 m_parameterNames.addAll(&src->m_parameterNames);
121 }
122
123 /**
124 * Construct event from template
125 */
126 Event::Event(const EventTemplate *eventTemplate, UINT32 sourceId, UINT32 dciId, const TCHAR *userTag, const char *szFormat, const TCHAR **names, va_list args)
127 {
128 _tcscpy(m_name, eventTemplate->getName());
129 m_timeStamp = time(NULL);
130 m_id = CreateUniqueEventId();
131 m_rootId = 0;
132 m_code = eventTemplate->getCode();
133 m_severity = eventTemplate->getSeverity();
134 m_flags = eventTemplate->getFlags();
135 m_sourceId = sourceId;
136 m_dciId = dciId;
137 m_messageText = NULL;
138 m_userTag = _tcsdup_ex(userTag);
139 if ((m_userTag != NULL) && (_tcslen(m_userTag) >= MAX_USERTAG_LENGTH))
140 m_userTag[MAX_USERTAG_LENGTH - 1] = 0;
141 m_customMessage = NULL;
142 m_parameters.setOwner(true);
143
144 // Create parameters
145 if (szFormat != NULL)
146 {
147 int count = (int)strlen(szFormat);
148 TCHAR *buffer;
149
150 for(int i = 0; i < count; i++)
151 {
152 switch(szFormat[i])
153 {
154 case 's':
155 {
156 const TCHAR *s = va_arg(args, const TCHAR *);
157 m_parameters.add(_tcsdup_ex(s));
158 }
159 break;
160 case 'm': // multibyte string
161 {
162 const char *s = va_arg(args, const char *);
163 #ifdef UNICODE
164 m_parameters.add((s != NULL) ? WideStringFromMBString(s) : _tcsdup(_T("")));
165 #else
166 m_parameters.add(strdup(CHECK_NULL_EX(s)));
167 #endif
168 }
169 break;
170 case 'u': // UNICODE string
171 {
172 const WCHAR *s = va_arg(args, const WCHAR *);
173 #ifdef UNICODE
174 m_parameters.add(wcsdup(CHECK_NULL_EX(s)));
175 #else
176 m_parameters.add((s != NULL) ? MBStringFromWideString(s) : strdup(""));
177 #endif
178 }
179 break;
180 case 'd':
181 buffer = (TCHAR *)malloc(16 * sizeof(TCHAR));
182 _sntprintf(buffer, 16, _T("%d"), va_arg(args, LONG));
183 m_parameters.add(buffer);
184 break;
185 case 'D':
186 buffer = (TCHAR *)malloc(32 * sizeof(TCHAR));
187 _sntprintf(buffer, 32, INT64_FMT, va_arg(args, INT64));
188 m_parameters.add(buffer);
189 break;
190 case 't':
191 buffer = (TCHAR *)malloc(32 * sizeof(TCHAR));
192 _sntprintf(buffer, 32, INT64_FMT, (INT64)va_arg(args, time_t));
193 m_parameters.add(buffer);
194 break;
195 case 'x':
196 case 'i':
197 buffer = (TCHAR *)malloc(16 * sizeof(TCHAR));
198 _sntprintf(buffer, 16, _T("0x%08X"), va_arg(args, UINT32));
199 m_parameters.add(buffer);
200 break;
201 case 'a': // IPv4 address
202 buffer = (TCHAR *)malloc(16 * sizeof(TCHAR));
203 IpToStr(va_arg(args, UINT32), buffer);
204 m_parameters.add(buffer);
205 break;
206 case 'A': // InetAddress object
207 buffer = (TCHAR *)malloc(64 * sizeof(TCHAR));
208 (va_arg(args, InetAddress *))->toString(buffer);
209 m_parameters.add(buffer);
210 break;
211 case 'h':
212 buffer = (TCHAR *)malloc(32 * sizeof(TCHAR));
213 MACToStr(va_arg(args, BYTE *), buffer);
214 m_parameters.add(buffer);
215 break;
216 default:
217 buffer = (TCHAR *)malloc(64 * sizeof(TCHAR));
218 _sntprintf(buffer, 64, _T("BAD FORMAT \"%c\" [value = 0x%08X]"), szFormat[i], va_arg(args, UINT32));
219 m_parameters.add(buffer);
220 break;
221 }
222 m_parameterNames.add(((names != NULL) && (names[i] != NULL)) ? names[i] : _T(""));
223 }
224 }
225
226 m_messageTemplate = _tcsdup(eventTemplate->getMessageTemplate());
227 }
228
229 /**
230 * Destructor for event
231 */
232 Event::~Event()
233 {
234 free(m_messageText);
235 free(m_messageTemplate);
236 free(m_userTag);
237 free(m_customMessage);
238 }
239
240 /**
241 * Create message text from template
242 */
243 void Event::expandMessageText()
244 {
245 if (m_messageTemplate == NULL)
246 return;
247
248 if (m_messageText != NULL)
249 free(m_messageText);
250 m_messageText = expandText(m_messageTemplate);
251 }
252
253 /**
254 * Substitute % macros in given text with actual values
255 */
256 TCHAR *Event::expandText(const TCHAR *textTemplate, const TCHAR *alarmMsg, const TCHAR *alarmKey)
257 {
258 return Event::expandText(this, m_sourceId, textTemplate, alarmMsg, alarmKey);
259 }
260
261 /**
262 * Substitute % macros in given text with actual values
263 */
264 TCHAR *Event::expandText(Event *event, UINT32 sourceObject, const TCHAR *textTemplate, const TCHAR *alarmMsg, const TCHAR *alarmKey)
265 {
266 const TCHAR *pCurr;
267 UINT32 dwPos, dwSize, dwParam;
268 UINT32 sourceId = (event != NULL) ? event->getSourceId() : sourceObject;
269 NetObj *pObject;
270 struct tm *lt;
271 TCHAR *pText, szBuffer[4], scriptName[256];
272 int i;
273
274 DbgPrintf(8, _T("Event::expandText(event=%p sourceObject=%d template='%s' alarmMsg='%s' alarmKey='%s')"),
275 event, (int)sourceObject, CHECK_NULL(textTemplate), CHECK_NULL(alarmMsg), CHECK_NULL(alarmKey));
276
277 pObject = FindObjectById(sourceId);
278 if (pObject == NULL)
279 {
280 pObject = FindObjectById(g_dwMgmtNode);
281 if (pObject == NULL)
282 pObject = g_pEntireNet;
283 }
284 dwSize = (UINT32)_tcslen(textTemplate) + 1;
285 pText = (TCHAR *)malloc(dwSize * sizeof(TCHAR));
286 for(pCurr = textTemplate, dwPos = 0; *pCurr != 0; pCurr++)
287 {
288 switch(*pCurr)
289 {
290 case '%': // Metacharacter
291 pCurr++;
292 if (*pCurr == 0)
293 {
294 pCurr--;
295 break; // Abnormal loop termination
296 }
297 switch(*pCurr)
298 {
299 case '%':
300 pText[dwPos++] = '%';
301 break;
302 case 'a': // IP address of event source
303 dwSize += 64;
304 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
305 GetObjectIpAddress(pObject).toString(&pText[dwPos]);
306 dwPos = (UINT32)_tcslen(pText);
307 break;
308 case 'A': // Associated alarm message
309 if (alarmMsg != NULL)
310 {
311 dwSize += (UINT32)_tcslen(alarmMsg);
312 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
313 _tcscpy(&pText[dwPos], alarmMsg);
314 dwPos += (UINT32)_tcslen(alarmMsg);
315 }
316 break;
317 case 'c': // Event code
318 dwSize += 16;
319 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
320 _sntprintf(&pText[dwPos], 16, _T("%u"), (unsigned int)((event != NULL) ? event->m_code : 0));
321 dwPos = (UINT32)_tcslen(pText);
322 break;
323 case 'g': // Source object's GUID
324 dwSize += 36;
325 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
326 pObject->getGuid().toString(&pText[dwPos]);
327 dwPos = (UINT32)_tcslen(pText);
328 break;
329 case 'i': // Source object identifier in form 0xhhhhhhhh
330 dwSize += 10;
331 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
332 _sntprintf(&pText[dwPos], 11, _T("0x%08X"), sourceId);
333 dwPos = (UINT32)_tcslen(pText);
334 break;
335 case 'I': // Source object identifier in decimal form
336 dwSize += 10;
337 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
338 _sntprintf(&pText[dwPos], 11, _T("%u"), (unsigned int)sourceId);
339 dwPos = (UINT32)_tcslen(pText);
340 break;
341 case 'K': // Associated alarm key
342 if (alarmKey != NULL)
343 {
344 dwSize += (UINT32)_tcslen(alarmKey);
345 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
346 _tcscpy(&pText[dwPos], alarmKey);
347 dwPos += (UINT32)_tcslen(alarmKey);
348 }
349 break;
350 case 'm':
351 if ((event != NULL) && (event->m_messageText != NULL))
352 {
353 dwSize += (UINT32)_tcslen(event->m_messageText);
354 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
355 _tcscpy(&pText[dwPos], event->m_messageText);
356 dwPos += (UINT32)_tcslen(event->m_messageText);
357 }
358 break;
359 case 'M': // Custom message (usually set by matching script in EPP)
360 if ((event != NULL) && (event->m_customMessage != NULL))
361 {
362 dwSize += (UINT32)_tcslen(event->m_customMessage);
363 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
364 _tcscpy(&pText[dwPos], event->m_customMessage);
365 dwPos += (UINT32)_tcslen(event->m_customMessage);
366 }
367 break;
368 case 'n': // Name of event source
369 dwSize += (UINT32)_tcslen(pObject->getName());
370 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
371 _tcscpy(&pText[dwPos], pObject->getName());
372 dwPos += (UINT32)_tcslen(pObject->getName());
373 break;
374 case 'N': // Event name
375 if (event != NULL)
376 {
377 dwSize += (UINT32)_tcslen(event->m_name);
378 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
379 _tcscpy(&pText[dwPos], event->m_name);
380 dwPos += (UINT32)_tcslen(event->m_name);
381 }
382 break;
383 case 's': // Severity code
384 if (event != NULL)
385 {
386 dwSize += 3;
387 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
388 _sntprintf(&pText[dwPos], 4, _T("%d"), (int)event->m_severity);
389 dwPos = (UINT32)_tcslen(pText);
390 }
391 break;
392 case 'S': // Severity text
393 if (event != NULL)
394 {
395 const TCHAR *statusText = GetStatusAsText(event->m_severity, false);
396 dwSize += (UINT32)_tcslen(statusText);
397 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
398 _tcscpy(&pText[dwPos], statusText);
399 dwPos += (UINT32)_tcslen(statusText);
400 }
401 break;
402 case 't': // Event's timestamp
403 dwSize += 32;
404 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
405 if (event != NULL)
406 {
407 lt = localtime(&event->m_timeStamp);
408 }
409 else
410 {
411 time_t t = time(NULL);
412 lt = localtime(&t);
413 }
414 _tcsftime(&pText[dwPos], 32, _T("%d-%b-%Y %H:%M:%S"), lt);
415 dwPos = (UINT32)_tcslen(pText);
416 break;
417 case 'T': // Event's timestamp as number of seconds since epoch
418 dwSize += 16;
419 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
420 _sntprintf(&pText[dwPos], 16, _T("%lu"), (unsigned long)((event != NULL) ? event->m_timeStamp : time(NULL)));
421 dwPos = (UINT32)_tcslen(pText);
422 break;
423 case 'u': // User tag
424 if ((event != NULL) && (event->m_userTag != NULL))
425 {
426 dwSize += (UINT32)_tcslen(event->m_userTag);
427 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
428 _tcscpy(&pText[dwPos], event->m_userTag);
429 dwPos += (UINT32)_tcslen(event->m_userTag);
430 }
431 break;
432 case 'v': // NetXMS server version
433 dwSize += (UINT32)_tcslen(NETXMS_VERSION_STRING);
434 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
435 _tcscpy(&pText[dwPos], NETXMS_VERSION_STRING);
436 dwPos += (UINT32)_tcslen(NETXMS_VERSION_STRING);
437 break;
438 case '0':
439 case '1':
440 case '2':
441 case '3':
442 case '4':
443 case '5':
444 case '6':
445 case '7':
446 case '8':
447 case '9':
448 if (event != NULL)
449 {
450 szBuffer[0] = *pCurr;
451 if (isdigit(*(pCurr + 1)))
452 {
453 pCurr++;
454 szBuffer[1] = *pCurr;
455 szBuffer[2] = 0;
456 }
457 else
458 {
459 szBuffer[1] = 0;
460 }
461 dwParam = _tcstoul(szBuffer, NULL, 10);
462 if ((dwParam > 0) && (dwParam <= (UINT32)event->m_parameters.size()))
463 {
464 const TCHAR *value = (TCHAR *)event->m_parameters.get(dwParam - 1);
465 if (value == NULL)
466 value = _T("");
467 dwSize += (UINT32)_tcslen(value);
468 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
469 _tcscpy(&pText[dwPos], value);
470 dwPos += (UINT32)_tcslen(value);
471 }
472 }
473 break;
474 case '[': // Script
475 for(i = 0, pCurr++; (*pCurr != ']') && (*pCurr != 0) && (i < 255); pCurr++)
476 {
477 scriptName[i++] = *pCurr;
478 }
479 if (*pCurr == 0) // no terminating ]
480 {
481 pCurr--;
482 }
483 else
484 {
485 scriptName[i] = 0;
486
487 // Entry point can be given in form script/entry_point
488 TCHAR *entryPoint = _tcschr(scriptName, _T('/'));
489 if (entryPoint != NULL)
490 {
491 *entryPoint = 0;
492 entryPoint++;
493 StrStrip(entryPoint);
494 }
495 StrStrip(scriptName);
496
497 NXSL_VM *vm = CreateServerScriptVM(scriptName);
498 if (vm != NULL)
499 {
500 if (pObject->getObjectClass() == OBJECT_NODE)
501 vm->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, pObject)));
502 vm->setGlobalVariable(_T("$event"), (event != NULL) ? new NXSL_Value(new NXSL_Object(&g_nxslEventClass, event)) : new NXSL_Value);
503 if (alarmMsg != NULL)
504 vm->setGlobalVariable(_T("$alarmMessage"), new NXSL_Value(alarmMsg));
505 if (alarmKey != NULL)
506 vm->setGlobalVariable(_T("$alarmKey"), new NXSL_Value(alarmKey));
507
508 if (vm->run(0, NULL, NULL, NULL, NULL, entryPoint))
509 {
510 NXSL_Value *result = vm->getResult();
511 if (result != NULL)
512 {
513 const TCHAR *temp = result->getValueAsCString();
514 if (temp != NULL)
515 {
516 dwSize += (UINT32)_tcslen(temp);
517 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
518 _tcscpy(&pText[dwPos], temp);
519 dwPos += (UINT32)_tcslen(temp);
520 DbgPrintf(4, _T("Event::ExpandText(%d, \"%s\"): Script %s executed successfully"),
521 (int)((event != NULL) ? event->m_code : 0), textTemplate, scriptName);
522 }
523 }
524 }
525 else
526 {
527 DbgPrintf(4, _T("Event::ExpandText(%d, \"%s\"): Script %s execution error: %s"),
528 (int)((event != NULL) ? event->m_code : 0), textTemplate, scriptName, vm->getErrorText());
529 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", scriptName, vm->getErrorText(), 0);
530 }
531 }
532 else
533 {
534 DbgPrintf(4, _T("Event::ExpandText(%d, \"%s\"): Cannot find script %s"),
535 (int)((event != NULL) ? event->m_code : 0), textTemplate, scriptName);
536 }
537 }
538 break;
539 case '{': // Custom attribute
540 for(i = 0, pCurr++; (*pCurr != '}') && (*pCurr != 0) && (i < 255); pCurr++)
541 {
542 scriptName[i++] = *pCurr;
543 }
544 if (*pCurr == 0) // no terminating }
545 {
546 pCurr--;
547 }
548 else
549 {
550 scriptName[i] = 0;
551 StrStrip(scriptName);
552 const TCHAR *temp = pObject->getCustomAttribute(scriptName);
553 if (temp != NULL)
554 {
555 dwSize += (UINT32)_tcslen(temp);
556 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
557 _tcscpy(&pText[dwPos], temp);
558 dwPos += (UINT32)_tcslen(temp);
559 }
560 }
561 break;
562 case '(': // input field - scan for closing ) for compatibility
563 for(i = 0, pCurr++; (*pCurr != ')') && (*pCurr != 0) && (i < 255); pCurr++)
564 {
565 }
566 if (*pCurr == 0) // no terminating }
567 {
568 pCurr--;
569 }
570 break;
571 case '<': // Named parameter
572 if (event != NULL)
573 {
574 for(i = 0, pCurr++; (*pCurr != '>') && (*pCurr != 0) && (i < 255); pCurr++)
575 {
576 scriptName[i++] = *pCurr;
577 }
578 if (*pCurr == 0) // no terminating >
579 {
580 pCurr--;
581 }
582 else
583 {
584 scriptName[i] = 0;
585 StrStrip(scriptName);
586 int index = event->m_parameterNames.indexOfIgnoreCase(scriptName);
587 if (index != -1)
588 {
589 const TCHAR *temp = (TCHAR *)event->m_parameters.get(index);
590 if (temp != NULL)
591 {
592 dwSize += (UINT32)_tcslen(temp);
593 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
594 _tcscpy(&pText[dwPos], temp);
595 dwPos += (UINT32)_tcslen(temp);
596 }
597 }
598 }
599 }
600 break;
601 default: // All other characters are invalid, ignore
602 break;
603 }
604 break;
605 case '\\': // Escape character
606 pCurr++;
607 if (*pCurr == 0)
608 {
609 pCurr--;
610 break; // Abnormal loop termination
611 }
612 switch(*pCurr)
613 {
614 case 't':
615 pText[dwPos++] = '\t';
616 break;
617 case 'n':
618 pText[dwPos++] = 0x0D;
619 pText[dwPos++] = 0x0A;
620 break;
621 default:
622 pText[dwPos++] = *pCurr;
623 break;
624 }
625 break;
626 default:
627 pText[dwPos++] = *pCurr;
628 break;
629 }
630 }
631 pText[dwPos] = 0;
632 return pText;
633 }
634
635 /**
636 * Add new parameter to event
637 */
638 void Event::addParameter(const TCHAR *name, const TCHAR *value)
639 {
640 m_parameters.add(_tcsdup(value));
641 m_parameterNames.add(name);
642 }
643
644 /**
645 * Set value of named parameter
646 */
647 void Event::setNamedParameter(const TCHAR *name, const TCHAR *value)
648 {
649 int index = m_parameterNames.indexOfIgnoreCase(name);
650 if (index != -1)
651 {
652 m_parameters.replace(index, _tcsdup(value));
653 m_parameterNames.replace(index, name);
654 }
655 else
656 {
657 m_parameters.add(_tcsdup(value));
658 m_parameterNames.add(name);
659 }
660 }
661
662 /**
663 * Set value (and optionally name) of parameter at given index.
664 *
665 * @param index 0-based parameter index
666 * @param name parameter name (can be NULL)
667 * @param value new value
668 */
669 void Event::setParameter(int index, const TCHAR *name, const TCHAR *value)
670 {
671 if (index < 0)
672 return;
673
674 int addup = index - m_parameters.size();
675 for(int i = 0; i < addup; i++)
676 {
677 m_parameters.add(_tcsdup(_T("")));
678 m_parameterNames.add(_T(""));
679 }
680 if (index < m_parameters.size())
681 {
682 m_parameters.replace(index, _tcsdup(value));
683 m_parameterNames.replace(index, CHECK_NULL_EX(name));
684 }
685 else
686 {
687 m_parameters.add(_tcsdup(value));
688 m_parameterNames.add(CHECK_NULL_EX(name));
689 }
690 }
691
692 /**
693 * Fill message with event data
694 */
695 void Event::prepareMessage(NXCPMessage *msg) const
696 {
697 msg->setField(VID_NUM_RECORDS, (UINT32)1);
698 msg->setField(VID_RECORDS_ORDER, (WORD)RECORD_ORDER_NORMAL);
699
700 UINT32 id = VID_EVENTLOG_MSG_BASE;
701 msg->setField(id++, m_id);
702 msg->setField(id++, m_code);
703 msg->setField(id++, (UINT32)m_timeStamp);
704 msg->setField(id++, m_sourceId);
705 msg->setField(id++, (WORD)m_severity);
706 msg->setField(id++, CHECK_NULL_EX(m_messageText));
707 msg->setField(id++, CHECK_NULL_EX(m_userTag));
708 msg->setField(id++, (UINT32)m_parameters.size());
709 for(int i = 0; i < m_parameters.size(); i++)
710 msg->setField(id++, (TCHAR *)m_parameters.get(i));
711 msg->setField(id++, m_dciId);
712 }
713
714 /**
715 * Create JSON object
716 */
717 String Event::createJson()
718 {
719 TCHAR buffer[64];
720
721 String json = _T("{ \"id\":");
722 json.append(m_id);
723 json.append(_T(", \"code\":"));
724 json.append(m_code);
725 if (EventNameFromCode(m_code, buffer))
726 {
727 json.append(_T(", \"name\":\""));
728 json.append(EscapeStringForJSON(buffer));
729 json.append(_T('"'));
730 }
731 json.append(_T(", \"timestamp\":"));
732 json.append((INT64)m_timeStamp);
733 json.append(_T(", \"source\":"));
734 json.append(m_sourceId);
735 json.append(_T(", \"dci\":"));
736 json.append(m_dciId);
737 json.append(_T(", \"severity\":"));
738 json.append(m_severity);
739 json.append(_T(", \"tag\":\""));
740 json.append(EscapeStringForJSON(m_userTag));
741 json.append(_T("\", \"message\":\""));
742 json.append(EscapeStringForJSON(m_messageText));
743 json.append(_T("\", \"parameters\":["));
744 for(int i = 0; i < m_parameters.size(); i++)
745 {
746 if (i > 0)
747 json.append(_T(','));
748 json.append(_T(" { \"name\":\""));
749 json.append(EscapeStringForJSON(m_parameterNames.get(i)));
750 json.append(_T("\", \"value\":\""));
751 json.append(EscapeStringForJSON((TCHAR *)m_parameters.get(i)));
752 json.append(_T("\" }"));
753 }
754 json.append(_T(" ] }"));
755 return json;
756 }
757
758 /**
759 * Load event configuration from database
760 */
761 static bool LoadEvents()
762 {
763 bool success = false;
764 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
765 DB_RESULT hResult = DBSelect(hdb, _T("SELECT event_code,severity,flags,message,description,event_name,guid FROM event_cfg"));
766 if (hResult != NULL)
767 {
768 int count = DBGetNumRows(hResult);
769 for(int i = 0; i < count; i++)
770 {
771 EventTemplate *t = new EventTemplate(hResult, i);
772 m_eventTemplates.set(t->getCode(), t);
773 t->decRefCount();
774 }
775
776 DBFreeResult(hResult);
777 success = true;
778 }
779 else
780 {
781 nxlog_write(MSG_EVENT_LOAD_ERROR, EVENTLOG_ERROR_TYPE, NULL);
782 }
783
784 DBConnectionPoolReleaseConnection(hdb);
785 return success;
786 }
787
788 /**
789 * Inilialize event handling subsystem
790 */
791 BOOL InitEventSubsystem()
792 {
793 BOOL bSuccess;
794
795 // Create template access mutex
796 m_rwlockTemplateAccess = RWLockCreate();
797
798 // Create event queue
799 g_pEventQueue = new Queue;
800
801 // Load events from database
802 bSuccess = LoadEvents();
803
804 // Create and initialize event processing policy
805 if (bSuccess)
806 {
807 g_pEventPolicy = new EventPolicy;
808 if (!g_pEventPolicy->loadFromDB())
809 {
810 bSuccess = FALSE;
811 nxlog_write(MSG_EPP_LOAD_FAILED, EVENTLOG_ERROR_TYPE, NULL);
812 delete g_pEventPolicy;
813 }
814 }
815
816 return bSuccess;
817 }
818
819 /**
820 * Shutdown event subsystem
821 */
822 void ShutdownEventSubsystem()
823 {
824 delete g_pEventQueue;
825 delete g_pEventPolicy;
826 RWLockDestroy(m_rwlockTemplateAccess);
827 }
828
829 /**
830 * Reload event templates from database
831 */
832 void ReloadEvents()
833 {
834 RWLockWriteLock(m_rwlockTemplateAccess, INFINITE);
835 m_eventTemplates.clear();
836 LoadEvents();
837 RWLockUnlock(m_rwlockTemplateAccess);
838 }
839
840 /**
841 * Delete event template from list
842 */
843 void DeleteEventTemplateFromList(UINT32 eventCode)
844 {
845 RWLockWriteLock(m_rwlockTemplateAccess, INFINITE);
846 m_eventTemplates.remove(eventCode);
847 RWLockUnlock(m_rwlockTemplateAccess);
848 }
849
850 /**
851 * Post event to given event queue.
852 *
853 * @param queue event queue to post events to
854 * @param eventCode Event code
855 * @param sourceId Event source object ID
856 * @param userTag event's user tag
857 * @param format Parameter format string, each parameter represented by one character.
858 * The following format characters can be used:
859 * s - String
860 * m - Multibyte string
861 * u - UNICODE string
862 * d - Decimal integer
863 * D - 64-bit decimal integer
864 * x - Hex integer
865 * a - IPv4 address
866 * A - InetAddress object
867 * h - MAC (hardware) address
868 * i - Object ID
869 * @param names names for parameters (NULL if parameters are unnamed)
870 * @param args event parameters
871 */
872 static bool RealPostEvent(Queue *queue, UINT64 *eventId, UINT32 eventCode, UINT32 sourceId, UINT32 dciId,
873 const TCHAR *userTag, const char *format, const TCHAR **names, va_list args)
874 {
875 EventTemplate *eventTemplate = NULL;
876 bool success = false;
877
878 RWLockReadLock(m_rwlockTemplateAccess, INFINITE);
879
880 eventTemplate = m_eventTemplates.get(eventCode);
881 if (eventTemplate != NULL)
882 {
883 // Template found, create new event
884 Event *evt = new Event(eventTemplate, sourceId, dciId, userTag, format, names, args);
885 if (eventId != NULL)
886 *eventId = evt->getId();
887
888 // Add new event to queue
889 queue->put(evt);
890
891 eventTemplate->decRefCount();
892 success = true;
893 }
894
895 RWLockUnlock(m_rwlockTemplateAccess);
896
897 if (eventTemplate == NULL)
898 {
899 DbgPrintf(3, _T("RealPostEvent: event with code %d not defined"), eventCode);
900 }
901 return success;
902 }
903
904 /**
905 * Post event to system event queue.
906 *
907 * @param eventCode Event code
908 * @param sourceId Event source object ID
909 * @param format Parameter format string, each parameter represented by one character.
910 * The following format characters can be used:
911 * s - String
912 * m - Multibyte string
913 * u - UNICODE string
914 * d - Decimal integer
915 * D - 64-bit decimal integer
916 * x - Hex integer
917 * a - IPv4 address
918 * A - InetAddress object
919 * h - MAC (hardware) address
920 * i - Object ID
921 * t - timestamp (time_t) as raw value (seconds since epoch)
922 */
923 bool NXCORE_EXPORTABLE PostEvent(UINT32 eventCode, UINT32 sourceId, const char *format, ...)
924 {
925 va_list args;
926 va_start(args, format);
927 bool success = RealPostEvent(g_pEventQueue, NULL, eventCode, sourceId, 0, NULL, format, NULL, args);
928 va_end(args);
929 return success;
930 }
931
932 /**
933 * Post DCI-related event to system event queue.
934 *
935 * @param eventCode Event code
936 * @param sourceId Event source object ID
937 * @param dciId DCI ID
938 * @param format Parameter format string, each parameter represented by one character.
939 * The following format characters can be used:
940 * s - String
941 * m - Multibyte string
942 * u - UNICODE string
943 * d - Decimal integer
944 * D - 64-bit decimal integer
945 * x - Hex integer
946 * a - IPv4 address
947 * A - InetAddress object
948 * h - MAC (hardware) address
949 * i - Object ID
950 * t - timestamp (time_t) as raw value (seconds since epoch)
951 */
952 bool NXCORE_EXPORTABLE PostDciEvent(UINT32 eventCode, UINT32 sourceId, UINT32 dciId, const char *format, ...)
953 {
954 va_list args;
955 va_start(args, format);
956 bool success = RealPostEvent(g_pEventQueue, NULL, eventCode, sourceId, dciId, NULL, format, NULL, args);
957 va_end(args);
958 return success;
959 }
960
961 /**
962 * Post event to system event queue and return ID of new event (0 in case of failure).
963 *
964 * @param eventCode Event code
965 * @param sourceId Event source object ID
966 * @param format Parameter format string, each parameter represented by one character.
967 * The following format characters can be used:
968 * s - String
969 * m - Multibyte string
970 * u - UNICODE string
971 * d - Decimal integer
972 * D - 64-bit decimal integer
973 * x - Hex integer
974 * a - IPv4 address
975 * A - InetAddress object
976 * h - MAC (hardware) address
977 * i - Object ID
978 * t - timestamp (time_t) as raw value (seconds since epoch)
979 */
980 UINT64 NXCORE_EXPORTABLE PostEvent2(UINT32 eventCode, UINT32 sourceId, const char *format, ...)
981 {
982 va_list args;
983 UINT64 eventId;
984 va_start(args, format);
985 bool success = RealPostEvent(g_pEventQueue, &eventId, eventCode, sourceId, 0, NULL, format, NULL, args);
986 va_end(args);
987 return success ? eventId : 0;
988 }
989
990 /**
991 * Post event to system event queue.
992 *
993 * @param eventCode Event code
994 * @param sourceId Event source object ID
995 * @param format Parameter format string, each parameter represented by one character.
996 * The following format characters can be used:
997 * s - String
998 * m - Multibyte string
999 * u - UNICODE string
1000 * d - Decimal integer
1001 * D - 64-bit decimal integer
1002 * x - Hex integer
1003 * a - IPv4 address
1004 * A - InetAddress object
1005 * h - MAC (hardware) address
1006 * i - Object ID
1007 * @param names names for parameters (NULL if parameters are unnamed)
1008 */
1009 bool NXCORE_EXPORTABLE PostEventWithNames(UINT32 eventCode, UINT32 sourceId, const char *format, const TCHAR **names, ...)
1010 {
1011 va_list args;
1012 va_start(args, names);
1013 bool success = RealPostEvent(g_pEventQueue, NULL, eventCode, sourceId, 0, NULL, format, names, args);
1014 va_end(args);
1015 return success;
1016 }
1017
1018 /**
1019 * Post DCI-related event to system event queue.
1020 *
1021 * @param eventCode Event code
1022 * @param sourceId Event source object ID
1023 * @param dciId DCI ID
1024 * @param format Parameter format string, each parameter represented by one character.
1025 * The following format characters can be used:
1026 * s - String
1027 * m - Multibyte string
1028 * u - UNICODE string
1029 * d - Decimal integer
1030 * D - 64-bit decimal integer
1031 * x - Hex integer
1032 * a - IPv4 address
1033 * A - InetAddress object
1034 * h - MAC (hardware) address
1035 * i - Object ID
1036 * @param names names for parameters (NULL if parameters are unnamed)
1037 */
1038 bool NXCORE_EXPORTABLE PostDciEventWithNames(UINT32 eventCode, UINT32 sourceId, UINT32 dciId, const char *format, const TCHAR **names, ...)
1039 {
1040 va_list args;
1041 va_start(args, names);
1042 bool success = RealPostEvent(g_pEventQueue, NULL, eventCode, sourceId, dciId, NULL, format, names, args);
1043 va_end(args);
1044 return success;
1045 }
1046
1047 /**
1048 * Post event to system event queue.
1049 *
1050 * @param eventCode Event code
1051 * @param sourceId Event source object ID
1052 * @param format Parameter format string, each parameter represented by one character.
1053 * The following format characters can be used:
1054 * s - String
1055 * m - Multibyte string
1056 * u - UNICODE string
1057 * d - Decimal integer
1058 * D - 64-bit decimal integer
1059 * x - Hex integer
1060 * a - IPv4 address
1061 * A - InetAddress object
1062 * h - MAC (hardware) address
1063 * i - Object ID
1064 * @param names names for parameters (NULL if parameters are unnamed)
1065 */
1066 bool NXCORE_EXPORTABLE PostEventWithTagAndNames(UINT32 eventCode, UINT32 sourceId, const TCHAR *userTag, const char *format, const TCHAR **names, ...)
1067 {
1068 va_list args;
1069 va_start(args, names);
1070 bool success = RealPostEvent(g_pEventQueue, NULL, eventCode, sourceId, 0, userTag, format, names, args);
1071 va_end(args);
1072 return success;
1073 }
1074
1075 /**
1076 * Post event to system event queue.
1077 *
1078 * @param eventCode Event code
1079 * @param sourceId Event source object ID
1080 * @param parameters event parameters list
1081 */
1082 bool NXCORE_EXPORTABLE PostEventWithNames(UINT32 eventCode, UINT32 sourceId, StringMap *parameters)
1083 {
1084 /*
1085 int count = parameters->size();
1086 if (count > 1023)
1087 count = 1023;
1088
1089 char format[1024];
1090 memset(format, 's', count);
1091 format[count] = 0;
1092
1093 const TCHAR *names[1024];
1094 const TCHAR *args[1024];
1095 for(int i = 0; i < count; i++)
1096 {
1097 names[i] = parameters->getKeyByIndex(i);
1098 args[i] = parameters->getValueByIndex(i);
1099 }
1100
1101 return RealPostEvent(g_pEventQueue, eventCode, sourceId, NULL, format, names, args);
1102 */
1103 return false;
1104 }
1105
1106 /**
1107 * Post DCI-related event to system event queue.
1108 *
1109 * @param eventCode Event code
1110 * @param sourceId Event source object ID
1111 * @param dciId DCI ID
1112 * @param parameters event parameters list
1113 */
1114 bool NXCORE_EXPORTABLE PostDciEventWithNames(UINT32 eventCode, UINT32 sourceId, UINT32 dciId, StringMap *parameters)
1115 {
1116 /*
1117 int count = parameters->size();
1118 if (count > 1023)
1119 count = 1023;
1120
1121 char format[1024];
1122 memset(format, 's', count);
1123 format[count] = 0;
1124
1125 const TCHAR *names[1024];
1126 const TCHAR *args[1024];
1127 for(int i = 0; i < count; i++)
1128 {
1129 names[i] = parameters->getKeyByIndex(i);
1130 args[i] = parameters->getValueByIndex(i);
1131 }
1132
1133 return RealPostEvent(g_pEventQueue, eventCode, sourceId, NULL, format, names, args);
1134 */
1135 return false;
1136 }
1137
1138 /**
1139 * Post event to system event queue.
1140 *
1141 * @param eventCode Event code
1142 * @param sourceId Event source object ID
1143 * @param userTag event's user tag
1144 * @param format Parameter format string, each parameter represented by one character.
1145 * The following format characters can be used:
1146 * s - String
1147 * m - Multibyte string
1148 * u - UNICODE string
1149 * d - Decimal integer
1150 * D - 64-bit decimal integer
1151 * x - Hex integer
1152 * a - IPv4 address
1153 * A - InetAddress object
1154 * h - MAC (hardware) address
1155 * i - Object ID
1156 * @param names names for parameters (NULL if parameters are unnamed)
1157 * @param args event parameters
1158 */
1159 bool NXCORE_EXPORTABLE PostEventWithTag(UINT32 eventCode, UINT32 sourceId, const TCHAR *userTag, const char *format, ...)
1160 {
1161 va_list args;
1162 va_start(args, format);
1163 bool success = RealPostEvent(g_pEventQueue, NULL, eventCode, sourceId, 0, userTag, format, NULL, args);
1164 va_end(args);
1165 return success;
1166 }
1167
1168 /**
1169 * Post event to given event queue.
1170 *
1171 * @param queue event queue to post events to
1172 * @param eventCode Event code
1173 * @param sourceId Event source object ID
1174 * @param format Parameter format string, each parameter represented by one character.
1175 * The following format characters can be used:
1176 * s - String
1177 * m - Multibyte string
1178 * u - UNICODE string
1179 * d - Decimal integer
1180 * D - 64-bit decimal integer
1181 * x - Hex integer
1182 * a - IPv4 address
1183 * A - InetAddress object
1184 * h - MAC (hardware) address
1185 * i - Object ID
1186 */
1187 bool NXCORE_EXPORTABLE PostEventEx(Queue *queue, UINT32 eventCode, UINT32 sourceId, const char *format, ...)
1188 {
1189 va_list args;
1190 va_start(args, format);
1191 bool success = RealPostEvent(queue, NULL, eventCode, sourceId, 0, NULL, format, NULL, args);
1192 va_end(args);
1193 return success;
1194 }
1195
1196 /**
1197 * Resend events from specific queue to system event queue
1198 */
1199 void NXCORE_EXPORTABLE ResendEvents(Queue *queue)
1200 {
1201 while(1)
1202 {
1203 void *pEvent = queue->get();
1204 if (pEvent == NULL)
1205 break;
1206 g_pEventQueue->put(pEvent);
1207 }
1208 }
1209
1210 /**
1211 * Create NXMP record for event
1212 */
1213 void CreateNXMPEventRecord(String &str, UINT32 eventCode)
1214 {
1215 String strText, strDescr;
1216
1217 RWLockReadLock(m_rwlockTemplateAccess, INFINITE);
1218
1219 // Find event template
1220 EventTemplate *p = m_eventTemplates.get(eventCode);
1221 if (p != NULL)
1222 {
1223 str.appendFormattedString(_T("\t\t<event id=\"%d\">\n")
1224 _T("\t\t\t<name>%s</name>\n")
1225 _T("\t\t\t<guid>%s</guid>\n")
1226 _T("\t\t\t<code>%d</code>\n")
1227 _T("\t\t\t<severity>%d</severity>\n")
1228 _T("\t\t\t<flags>%d</flags>\n")
1229 _T("\t\t\t<message>%s</message>\n")
1230 _T("\t\t\t<description>%s</description>\n")
1231 _T("\t\t</event>\n"),
1232 p->getCode(), (const TCHAR *)EscapeStringForXML2(p->getName()),
1233 (const TCHAR *)p->getGuid().toString(), p->getCode(), p->getSeverity(),
1234 p->getFlags(), (const TCHAR *)EscapeStringForXML2(p->getMessageTemplate()),
1235 (const TCHAR *)EscapeStringForXML2(p->getDescription()));
1236 p->decRefCount();
1237 }
1238
1239 RWLockUnlock(m_rwlockTemplateAccess);
1240 }
1241
1242 /**
1243 * Resolve event name
1244 */
1245 bool EventNameFromCode(UINT32 eventCode, TCHAR *buffer)
1246 {
1247 bool bRet = false;
1248
1249 RWLockReadLock(m_rwlockTemplateAccess, INFINITE);
1250
1251 EventTemplate *p = m_eventTemplates.get(eventCode);
1252 if (p != NULL)
1253 {
1254 _tcscpy(buffer, p->getName());
1255 p->decRefCount();
1256 bRet = true;
1257 }
1258 else
1259 {
1260 _tcscpy(buffer, _T("UNKNOWN_EVENT"));
1261 }
1262
1263 RWLockUnlock(m_rwlockTemplateAccess);
1264 return bRet;
1265 }
1266
1267 /**
1268 * Find event template by code - suitable for external call
1269 */
1270 EventTemplate *FindEventTemplateByCode(UINT32 eventCode)
1271 {
1272 RWLockReadLock(m_rwlockTemplateAccess, INFINITE);
1273 EventTemplate *p = m_eventTemplates.get(eventCode);
1274 RWLockUnlock(m_rwlockTemplateAccess);
1275 return p;
1276 }
1277
1278 /**
1279 * Find event template by name - suitable for external call
1280 */
1281 EventTemplate *FindEventTemplateByName(const TCHAR *name)
1282 {
1283 EventTemplate *result = NULL;
1284
1285 RWLockReadLock(m_rwlockTemplateAccess, INFINITE);
1286 Iterator<EventTemplate> *it = m_eventTemplates.iterator();
1287 while(it->hasNext())
1288 {
1289 EventTemplate *t = it->next();
1290 if (!_tcscmp(t->getName(), name))
1291 {
1292 result = t;
1293 result->incRefCount();
1294 break;
1295 }
1296 }
1297 delete it;
1298 RWLockUnlock(m_rwlockTemplateAccess);
1299 return result;
1300 }
1301
1302 /**
1303 * Translate event name to code
1304 * If event with given name does not exist, returns supplied default value
1305 */
1306 UINT32 NXCORE_EXPORTABLE EventCodeFromName(const TCHAR *name, UINT32 defaultValue)
1307 {
1308 EventTemplate *p = FindEventTemplateByName(name);
1309 if (p == NULL)
1310 return defaultValue;
1311 UINT32 code = p->getCode();
1312 p->decRefCount();
1313 return code;
1314 }
1315
1316 /**
1317 * Get status as text
1318 */
1319 const TCHAR NXCORE_EXPORTABLE *GetStatusAsText(int status, bool allCaps)
1320 {
1321 static const TCHAR *statusText[] = { _T("NORMAL"), _T("WARNING"), _T("MINOR"), _T("MAJOR"), _T("CRITICAL"), _T("UNKNOWN"), _T("UNMANAGED"), _T("DISABLED"), _T("TESTING") };
1322 static const TCHAR *statusTextSmall[] = { _T("Normal"), _T("Warning"), _T("Minor"), _T("Major"), _T("Critical"), _T("Unknown"), _T("Unmanaged"), _T("Disabled"), _T("Testing") };
1323
1324 if (allCaps)
1325 {
1326 return ((status >= STATUS_NORMAL) && (status <= STATUS_TESTING)) ? statusText[status] : _T("INTERNAL ERROR");
1327 }
1328 else
1329 {
1330 return ((status >= STATUS_NORMAL) && (status <= STATUS_TESTING)) ? statusTextSmall[status] : _T("INTERNAL ERROR");
1331 }
1332 }