b7ff9ab06f0daeae13ca783f00b37f06cd8578bb
[public/netxms.git] / src / server / core / alarm.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2012 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: alarm.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25
26 //
27 // Global instance of alarm manager
28 //
29
30 AlarmManager g_alarmMgr;
31
32
33 //
34 // Fill CSCP message with alarm data
35 //
36
37 void FillAlarmInfoMessage(CSCPMessage *pMsg, NXC_ALARM *pAlarm)
38 {
39 pMsg->SetVariable(VID_ALARM_ID, pAlarm->dwAlarmId);
40 pMsg->SetVariable(VID_ACK_BY_USER, pAlarm->dwAckByUser);
41 pMsg->SetVariable(VID_RESOLVED_BY_USER, pAlarm->dwResolvedByUser);
42 pMsg->SetVariable(VID_TERMINATED_BY_USER, pAlarm->dwTermByUser);
43 pMsg->SetVariable(VID_EVENT_CODE, pAlarm->dwSourceEventCode);
44 pMsg->SetVariable(VID_EVENT_ID, pAlarm->qwSourceEventId);
45 pMsg->SetVariable(VID_OBJECT_ID, pAlarm->dwSourceObject);
46 pMsg->SetVariable(VID_CREATION_TIME, pAlarm->dwCreationTime);
47 pMsg->SetVariable(VID_LAST_CHANGE_TIME, pAlarm->dwLastChangeTime);
48 pMsg->SetVariable(VID_ALARM_KEY, pAlarm->szKey);
49 pMsg->SetVariable(VID_ALARM_MESSAGE, pAlarm->szMessage);
50 pMsg->SetVariable(VID_STATE, (WORD)(pAlarm->nState & ALARM_STATE_MASK)); // send only state to client, without flags
51 pMsg->SetVariable(VID_IS_STICKY, (WORD)((pAlarm->nState & ALARM_STATE_STICKY) ? 1 : 0));
52 pMsg->SetVariable(VID_CURRENT_SEVERITY, (WORD)pAlarm->nCurrentSeverity);
53 pMsg->SetVariable(VID_ORIGINAL_SEVERITY, (WORD)pAlarm->nOriginalSeverity);
54 pMsg->SetVariable(VID_HELPDESK_STATE, (WORD)pAlarm->nHelpDeskState);
55 pMsg->SetVariable(VID_HELPDESK_REF, pAlarm->szHelpDeskRef);
56 pMsg->SetVariable(VID_REPEAT_COUNT, pAlarm->dwRepeatCount);
57 pMsg->SetVariable(VID_ALARM_TIMEOUT, pAlarm->dwTimeout);
58 pMsg->SetVariable(VID_ALARM_TIMEOUT_EVENT, pAlarm->dwTimeoutEvent);
59 pMsg->SetVariable(VID_NUM_COMMENTS, pAlarm->noteCount);
60 }
61
62
63 //
64 // Alarm manager constructor
65 //
66
67 AlarmManager::AlarmManager()
68 {
69 m_dwNumAlarms = 0;
70 m_pAlarmList = NULL;
71 m_mutex = MutexCreate();
72 m_condShutdown = ConditionCreate(FALSE);
73 m_hWatchdogThread = INVALID_THREAD_HANDLE;
74 }
75
76
77 //
78 // Alarm manager destructor
79 //
80
81 AlarmManager::~AlarmManager()
82 {
83 safe_free(m_pAlarmList);
84 MutexDestroy(m_mutex);
85 ConditionSet(m_condShutdown);
86 ThreadJoin(m_hWatchdogThread);
87 }
88
89
90 //
91 // Watchdog thread starter
92 //
93
94 static THREAD_RESULT THREAD_CALL WatchdogThreadStarter(void *pArg)
95 {
96 ((AlarmManager *)pArg)->watchdogThread();
97 return THREAD_OK;
98 }
99
100
101 //
102 // Get number of notes for alarm
103 //
104
105 static DWORD GetNoteCount(DB_HANDLE hdb, DWORD alarmId)
106 {
107 DWORD value = 0;
108 DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT count(*) FROM alarm_notes WHERE alarm_id=?"));
109 if (hStmt != NULL)
110 {
111 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, alarmId);
112 DB_RESULT hResult = DBSelectPrepared(hStmt);
113 if (hResult != NULL)
114 {
115 if (DBGetNumRows(hResult) > 0)
116 value = DBGetFieldULong(hResult, 0, 0);
117 DBFreeResult(hResult);
118 }
119 DBFreeStatement(hStmt);
120 }
121 return value;
122 }
123
124
125 //
126 // Initialize alarm manager at system startup
127 //
128
129 BOOL AlarmManager::init()
130 {
131 DB_RESULT hResult;
132 DWORD i;
133
134 // Load active alarms into memory
135 hResult = DBSelect(g_hCoreDB, _T("SELECT alarm_id,source_object_id,")
136 _T("source_event_code,source_event_id,message,")
137 _T("original_severity,current_severity,")
138 _T("alarm_key,creation_time,last_change_time,")
139 _T("hd_state,hd_ref,ack_by,repeat_count,")
140 _T("alarm_state,timeout,timeout_event,resolved_by ")
141 _T("FROM alarms WHERE alarm_state<>3"));
142 if (hResult == NULL)
143 return FALSE;
144
145 m_dwNumAlarms = DBGetNumRows(hResult);
146 if (m_dwNumAlarms > 0)
147 {
148 m_pAlarmList = (NXC_ALARM *)malloc(sizeof(NXC_ALARM) * m_dwNumAlarms);
149 memset(m_pAlarmList, 0, sizeof(NXC_ALARM) * m_dwNumAlarms);
150 for(i = 0; i < m_dwNumAlarms; i++)
151 {
152 m_pAlarmList[i].dwAlarmId = DBGetFieldULong(hResult, i, 0);
153 m_pAlarmList[i].dwSourceObject = DBGetFieldULong(hResult, i, 1);
154 m_pAlarmList[i].dwSourceEventCode = DBGetFieldULong(hResult, i, 2);
155 m_pAlarmList[i].qwSourceEventId = DBGetFieldUInt64(hResult, i, 3);
156 DBGetField(hResult, i, 4, m_pAlarmList[i].szMessage, MAX_DB_STRING);
157 m_pAlarmList[i].nOriginalSeverity = (BYTE)DBGetFieldLong(hResult, i, 5);
158 m_pAlarmList[i].nCurrentSeverity = (BYTE)DBGetFieldLong(hResult, i, 6);
159 DBGetField(hResult, i, 7, m_pAlarmList[i].szKey, MAX_DB_STRING);
160 m_pAlarmList[i].dwCreationTime = DBGetFieldULong(hResult, i, 8);
161 m_pAlarmList[i].dwLastChangeTime = DBGetFieldULong(hResult, i, 9);
162 m_pAlarmList[i].nHelpDeskState = (BYTE)DBGetFieldLong(hResult, i, 10);
163 DBGetField(hResult, i, 11, m_pAlarmList[i].szHelpDeskRef, MAX_HELPDESK_REF_LEN);
164 m_pAlarmList[i].dwAckByUser = DBGetFieldULong(hResult, i, 12);
165 m_pAlarmList[i].dwRepeatCount = DBGetFieldULong(hResult, i, 13);
166 m_pAlarmList[i].nState = (BYTE)DBGetFieldLong(hResult, i, 14);
167 m_pAlarmList[i].dwTimeout = DBGetFieldULong(hResult, i, 15);
168 m_pAlarmList[i].dwTimeoutEvent = DBGetFieldULong(hResult, i, 16);
169 m_pAlarmList[i].noteCount = GetNoteCount(g_hCoreDB, m_pAlarmList[i].dwAlarmId);
170 m_pAlarmList[i].dwResolvedByUser = DBGetFieldULong(hResult, i, 17);
171 }
172 }
173
174 DBFreeResult(hResult);
175
176 m_hWatchdogThread = ThreadCreateEx(WatchdogThreadStarter, 0, this);
177 return TRUE;
178 }
179
180
181 //
182 // Create new alarm
183 //
184
185 void AlarmManager::newAlarm(TCHAR *pszMsg, TCHAR *pszKey, int nState,
186 int iSeverity, DWORD dwTimeout,
187 DWORD dwTimeoutEvent, Event *pEvent)
188 {
189 NXC_ALARM alarm;
190 TCHAR *pszExpMsg, *pszExpKey, szQuery[2048];
191 DWORD i, dwObjectId = 0;
192 BOOL bNewAlarm = TRUE;
193
194 // Expand alarm's message and key
195 pszExpMsg = pEvent->expandText(pszMsg);
196 pszExpKey = pEvent->expandText(pszKey);
197
198 // Check if we have a duplicate alarm
199 if (((nState & ALARM_STATE_MASK) != ALARM_STATE_TERMINATED) && (*pszExpKey != 0))
200 {
201 lock();
202
203 for(i = 0; i < m_dwNumAlarms; i++)
204 if (!_tcscmp(pszExpKey, m_pAlarmList[i].szKey))
205 {
206 m_pAlarmList[i].dwRepeatCount++;
207 m_pAlarmList[i].dwLastChangeTime = (DWORD)time(NULL);
208 m_pAlarmList[i].dwSourceObject = pEvent->getSourceId();
209 if ((m_pAlarmList[i].nState & ALARM_STATE_STICKY) == 0)
210 m_pAlarmList[i].nState = nState;
211 m_pAlarmList[i].nCurrentSeverity = iSeverity;
212 m_pAlarmList[i].dwTimeout = dwTimeout;
213 m_pAlarmList[i].dwTimeoutEvent = dwTimeoutEvent;
214 nx_strncpy(m_pAlarmList[i].szMessage, pszExpMsg, MAX_DB_STRING);
215
216 notifyClients(NX_NOTIFY_ALARM_CHANGED, &m_pAlarmList[i]);
217 updateAlarmInDB(&m_pAlarmList[i]);
218
219 bNewAlarm = FALSE;
220 break;
221 }
222
223 unlock();
224 }
225
226 if (bNewAlarm)
227 {
228 // Create new alarm structure
229 memset(&alarm, 0, sizeof(NXC_ALARM));
230 alarm.dwAlarmId = CreateUniqueId(IDG_ALARM);
231 alarm.qwSourceEventId = pEvent->getId();
232 alarm.dwSourceEventCode = pEvent->getCode();
233 alarm.dwSourceObject = pEvent->getSourceId();
234 alarm.dwCreationTime = (DWORD)time(NULL);
235 alarm.dwLastChangeTime = alarm.dwCreationTime;
236 alarm.nState = nState;
237 alarm.nOriginalSeverity = iSeverity;
238 alarm.nCurrentSeverity = iSeverity;
239 alarm.dwRepeatCount = 1;
240 alarm.nHelpDeskState = ALARM_HELPDESK_IGNORED;
241 alarm.dwTimeout = dwTimeout;
242 alarm.dwTimeoutEvent = dwTimeoutEvent;
243 alarm.noteCount = 0;
244 nx_strncpy(alarm.szMessage, pszExpMsg, MAX_DB_STRING);
245 nx_strncpy(alarm.szKey, pszExpKey, MAX_DB_STRING);
246
247 // Add new alarm to active alarm list if needed
248 if ((alarm.nState & ALARM_STATE_MASK) != ALARM_STATE_TERMINATED)
249 {
250 lock();
251
252 DbgPrintf(7, _T("AlarmManager: adding new active alarm, current alarm count %d"), (int)m_dwNumAlarms);
253 m_dwNumAlarms++;
254 m_pAlarmList = (NXC_ALARM *)realloc(m_pAlarmList, sizeof(NXC_ALARM) * m_dwNumAlarms);
255 memcpy(&m_pAlarmList[m_dwNumAlarms - 1], &alarm, sizeof(NXC_ALARM));
256 dwObjectId = alarm.dwSourceObject;
257
258 unlock();
259 }
260
261 // Save alarm to database
262 _sntprintf(szQuery, 2048,
263 _T("INSERT INTO alarms (alarm_id,creation_time,last_change_time,")
264 _T("source_object_id,source_event_code,message,original_severity,")
265 _T("current_severity,alarm_key,alarm_state,ack_by,resolved_by,hd_state,")
266 _T("hd_ref,repeat_count,term_by,timeout,timeout_event,source_event_id) VALUES ")
267 _T("(%d,%d,%d,%d,%d,%s,%d,%d,%s,%d,%d,%d,%d,%s,%d,%d,%d,%d,") UINT64_FMT _T(")"),
268 alarm.dwAlarmId, alarm.dwCreationTime, alarm.dwLastChangeTime,
269 alarm.dwSourceObject, alarm.dwSourceEventCode,
270 (const TCHAR *)DBPrepareString(g_hCoreDB, alarm.szMessage),
271 alarm.nOriginalSeverity, alarm.nCurrentSeverity,
272 (const TCHAR *)DBPrepareString(g_hCoreDB, alarm.szKey),
273 alarm.nState, alarm.dwAckByUser, alarm.dwResolvedByUser, alarm.nHelpDeskState,
274 (const TCHAR *)DBPrepareString(g_hCoreDB, alarm.szHelpDeskRef),
275 alarm.dwRepeatCount, alarm.dwTermByUser, alarm.dwTimeout,
276 alarm.dwTimeoutEvent, alarm.qwSourceEventId);
277 QueueSQLRequest(szQuery);
278
279 // Notify connected clients about new alarm
280 notifyClients(NX_NOTIFY_NEW_ALARM, &alarm);
281 }
282
283 // Update status of related object if needed
284 if ((dwObjectId != 0) && ((alarm.nState & ALARM_STATE_MASK) != ALARM_STATE_TERMINATED))
285 updateObjectStatus(dwObjectId);
286
287 free(pszExpMsg);
288 free(pszExpKey);
289 }
290
291
292 //
293 // Acknowledge alarm with given ID
294 //
295
296 DWORD AlarmManager::ackById(DWORD dwAlarmId, DWORD dwUserId, bool sticky)
297 {
298 DWORD i, dwObject, dwRet = RCC_INVALID_ALARM_ID;
299
300 lock();
301 for(i = 0; i < m_dwNumAlarms; i++)
302 if (m_pAlarmList[i].dwAlarmId == dwAlarmId)
303 {
304 if ((m_pAlarmList[i].nState & ALARM_STATE_MASK) == ALARM_STATE_OUTSTANDING)
305 {
306 m_pAlarmList[i].nState = ALARM_STATE_ACKNOWLEDGED;
307 if (sticky)
308 m_pAlarmList[i].nState |= ALARM_STATE_STICKY;
309 m_pAlarmList[i].dwAckByUser = dwUserId;
310 m_pAlarmList[i].dwLastChangeTime = (DWORD)time(NULL);
311 dwObject = m_pAlarmList[i].dwSourceObject;
312 notifyClients(NX_NOTIFY_ALARM_CHANGED, &m_pAlarmList[i]);
313 updateAlarmInDB(&m_pAlarmList[i]);
314 dwRet = RCC_SUCCESS;
315 }
316 else
317 {
318 dwRet = RCC_ALARM_NOT_OUTSTANDING;
319 }
320 break;
321 }
322 unlock();
323
324 if (dwRet == RCC_SUCCESS)
325 updateObjectStatus(dwObject);
326 return dwRet;
327 }
328
329
330 //
331 // Resolve and possibly terminate alarm with given ID
332 // Should return RCC which can be sent to client
333 //
334
335 DWORD AlarmManager::resolveById(DWORD dwAlarmId, DWORD dwUserId, bool terminate)
336 {
337 DWORD i, dwObject, dwRet = RCC_INVALID_ALARM_ID;
338
339 lock();
340 for(i = 0; i < m_dwNumAlarms; i++)
341 if (m_pAlarmList[i].dwAlarmId == dwAlarmId)
342 {
343 // If alarm is open in helpdesk, it cannot be terminated
344 if (m_pAlarmList[i].nHelpDeskState != ALARM_HELPDESK_OPEN)
345 {
346 dwObject = m_pAlarmList[i].dwSourceObject;
347 if (terminate)
348 m_pAlarmList[i].dwTermByUser = dwUserId;
349 else
350 m_pAlarmList[i].dwResolvedByUser = dwUserId;
351 m_pAlarmList[i].dwLastChangeTime = (DWORD)time(NULL);
352 m_pAlarmList[i].nState = terminate ? ALARM_STATE_TERMINATED : ALARM_STATE_RESOLVED;
353 notifyClients(terminate ? NX_NOTIFY_ALARM_TERMINATED : NX_NOTIFY_ALARM_CHANGED, &m_pAlarmList[i]);
354 updateAlarmInDB(&m_pAlarmList[i]);
355 if (terminate)
356 {
357 m_dwNumAlarms--;
358 memmove(&m_pAlarmList[i], &m_pAlarmList[i + 1], sizeof(NXC_ALARM) * (m_dwNumAlarms - i));
359 }
360 dwRet = RCC_SUCCESS;
361 }
362 else
363 {
364 dwRet = RCC_ALARM_OPEN_IN_HELPDESK;
365 }
366 break;
367 }
368 unlock();
369
370 if (dwRet == RCC_SUCCESS)
371 updateObjectStatus(dwObject);
372 return dwRet;
373 }
374
375
376 //
377 // Resolve and possibly terminate all alarms with given key
378 //
379
380 void AlarmManager::resolveByKey(const TCHAR *pszKey, bool useRegexp, bool terminate)
381 {
382 DWORD i, j, dwNumObjects, *pdwObjectList, dwCurrTime;
383
384 pdwObjectList = (DWORD *)malloc(sizeof(DWORD) * m_dwNumAlarms);
385
386 lock();
387 dwCurrTime = (DWORD)time(NULL);
388 for(i = 0, dwNumObjects = 0; i < m_dwNumAlarms; i++)
389 if ((useRegexp ? RegexpMatch(m_pAlarmList[i].szKey, pszKey, TRUE) : !_tcscmp(pszKey, m_pAlarmList[i].szKey)) &&
390 (m_pAlarmList[i].nHelpDeskState != ALARM_HELPDESK_OPEN))
391 {
392 // Add alarm's source object to update list
393 for(j = 0; j < dwNumObjects; j++)
394 {
395 if (pdwObjectList[j] == m_pAlarmList[i].dwSourceObject)
396 break;
397 }
398 if (j == dwNumObjects)
399 {
400 pdwObjectList[dwNumObjects++] = m_pAlarmList[i].dwSourceObject;
401 }
402
403 // Terminate alarm
404 m_pAlarmList[i].nState = terminate ? ALARM_STATE_TERMINATED : ALARM_STATE_RESOLVED;
405 m_pAlarmList[i].dwLastChangeTime = dwCurrTime;
406 if (terminate)
407 m_pAlarmList[i].dwTermByUser = 0;
408 else
409 m_pAlarmList[i].dwResolvedByUser = 0;
410 notifyClients(terminate ? NX_NOTIFY_ALARM_TERMINATED : NX_NOTIFY_ALARM_CHANGED, &m_pAlarmList[i]);
411 updateAlarmInDB(&m_pAlarmList[i]);
412 if (terminate)
413 {
414 m_dwNumAlarms--;
415 memmove(&m_pAlarmList[i], &m_pAlarmList[i + 1], sizeof(NXC_ALARM) * (m_dwNumAlarms - i));
416 i--;
417 }
418 }
419 unlock();
420
421 // Update status of objects
422 for(i = 0; i < dwNumObjects; i++)
423 updateObjectStatus(pdwObjectList[i]);
424 free(pdwObjectList);
425 }
426
427
428 //
429 // Delete alarm with given ID
430 //
431
432 void AlarmManager::deleteAlarm(DWORD dwAlarmId)
433 {
434 DWORD i, dwObject;
435 TCHAR szQuery[256];
436
437 // Delete alarm from in-memory list
438 lock();
439 for(i = 0; i < m_dwNumAlarms; i++)
440 if (m_pAlarmList[i].dwAlarmId == dwAlarmId)
441 {
442 dwObject = m_pAlarmList[i].dwSourceObject;
443 notifyClients(NX_NOTIFY_ALARM_DELETED, &m_pAlarmList[i]);
444 m_dwNumAlarms--;
445 memmove(&m_pAlarmList[i], &m_pAlarmList[i + 1], sizeof(NXC_ALARM) * (m_dwNumAlarms - i));
446 break;
447 }
448 unlock();
449
450 // Delete from database
451 _sntprintf(szQuery, 256, _T("DELETE FROM alarms WHERE alarm_id=%d"), dwAlarmId);
452 QueueSQLRequest(szQuery);
453
454 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
455 DeleteAlarmNotes(hdb, dwAlarmId);
456 DBConnectionPoolReleaseConnection(hdb);
457
458 updateObjectStatus(dwObject);
459 }
460
461
462 //
463 // Update alarm information in database
464 //
465
466 void AlarmManager::updateAlarmInDB(NXC_ALARM *pAlarm)
467 {
468 TCHAR szQuery[2048];
469
470 _sntprintf(szQuery, 2048, _T("UPDATE alarms SET alarm_state=%d,ack_by=%d,term_by=%d,")
471 _T("last_change_time=%d,current_severity=%d,repeat_count=%d,")
472 _T("hd_state=%d,hd_ref=%s,timeout=%d,timeout_event=%d,")
473 _T("message=%s,resolved_by=%d WHERE alarm_id=%d"),
474 pAlarm->nState, pAlarm->dwAckByUser, pAlarm->dwTermByUser,
475 pAlarm->dwLastChangeTime, pAlarm->nCurrentSeverity,
476 pAlarm->dwRepeatCount, pAlarm->nHelpDeskState,
477 (const TCHAR *)DBPrepareString(g_hCoreDB, pAlarm->szHelpDeskRef),
478 pAlarm->dwTimeout, pAlarm->dwTimeoutEvent,
479 (const TCHAR *)DBPrepareString(g_hCoreDB, pAlarm->szMessage),
480 pAlarm->dwResolvedByUser, pAlarm->dwAlarmId);
481 QueueSQLRequest(szQuery);
482 }
483
484
485 //
486 // Callback for client session enumeration
487 //
488
489 void AlarmManager::sendAlarmNotification(ClientSession *pSession, void *pArg)
490 {
491 pSession->onAlarmUpdate(((AlarmManager *)pArg)->m_dwNotifyCode,
492 ((AlarmManager *)pArg)->m_pNotifyAlarmInfo);
493 }
494
495
496 //
497 // Notify connected clients about changes
498 //
499
500 void AlarmManager::notifyClients(DWORD dwCode, NXC_ALARM *pAlarm)
501 {
502 m_dwNotifyCode = dwCode;
503 m_pNotifyAlarmInfo = pAlarm;
504 EnumerateClientSessions(sendAlarmNotification, this);
505 }
506
507
508 //
509 // Send all alarms to client
510 //
511
512 void AlarmManager::sendAlarmsToClient(DWORD dwRqId, ClientSession *pSession)
513 {
514 DWORD i, dwUserId;
515 NetObj *pObject;
516 CSCPMessage msg;
517
518 dwUserId = pSession->getUserId();
519
520 // Prepare message
521 msg.SetCode(CMD_ALARM_DATA);
522 msg.SetId(dwRqId);
523
524 lock();
525 for(i = 0; i < m_dwNumAlarms; i++)
526 {
527 pObject = FindObjectById(m_pAlarmList[i].dwSourceObject);
528 if (pObject != NULL)
529 {
530 if (pObject->CheckAccessRights(dwUserId, OBJECT_ACCESS_READ_ALARMS))
531 {
532 FillAlarmInfoMessage(&msg, &m_pAlarmList[i]);
533 pSession->sendMessage(&msg);
534 msg.DeleteAllVariables();
535 }
536 }
537 }
538 unlock();
539
540 // Send end-of-list indicator
541 msg.SetVariable(VID_ALARM_ID, (DWORD)0);
542 pSession->sendMessage(&msg);
543 }
544
545
546 //
547 // Get alarm with given ID into NXCP message
548 // Should return RCC which can be sent to client
549 //
550
551 DWORD AlarmManager::getAlarm(DWORD dwAlarmId, CSCPMessage *msg)
552 {
553 DWORD i, dwRet = RCC_INVALID_ALARM_ID;
554
555 lock();
556 for(i = 0; i < m_dwNumAlarms; i++)
557 if (m_pAlarmList[i].dwAlarmId == dwAlarmId)
558 {
559 FillAlarmInfoMessage(msg, &m_pAlarmList[i]);
560 dwRet = RCC_SUCCESS;
561 break;
562 }
563 unlock();
564
565 return dwRet;
566 }
567
568
569 //
570 // Get source object for given alarm id
571 //
572
573 NetObj *AlarmManager::getAlarmSourceObject(DWORD dwAlarmId)
574 {
575 DWORD i, dwObjectId = 0;
576 TCHAR szQuery[256];
577 DB_RESULT hResult;
578
579 // First, look at our in-memory list
580 lock();
581 for(i = 0; i < m_dwNumAlarms; i++)
582 if (m_pAlarmList[i].dwAlarmId == dwAlarmId)
583 {
584 dwObjectId = m_pAlarmList[i].dwSourceObject;
585 break;
586 }
587 unlock();
588
589 // If not found, search database
590 if (i == m_dwNumAlarms)
591 {
592 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("SELECT source_object_id FROM alarms WHERE alarm_id=%d"), dwAlarmId);
593 hResult = DBSelect(g_hCoreDB, szQuery);
594 if (hResult != NULL)
595 {
596 if (DBGetNumRows(hResult) > 0)
597 {
598 dwObjectId = DBGetFieldULong(hResult, 0, 0);
599 }
600 DBFreeResult(hResult);
601 }
602 }
603
604 return FindObjectById(dwObjectId);
605 }
606
607
608 //
609 // Get most critical status among active alarms for given object
610 // Will return STATUS_UNKNOWN if there are no active alarms
611 //
612
613 int AlarmManager::getMostCriticalStatusForObject(DWORD dwObjectId)
614 {
615 DWORD i;
616 int iStatus = STATUS_UNKNOWN;
617
618 lock();
619 for(i = 0; i < m_dwNumAlarms; i++)
620 {
621 if ((m_pAlarmList[i].dwSourceObject == dwObjectId) &&
622 ((m_pAlarmList[i].nState & ALARM_STATE_MASK) < ALARM_STATE_RESOLVED) &&
623 ((m_pAlarmList[i].nCurrentSeverity > iStatus) || (iStatus == STATUS_UNKNOWN)))
624 {
625 iStatus = (int)m_pAlarmList[i].nCurrentSeverity;
626 }
627 }
628 unlock();
629 return iStatus;
630 }
631
632
633 //
634 // Update object status after alarm acknowledgement or deletion
635 //
636
637 void AlarmManager::updateObjectStatus(DWORD dwObjectId)
638 {
639 NetObj *pObject;
640
641 pObject = FindObjectById(dwObjectId);
642 if (pObject != NULL)
643 pObject->calculateCompoundStatus();
644 }
645
646
647 //
648 // Fill message with alarm stats
649 //
650
651 void AlarmManager::getAlarmStats(CSCPMessage *pMsg)
652 {
653 DWORD i, dwCount[5];
654
655 lock();
656 pMsg->SetVariable(VID_NUM_ALARMS, m_dwNumAlarms);
657 memset(dwCount, 0, sizeof(DWORD) * 5);
658 for(i = 0; i < m_dwNumAlarms; i++)
659 dwCount[m_pAlarmList[i].nCurrentSeverity]++;
660 unlock();
661 pMsg->SetVariableToInt32Array(VID_ALARMS_BY_SEVERITY, 5, dwCount);
662 }
663
664
665 //
666 // Watchdog thread
667 //
668
669 void AlarmManager::watchdogThread()
670 {
671 DWORD i;
672 time_t now;
673
674 while(1)
675 {
676 if (ConditionWait(m_condShutdown, 1000))
677 break;
678
679 lock();
680 now = time(NULL);
681 for(i = 0; i < m_dwNumAlarms; i++)
682 {
683 if ((m_pAlarmList[i].dwTimeout > 0) &&
684 ((m_pAlarmList[i].nState & ALARM_STATE_MASK) == ALARM_STATE_OUTSTANDING) &&
685 (((time_t)m_pAlarmList[i].dwLastChangeTime + (time_t)m_pAlarmList[i].dwTimeout) < now))
686 {
687 DbgPrintf(5, _T("Alarm timeout: alarm_id=%d, last_change=%d, timeout=%d, now=%d"),
688 m_pAlarmList[i].dwAlarmId, m_pAlarmList[i].dwLastChangeTime,
689 m_pAlarmList[i].dwTimeout, now);
690
691 PostEvent(m_pAlarmList[i].dwTimeoutEvent, m_pAlarmList[i].dwSourceObject, "dssd",
692 m_pAlarmList[i].dwAlarmId, m_pAlarmList[i].szMessage,
693 m_pAlarmList[i].szKey, m_pAlarmList[i].dwSourceEventCode);
694 m_pAlarmList[i].dwTimeout = 0; // Disable repeated timeout events
695 updateAlarmInDB(&m_pAlarmList[i]);
696 }
697 }
698 unlock();
699 }
700 }
701
702
703 //
704 // Check if givel alram/note id pair is valid
705 //
706
707 static bool IsValidNoteId(DWORD alarmId, DWORD noteId)
708 {
709 bool isValid = false;
710 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
711 DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT note_id FROM alarm_notes WHERE alarm_id=? AND note_id=?"));
712 if (hStmt != NULL)
713 {
714 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, alarmId);
715 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, noteId);
716 DB_RESULT hResult = DBSelectPrepared(hStmt);
717 if (hResult != NULL)
718 {
719 isValid = (DBGetNumRows(hResult) > 0);
720 DBFreeResult(hResult);
721 }
722 DBFreeStatement(hStmt);
723 }
724 DBConnectionPoolReleaseConnection(hdb);
725 return isValid;
726 }
727
728
729 //
730 // Update alarm's note
731 //
732
733 DWORD AlarmManager::updateAlarmNote(DWORD alarmId, DWORD noteId, const TCHAR *text, DWORD userId)
734 {
735 DWORD rcc = RCC_INVALID_ALARM_ID;
736
737 lock();
738 for(DWORD i = 0; i < m_dwNumAlarms; i++)
739 if (m_pAlarmList[i].dwAlarmId == alarmId)
740 {
741 if (noteId != 0)
742 {
743 if (IsValidNoteId(alarmId, noteId))
744 {
745 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
746 DB_STATEMENT hStmt = DBPrepare(hdb, _T("UPDATE alarm_notes SET change_time=?,user_id=?,note_text=? WHERE note_id=?"));
747 if (hStmt != NULL)
748 {
749 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (DWORD)time(NULL));
750 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, userId);
751 DBBind(hStmt, 3, DB_SQLTYPE_TEXT, text, DB_BIND_STATIC);
752 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, noteId);
753 rcc = DBExecute(hStmt) ? RCC_SUCCESS : RCC_DB_FAILURE;
754 DBFreeStatement(hStmt);
755 }
756 else
757 {
758 rcc = RCC_DB_FAILURE;
759 }
760 DBConnectionPoolReleaseConnection(hdb);
761 }
762 else
763 {
764 rcc = RCC_INVALID_ALARM_NOTE_ID;
765 }
766 }
767 else
768 {
769 // new note
770 noteId = CreateUniqueId(IDG_ALARM_NOTE);
771 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
772 DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO alarm_notes (note_id,alarm_id,change_time,user_id,note_text) VALUES (?,?,?,?,?)"));
773 if (hStmt != NULL)
774 {
775 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, noteId);
776 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, alarmId);
777 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (DWORD)time(NULL));
778 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, userId);
779 DBBind(hStmt, 5, DB_SQLTYPE_TEXT, text, DB_BIND_STATIC);
780 rcc = DBExecute(hStmt) ? RCC_SUCCESS : RCC_DB_FAILURE;
781 DBFreeStatement(hStmt);
782 }
783 else
784 {
785 rcc = RCC_DB_FAILURE;
786 }
787 DBConnectionPoolReleaseConnection(hdb);
788 }
789 if (rcc == RCC_SUCCESS)
790 {
791 m_pAlarmList[i].noteCount++;
792 notifyClients(NX_NOTIFY_ALARM_CHANGED, &m_pAlarmList[i]);
793 }
794 break;
795 }
796 unlock();
797
798 return rcc;
799 }
800
801
802 //
803 // Get alarm's notes
804 //
805
806 DWORD AlarmManager::getAlarmNotes(DWORD alarmId, CSCPMessage *msg)
807 {
808 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
809 DWORD rcc = RCC_DB_FAILURE;
810
811 DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT note_id,change_time,user_id,note_text FROM alarm_notes WHERE alarm_id=?"));
812 if (hStmt != NULL)
813 {
814 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, alarmId);
815 DB_RESULT hResult = DBSelectPrepared(hStmt);
816 if (hResult != NULL)
817 {
818 int count = DBGetNumRows(hResult);
819 msg->SetVariable(VID_NUM_ELEMENTS, (DWORD)count);
820
821 DWORD varId = VID_ELEMENT_LIST_BASE;
822 for(int i = 0; i < count; i++)
823 {
824 msg->SetVariable(varId++, DBGetFieldULong(hResult, i, 0));
825 msg->SetVariable(varId++, alarmId);
826 msg->SetVariable(varId++, DBGetFieldULong(hResult, i, 1));
827 msg->SetVariable(varId++, DBGetFieldULong(hResult, i, 2));
828 TCHAR *text = DBGetField(hResult, i, 3, NULL, 0);
829 msg->SetVariable(varId++, CHECK_NULL_EX(text));
830 safe_free(text);
831 varId += 5;
832 }
833 DBFreeResult(hResult);
834 rcc = RCC_SUCCESS;
835 }
836 DBFreeStatement(hStmt);
837 }
838
839 DBConnectionPoolReleaseConnection(hdb);
840 return rcc;
841 }