fixed memory leak in alarms; fixed invalid memory access on server shutdown; updated...
[public/netxms.git] / src / server / core / alarm.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 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 * Global instance of alarm manager
27 */
28 static ObjectArray<Alarm> *m_alarmList;
29 static MUTEX m_mutex = INVALID_MUTEX_HANDLE;
30 static CONDITION m_condShutdown = INVALID_CONDITION_HANDLE;
31 static THREAD m_hWatchdogThread = INVALID_THREAD_HANDLE;
32
33 /**
34 * Get number of comments for alarm
35 */
36 static UINT32 GetCommentCount(DB_HANDLE hdb, UINT32 alarmId)
37 {
38 UINT32 value = 0;
39 DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT count(*) FROM alarm_notes WHERE alarm_id=?"));
40 if (hStmt != NULL)
41 {
42 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, alarmId);
43 DB_RESULT hResult = DBSelectPrepared(hStmt);
44 if (hResult != NULL)
45 {
46 if (DBGetNumRows(hResult) > 0)
47 value = DBGetFieldULong(hResult, 0, 0);
48 DBFreeResult(hResult);
49 }
50 DBFreeStatement(hStmt);
51 }
52 return value;
53 }
54
55 /**
56 * Create new alarm from event
57 */
58 Alarm::Alarm(Event *event, const TCHAR *message, const TCHAR *key, int state, int severity, UINT32 timeout, UINT32 timeoutEvent, UINT32 ackTimeout, IntegerArray<UINT32> *alarmCategoryList)
59 {
60 m_alarmId = CreateUniqueId(IDG_ALARM);
61 m_sourceEventId = event->getId();
62 m_sourceEventCode = event->getCode();
63 m_sourceObject = event->getSourceId();
64 m_dciId = event->getDciId();
65 m_creationTime = time(NULL);
66 m_lastChangeTime = m_creationTime;
67 m_state = state;
68 m_originalSeverity = severity;
69 m_currentSeverity = severity;
70 m_repeatCount = 1;
71 m_helpDeskState = ALARM_HELPDESK_IGNORED;
72 m_helpDeskRef[0] = 0;
73 m_timeout = timeout;
74 m_timeoutEvent = timeoutEvent;
75 m_commentCount = 0;
76 m_ackTimeout = 0;
77 m_ackByUser = 0;
78 m_resolvedByUser = 0;
79 m_termByUser = 0;
80 m_relatedEvents = new IntegerArray<UINT64>(16, 16);
81 m_relatedEvents->add(event->getId());
82 nx_strncpy(m_message, message, MAX_EVENT_MSG_LENGTH);
83 nx_strncpy(m_key, key, MAX_DB_STRING);
84 m_alarmCategoryList = new IntegerArray<UINT32>(alarmCategoryList);
85 }
86
87 /**
88 * Create alarm object from database record
89 */
90 Alarm::Alarm(DB_HANDLE hdb, DB_RESULT hResult, int row)
91 {
92 m_alarmId = DBGetFieldULong(hResult, row, 0);
93 m_sourceObject = DBGetFieldULong(hResult, row, 1);
94 m_sourceEventCode = DBGetFieldULong(hResult, row, 2);
95 m_sourceEventId = DBGetFieldUInt64(hResult, row, 3);
96 DBGetField(hResult, row, 4, m_message, MAX_EVENT_MSG_LENGTH);
97 m_originalSeverity = (BYTE)DBGetFieldLong(hResult, row, 5);
98 m_currentSeverity = (BYTE)DBGetFieldLong(hResult, row, 6);
99 DBGetField(hResult, row, 7, m_key, MAX_DB_STRING);
100 m_creationTime = DBGetFieldULong(hResult, row, 8);
101 m_lastChangeTime = DBGetFieldULong(hResult, row, 9);
102 m_helpDeskState = (BYTE)DBGetFieldLong(hResult, row, 10);
103 DBGetField(hResult, row, 11, m_helpDeskRef, MAX_HELPDESK_REF_LEN);
104 m_ackByUser = DBGetFieldULong(hResult, row, 12);
105 m_repeatCount = DBGetFieldULong(hResult, row, 13);
106 m_state = (BYTE)DBGetFieldLong(hResult, row, 14);
107 m_timeout = DBGetFieldULong(hResult, row, 15);
108 m_timeoutEvent = DBGetFieldULong(hResult, row, 16);
109 m_resolvedByUser = DBGetFieldULong(hResult, row, 17);
110 m_ackTimeout = DBGetFieldULong(hResult, row, 18);
111 m_dciId = DBGetFieldULong(hResult, row, 19);
112
113 m_commentCount = GetCommentCount(hdb, m_alarmId);
114
115 m_termByUser = 0;
116 m_relatedEvents = new IntegerArray<UINT64>(16, 16);
117
118 TCHAR query[256];
119 _sntprintf(query, 256, _T("SELECT event_id FROM alarm_events WHERE alarm_id=%d"), (int)m_alarmId);
120 DB_RESULT eventResult = DBSelect(hdb, query);
121 if (eventResult != NULL)
122 {
123 int count = DBGetNumRows(eventResult);
124 for(int j = 0; j < count; j++)
125 {
126 m_relatedEvents->add(DBGetFieldUInt64(eventResult, j, 0));
127 }
128 DBFreeResult(eventResult);
129 }
130
131 TCHAR categoryList[MAX_DB_STRING];
132 DBGetField(hResult, row, 20, categoryList, MAX_DB_STRING);
133 m_alarmCategoryList = new IntegerArray<UINT32>(16, 16);
134
135 int count;
136 TCHAR **ids = SplitString(categoryList, _T(','), &count);
137 for(int i = 0; i < count; i++)
138 {
139 m_alarmCategoryList->add(_tcstoul(ids[i], NULL, 10));
140 free(ids[i]);
141 }
142 free(ids);
143 }
144
145 /**
146 * Copy constructor
147 */
148 Alarm::Alarm(const Alarm *src, bool copyEvents)
149 {
150 m_sourceEventId = src->m_sourceEventId;
151 m_alarmId = src->m_alarmId;
152 m_creationTime = src->m_creationTime;
153 m_lastChangeTime = src->m_lastChangeTime;
154 m_sourceObject = src->m_sourceObject;
155 m_sourceEventCode = src->m_sourceEventCode;
156 m_dciId = src->m_dciId;
157 m_currentSeverity = src->m_currentSeverity;
158 m_originalSeverity = src->m_originalSeverity;
159 m_state = src->m_state;
160 m_helpDeskState = src->m_helpDeskState;
161 m_ackByUser = src->m_ackByUser;
162 m_resolvedByUser = src->m_resolvedByUser;
163 m_termByUser = src->m_termByUser;
164 m_ackTimeout = src->m_ackTimeout;
165 m_repeatCount = src->m_repeatCount;
166 m_timeout = src->m_timeout;
167 m_timeoutEvent = src->m_timeoutEvent;
168 _tcscpy(m_message, src->m_message);
169 _tcscpy(m_key, src->m_key);
170 _tcscpy(m_helpDeskRef, src->m_helpDeskRef);
171 m_commentCount = src->m_commentCount;
172 if (copyEvents && (src->m_relatedEvents != NULL))
173 {
174 m_relatedEvents = new IntegerArray<UINT64>(src->m_relatedEvents);
175 }
176 else
177 {
178 m_relatedEvents = NULL;
179 }
180 m_alarmCategoryList = new IntegerArray<UINT32>(src->m_alarmCategoryList);
181 }
182
183 /**
184 * Alarm destructor
185 */
186 Alarm::~Alarm()
187 {
188 delete m_relatedEvents;
189 delete m_alarmCategoryList;
190 }
191
192 /**
193 * Convert alarm category list to string
194 */
195 String Alarm::categoryListToString()
196 {
197 String buffer;
198 for(int i = 0; i < m_alarmCategoryList->size(); i++)
199 {
200 if (buffer.length() > 0)
201 buffer.append(_T(','));
202 buffer.append(m_alarmCategoryList->get(i));
203 }
204 return buffer;
205 }
206
207 /**
208 * Check alarm category acl
209 */
210 bool Alarm::checkCategoryAcl(DWORD userId, ClientSession *session) const
211 {
212 if (session->checkSysAccessRights(SYSTEM_ACCESS_VIEW_ALL_ALARMS))
213 {
214 return true;
215 }
216
217 TCHAR szQuery[256];
218 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
219 _sntprintf(szQuery, 256, _T("SELECT category_id FROM alarm_category_acl WHERE user_id=%d"), userId);
220 DB_RESULT hResult = DBSelect(hdb, szQuery);
221
222 if (hResult == NULL)
223 return false;
224
225 UINT32 count = DBGetNumRows(hResult);
226 for(int i = 0; i < count; i++)
227 {
228 if (DBGetFieldULong(hResult, i, 0) == m_alarmCategoryList->get(i))
229 {
230 DBFreeResult(hResult);
231 DBConnectionPoolReleaseConnection(hdb);
232 return true;
233 }
234 }
235 DBFreeResult(hResult);
236 DBConnectionPoolReleaseConnection(hdb);
237
238 return false;
239 }
240
241 /**
242 * Client notification data
243 */
244 struct CLIENT_NOTIFICATION_DATA
245 {
246 UINT32 code;
247 const Alarm *alarm;
248 };
249
250 /**
251 * Callback for client session enumeration
252 */
253 static void SendAlarmNotification(ClientSession *session, void *arg)
254 {
255 session->onAlarmUpdate(((CLIENT_NOTIFICATION_DATA *)arg)->code,
256 ((CLIENT_NOTIFICATION_DATA *)arg)->alarm);
257 }
258
259 /**
260 * Callback for client session enumeration
261 */
262 static void SendBulkAlarmTerminateNotification(ClientSession *session, void *arg)
263 {
264 session->sendMessage((NXCPMessage *)arg);
265 }
266
267 /**
268 * Notify connected clients about changes
269 */
270 static void NotifyClients(UINT32 code, const Alarm *alarm)
271 {
272 CALL_ALL_MODULES(pfAlarmChangeHook, (code, alarm));
273
274 CLIENT_NOTIFICATION_DATA data;
275 data.code = code;
276 data.alarm = alarm;
277 EnumerateClientSessions(SendAlarmNotification, &data);
278 }
279
280 /**
281 * Create alarm record in database
282 */
283 void Alarm::createInDatabase()
284 {
285 TCHAR categoryList[MAX_DB_STRING];
286 categoryList[0] = 0;
287
288 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
289
290 DB_STATEMENT hStmt = DBPrepare(hdb,
291 _T("INSERT INTO alarms (alarm_id,creation_time,last_change_time,")
292 _T("source_object_id,source_event_code,message,original_severity,")
293 _T("current_severity,alarm_key,alarm_state,ack_by,resolved_by,hd_state,")
294 _T("hd_ref,repeat_count,term_by,timeout,timeout_event,source_event_id,")
295 _T("ack_timeout,dci_id,alarm_category_ids) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
296 if (hStmt != NULL)
297 {
298 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_alarmId);
299 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (UINT32)m_creationTime);
300 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (UINT32)m_lastChangeTime);
301 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, m_sourceObject);
302 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, m_sourceEventCode);
303 DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, m_message, DB_BIND_STATIC);
304 DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, (INT32)m_originalSeverity);
305 DBBind(hStmt, 8, DB_SQLTYPE_INTEGER, (INT32)m_currentSeverity);
306 DBBind(hStmt, 9, DB_SQLTYPE_VARCHAR, m_key, DB_BIND_STATIC);
307 DBBind(hStmt, 10, DB_SQLTYPE_INTEGER, (INT32)m_state);
308 DBBind(hStmt, 11, DB_SQLTYPE_INTEGER, m_ackByUser);
309 DBBind(hStmt, 12, DB_SQLTYPE_INTEGER, m_resolvedByUser);
310 DBBind(hStmt, 13, DB_SQLTYPE_INTEGER, (INT32)m_helpDeskState);
311 DBBind(hStmt, 14, DB_SQLTYPE_VARCHAR, m_helpDeskRef, DB_BIND_STATIC);
312 DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, m_repeatCount);
313 DBBind(hStmt, 16, DB_SQLTYPE_INTEGER, m_termByUser);
314 DBBind(hStmt, 17, DB_SQLTYPE_INTEGER, m_timeout);
315 DBBind(hStmt, 18, DB_SQLTYPE_INTEGER, m_timeoutEvent);
316 DBBind(hStmt, 19, DB_SQLTYPE_BIGINT, m_sourceEventId);
317 DBBind(hStmt, 20, DB_SQLTYPE_INTEGER, (UINT32)m_ackTimeout);
318 DBBind(hStmt, 21, DB_SQLTYPE_INTEGER, m_dciId);
319 DBBind(hStmt, 22, DB_SQLTYPE_VARCHAR, categoryListToString(), DB_BIND_TRANSIENT);
320
321 DBExecute(hStmt);
322 DBFreeStatement(hStmt);
323 }
324
325 DBConnectionPoolReleaseConnection(hdb);
326 }
327
328 /**
329 * Update alarm information in database
330 */
331 void Alarm::updateInDatabase()
332 {
333 TCHAR categoryList[MAX_DB_STRING];
334 categoryList[0] = 0;
335
336 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
337
338 DB_STATEMENT hStmt = DBPrepare(hdb,
339 _T("UPDATE alarms SET alarm_state=?,ack_by=?,term_by=?,")
340 _T("last_change_time=?,current_severity=?,repeat_count=?,")
341 _T("hd_state=?,hd_ref=?,timeout=?,timeout_event=?,")
342 _T("message=?,resolved_by=?,ack_timeout=?,source_object_id=?,")
343 _T("dci_id=?,alarm_category_ids=? WHERE alarm_id=?"));
344 if (hStmt != NULL)
345 {
346 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (INT32)m_state);
347 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_ackByUser);
348 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, m_termByUser);
349 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (UINT32)m_lastChangeTime);
350 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (INT32)m_currentSeverity);
351 DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, m_repeatCount);
352 DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, (INT32)m_helpDeskState);
353 DBBind(hStmt, 8, DB_SQLTYPE_VARCHAR, m_helpDeskRef, DB_BIND_STATIC);
354 DBBind(hStmt, 9, DB_SQLTYPE_INTEGER, m_timeout);
355 DBBind(hStmt, 10, DB_SQLTYPE_INTEGER, m_timeoutEvent);
356 DBBind(hStmt, 11, DB_SQLTYPE_VARCHAR, m_message, DB_BIND_STATIC);
357 DBBind(hStmt, 12, DB_SQLTYPE_INTEGER, m_resolvedByUser);
358 DBBind(hStmt, 13, DB_SQLTYPE_INTEGER, (UINT32)m_ackTimeout);
359 DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, m_sourceObject);
360 DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, m_dciId);
361 DBBind(hStmt, 16, DB_SQLTYPE_VARCHAR, categoryListToString(), DB_BIND_TRANSIENT);
362 DBBind(hStmt, 17, DB_SQLTYPE_INTEGER, m_alarmId);
363 DBExecute(hStmt);
364 DBFreeStatement(hStmt);
365 }
366
367 if (m_state == ALARM_STATE_TERMINATED)
368 {
369 TCHAR query[256];
370 _sntprintf(query, 256, _T("DELETE FROM alarm_events WHERE alarm_id=%d"), m_alarmId);
371 QueueSQLRequest(query);
372
373 DeleteAlarmNotes(hdb, m_alarmId);
374 }
375 DBConnectionPoolReleaseConnection(hdb);
376 }
377
378 /**
379 * Fill NXCP message with alarm data
380 */
381 void Alarm::fillMessage(NXCPMessage *msg)
382 {
383 msg->setField(VID_ALARM_ID, m_alarmId);
384 msg->setField(VID_ACK_BY_USER, m_ackByUser);
385 msg->setField(VID_RESOLVED_BY_USER, m_resolvedByUser);
386 msg->setField(VID_TERMINATED_BY_USER, m_termByUser);
387 msg->setField(VID_EVENT_CODE, m_sourceEventCode);
388 msg->setField(VID_EVENT_ID, m_sourceEventId);
389 msg->setField(VID_OBJECT_ID, m_sourceObject);
390 msg->setField(VID_DCI_ID, m_dciId);
391 msg->setFieldFromTime(VID_CREATION_TIME, m_creationTime);
392 msg->setFieldFromTime(VID_LAST_CHANGE_TIME, m_lastChangeTime);
393 msg->setField(VID_ALARM_KEY, m_key);
394 msg->setField(VID_ALARM_MESSAGE, m_message);
395 msg->setField(VID_STATE, (WORD)(m_state & ALARM_STATE_MASK)); // send only state to client, without flags
396 msg->setField(VID_IS_STICKY, (WORD)((m_state & ALARM_STATE_STICKY) ? 1 : 0));
397 msg->setField(VID_CURRENT_SEVERITY, (WORD)m_currentSeverity);
398 msg->setField(VID_ORIGINAL_SEVERITY, (WORD)m_originalSeverity);
399 msg->setField(VID_HELPDESK_STATE, (WORD)m_helpDeskState);
400 msg->setField(VID_HELPDESK_REF, m_helpDeskRef);
401 msg->setField(VID_REPEAT_COUNT, m_repeatCount);
402 msg->setField(VID_ALARM_TIMEOUT, m_timeout);
403 msg->setField(VID_ALARM_TIMEOUT_EVENT, m_timeoutEvent);
404 msg->setField(VID_NUM_COMMENTS, m_commentCount);
405 msg->setField(VID_TIMESTAMP, (UINT32)((m_ackTimeout != 0) ? (m_ackTimeout - time(NULL)) : 0));
406 }
407
408 /**
409 * Update object status after alarm acknowledgment or deletion
410 */
411 static void UpdateObjectStatus(UINT32 objectId)
412 {
413 NetObj *object = FindObjectById(objectId);
414 if (object != NULL)
415 object->calculateCompoundStatus();
416 }
417
418 /**
419 * Fill NXCP message with event data from SQL query
420 * Expected field order: event_id,event_code,event_name,severity,source_object_id,event_timestamp,message
421 */
422 static void FillEventData(NXCPMessage *msg, UINT32 baseId, DB_RESULT hResult, int row, QWORD rootId)
423 {
424 TCHAR buffer[MAX_EVENT_MSG_LENGTH];
425
426 msg->setField(baseId, DBGetFieldUInt64(hResult, row, 0));
427 msg->setField(baseId + 1, rootId);
428 msg->setField(baseId + 2, DBGetFieldULong(hResult, row, 1));
429 msg->setField(baseId + 3, DBGetField(hResult, row, 2, buffer, MAX_DB_STRING));
430 msg->setField(baseId + 4, (WORD)DBGetFieldLong(hResult, row, 3)); // severity
431 msg->setField(baseId + 5, DBGetFieldULong(hResult, row, 4)); // source object
432 msg->setField(baseId + 6, DBGetFieldULong(hResult, row, 5)); // timestamp
433 msg->setField(baseId + 7, DBGetField(hResult, row, 6, buffer, MAX_EVENT_MSG_LENGTH));
434 }
435
436 /**
437 * Get events correlated to given event into NXCP message
438 *
439 * @return number of consumed variable identifiers
440 */
441 static UINT32 GetCorrelatedEvents(QWORD eventId, NXCPMessage *msg, UINT32 baseId, DB_HANDLE hdb)
442 {
443 UINT32 varId = baseId;
444 DB_STATEMENT hStmt = DBPrepare(hdb,
445 (g_dbSyntax == DB_SYNTAX_ORACLE) ?
446 _T("SELECT e.event_id,e.event_code,c.event_name,e.event_severity,e.event_source,e.event_timestamp,e.event_message ")
447 _T("FROM event_log e,event_cfg c WHERE zero_to_null(e.root_event_id)=? AND c.event_code=e.event_code")
448 :
449 _T("SELECT e.event_id,e.event_code,c.event_name,e.event_severity,e.event_source,e.event_timestamp,e.event_message ")
450 _T("FROM event_log e,event_cfg c WHERE e.root_event_id=? AND c.event_code=e.event_code"));
451 if (hStmt != NULL)
452 {
453 DBBind(hStmt, 1, DB_SQLTYPE_BIGINT, eventId);
454 DB_RESULT hResult = DBSelectPrepared(hStmt);
455 if (hResult != NULL)
456 {
457 int count = DBGetNumRows(hResult);
458 for(int i = 0; i < count; i++)
459 {
460 FillEventData(msg, varId, hResult, i, eventId);
461 varId += 10;
462 QWORD eventId = DBGetFieldUInt64(hResult, i, 0);
463 varId += GetCorrelatedEvents(eventId, msg, varId, hdb);
464 }
465 DBFreeResult(hResult);
466 }
467 DBFreeStatement(hStmt);
468 }
469 return varId - baseId;
470 }
471
472 /**
473 * Fill NXCP message with alarm's related events
474 */
475 static void FillAlarmEventsMessage(NXCPMessage *msg, UINT32 alarmId)
476 {
477 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
478 const TCHAR *query;
479 switch(g_dbSyntax)
480 {
481 case DB_SYNTAX_ORACLE:
482 query = _T("SELECT * FROM (SELECT event_id,event_code,event_name,severity,source_object_id,event_timestamp,message FROM alarm_events WHERE alarm_id=? ORDER BY event_timestamp DESC) WHERE ROWNUM<=200");
483 break;
484 case DB_SYNTAX_MSSQL:
485 query = _T("SELECT TOP 200 event_id,event_code,event_name,severity,source_object_id,event_timestamp,message FROM alarm_events WHERE alarm_id=? ORDER BY event_timestamp DESC");
486 break;
487 case DB_SYNTAX_DB2:
488 query = _T("SELECT event_id,event_code,event_name,severity,source_object_id,event_timestamp,message ")
489 _T("FROM alarm_events WHERE alarm_id=? ORDER BY event_timestamp DESC FETCH FIRST 200 ROWS ONLY");
490 break;
491 default:
492 query = _T("SELECT event_id,event_code,event_name,severity,source_object_id,event_timestamp,message FROM alarm_events WHERE alarm_id=? ORDER BY event_timestamp DESC LIMIT 200");
493 break;
494 }
495 DB_STATEMENT hStmt = DBPrepare(hdb, query);
496 if (hStmt != NULL)
497 {
498 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, alarmId);
499 DB_RESULT hResult = DBSelectPrepared(hStmt);
500 if (hResult != NULL)
501 {
502 int count = DBGetNumRows(hResult);
503 UINT32 varId = VID_ELEMENT_LIST_BASE;
504 for(int i = 0; i < count; i++)
505 {
506 FillEventData(msg, varId, hResult, i, 0);
507 varId += 10;
508 QWORD eventId = DBGetFieldUInt64(hResult, i, 0);
509 varId += GetCorrelatedEvents(eventId, msg, varId, hdb);
510 }
511 DBFreeResult(hResult);
512 msg->setField(VID_NUM_ELEMENTS, (varId - VID_ELEMENT_LIST_BASE) / 10);
513 }
514 DBFreeStatement(hStmt);
515 }
516 DBConnectionPoolReleaseConnection(hdb);
517 }
518
519 /**
520 * Update existing alarm from event
521 */
522 void Alarm::updateFromEvent(Event *event, int state, int severity, UINT32 timeout, UINT32 timeoutEvent, UINT32 ackTimeout, const TCHAR *message, IntegerArray<UINT32> *alarmCategoryList)
523 {
524 m_repeatCount++;
525 m_lastChangeTime = (UINT32)time(NULL);
526 m_sourceObject = event->getSourceId();
527 m_dciId = event->getDciId();
528 if ((m_state & ALARM_STATE_STICKY) == 0)
529 m_state = state;
530 m_currentSeverity = severity;
531 m_timeout = timeout;
532 m_timeoutEvent = timeoutEvent;
533 m_ackTimeout = ackTimeout;
534 nx_strncpy(m_message, message, MAX_EVENT_MSG_LENGTH);
535 m_alarmCategoryList = new IntegerArray<UINT32>(alarmCategoryList);
536
537 NotifyClients(NX_NOTIFY_ALARM_CHANGED, this);
538 updateInDatabase();
539 }
540
541 /**
542 * Create new alarm
543 */
544 void NXCORE_EXPORTABLE CreateNewAlarm(TCHAR *message, TCHAR *key, int state, int severity, UINT32 timeout,
545 UINT32 timeoutEvent, Event *event, UINT32 ackTimeout, IntegerArray<UINT32> *alarmCategoryList)
546 {
547 UINT32 alarmId = 0;
548 bool newAlarm = true;
549 bool updateRelatedEvent = false;
550
551 // Expand alarm's message and key
552 TCHAR *pszExpMsg = event->expandText(message);
553 TCHAR *pszExpKey = event->expandText(key);
554
555 // Check if we have a duplicate alarm
556 if (((state & ALARM_STATE_MASK) != ALARM_STATE_TERMINATED) && (*pszExpKey != 0))
557 {
558 MutexLock(m_mutex);
559
560 for(int i = 0; i < m_alarmList->size(); i++)
561 {
562 Alarm *alarm = m_alarmList->get(i);
563 if (!_tcscmp(pszExpKey, alarm->getKey()))
564 {
565 alarm->updateFromEvent(event, state, severity, timeout, timeoutEvent, ackTimeout, pszExpMsg, alarmCategoryList);
566 if (!alarm->isEventRelated(event->getId()))
567 {
568 alarmId = alarm->getAlarmId(); // needed for correct update of related events
569 updateRelatedEvent = true;
570 alarm->addRelatedEvent(event->getId());
571 }
572
573 newAlarm = false;
574 break;
575 }
576 }
577
578 MutexUnlock(m_mutex);
579 }
580
581 if (newAlarm)
582 {
583 // Create new alarm structure
584 Alarm *alarm = new Alarm(event, pszExpMsg, pszExpKey, state, severity, timeout, timeoutEvent, ackTimeout, alarmCategoryList);
585 alarmId = alarm->getAlarmId();
586
587 // Add new alarm to active alarm list if needed
588 if ((alarm->getState() & ALARM_STATE_MASK) != ALARM_STATE_TERMINATED)
589 {
590 MutexLock(m_mutex);
591 DbgPrintf(7, _T("AlarmManager: adding new active alarm, current alarm count %d"), m_alarmList->size());
592 m_alarmList->add(alarm);
593 MutexUnlock(m_mutex);
594 }
595
596 alarm->createInDatabase();
597 updateRelatedEvent = true;
598
599 // Notify connected clients about new alarm
600 NotifyClients(NX_NOTIFY_NEW_ALARM, alarm);
601 }
602
603 // Update status of related object if needed
604 if ((state & ALARM_STATE_MASK) != ALARM_STATE_TERMINATED)
605 UpdateObjectStatus(event->getSourceId());
606
607 if (updateRelatedEvent)
608 {
609 // Add record to alarm_events table
610 TCHAR valAlarmId[16], valEventId[32], valEventCode[16], valSeverity[16], valSource[16], valTimestamp[16];
611 const TCHAR *values[8] = { valAlarmId, valEventId, valEventCode, event->getName(), valSeverity, valSource, valTimestamp, event->getMessage() };
612 _sntprintf(valAlarmId, 16, _T("%d"), (int)alarmId);
613 _sntprintf(valEventId, 32, UINT64_FMT, event->getId());
614 _sntprintf(valEventCode, 16, _T("%d"), (int)event->getCode());
615 _sntprintf(valSeverity, 16, _T("%d"), (int)event->getSeverity());
616 _sntprintf(valSource, 16, _T("%d"), event->getSourceId());
617 _sntprintf(valTimestamp, 16, _T("%u"), (UINT32)event->getTimeStamp());
618 static int sqlTypes[8] = { DB_SQLTYPE_INTEGER, DB_SQLTYPE_BIGINT, DB_SQLTYPE_INTEGER, DB_SQLTYPE_VARCHAR, DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER, DB_SQLTYPE_VARCHAR };
619 QueueSQLRequest(_T("INSERT INTO alarm_events (alarm_id,event_id,event_code,event_name,severity,source_object_id,event_timestamp,message) VALUES (?,?,?,?,?,?,?,?)"),
620 8, sqlTypes, values);
621 }
622
623 free(pszExpMsg);
624 free(pszExpKey);
625 }
626
627 /**
628 * Do acknowledge
629 */
630 UINT32 Alarm::acknowledge(ClientSession *session, bool sticky, UINT32 acknowledgmentActionTime)
631 {
632 if ((m_state & ALARM_STATE_MASK) != ALARM_STATE_OUTSTANDING)
633 return RCC_ALARM_NOT_OUTSTANDING;
634
635 if (session != NULL)
636 {
637 WriteAuditLog(AUDIT_OBJECTS, TRUE, session->getUserId(), session->getWorkstation(), session->getId(), m_sourceObject,
638 _T("Acknowledged alarm %d (%s) on object %s"), m_alarmId, m_message,
639 GetObjectName(m_sourceObject, _T("")));
640 }
641
642 UINT32 endTime = acknowledgmentActionTime != 0 ? (UINT32)time(NULL) + acknowledgmentActionTime : 0;
643 m_ackTimeout = endTime;
644 m_state = ALARM_STATE_ACKNOWLEDGED;
645 if (sticky)
646 m_state |= ALARM_STATE_STICKY;
647 m_ackByUser = (session != NULL) ? session->getUserId() : 0;
648 m_lastChangeTime = (UINT32)time(NULL);
649 NotifyClients(NX_NOTIFY_ALARM_CHANGED, this);
650 updateInDatabase();
651 return RCC_SUCCESS;
652 }
653
654 /**
655 * Acknowledge alarm with given ID
656 */
657 UINT32 NXCORE_EXPORTABLE AckAlarmById(UINT32 alarmId, ClientSession *session, bool sticky, UINT32 acknowledgmentActionTime)
658 {
659 UINT32 dwObject, dwRet = RCC_INVALID_ALARM_ID;
660
661 MutexLock(m_mutex);
662 for(int i = 0; i < m_alarmList->size(); i++)
663 {
664 Alarm *alarm = m_alarmList->get(i);
665 if (alarm->getAlarmId() == alarmId)
666 {
667 dwRet = alarm->acknowledge(session, sticky, acknowledgmentActionTime);
668 dwObject = alarm->getSourceObject();
669 break;
670 }
671 }
672 MutexUnlock(m_mutex);
673
674 if (dwRet == RCC_SUCCESS)
675 UpdateObjectStatus(dwObject);
676 return dwRet;
677 }
678
679 /**
680 * Acknowledge alarm with given helpdesk reference
681 */
682 UINT32 NXCORE_EXPORTABLE AckAlarmByHDRef(const TCHAR *hdref, ClientSession *session, bool sticky, UINT32 acknowledgmentActionTime)
683 {
684 UINT32 dwObject, dwRet = RCC_INVALID_ALARM_ID;
685
686 MutexLock(m_mutex);
687 for(int i = 0; i < m_alarmList->size(); i++)
688 {
689 Alarm *alarm = m_alarmList->get(i);
690 if (!_tcscmp(alarm->getHelpDeskRef(), hdref))
691 {
692 dwRet = alarm->acknowledge(session, sticky, acknowledgmentActionTime);
693 dwObject = alarm->getSourceObject();
694 break;
695 }
696 }
697 MutexUnlock(m_mutex);
698
699 if (dwRet == RCC_SUCCESS)
700 UpdateObjectStatus(dwObject);
701 return dwRet;
702 }
703
704 /**
705 * Resolve alarm
706 */
707 void Alarm::resolve(UINT32 userId, Event *event, bool terminate)
708 {
709 if (terminate)
710 m_termByUser = userId;
711 else
712 m_resolvedByUser = userId;
713 m_lastChangeTime = (UINT32)time(NULL);
714 m_state = terminate ? ALARM_STATE_TERMINATED : ALARM_STATE_RESOLVED;
715 m_ackTimeout = 0;
716 if (m_helpDeskState != ALARM_HELPDESK_IGNORED)
717 m_helpDeskState = ALARM_HELPDESK_CLOSED;
718 if (!terminate)
719 NotifyClients(terminate ? NX_NOTIFY_ALARM_TERMINATED : NX_NOTIFY_ALARM_CHANGED, this);
720 updateInDatabase();
721
722 if (!terminate && (event != NULL) && (m_relatedEvents != NULL) && !m_relatedEvents->contains(event->getId()))
723 {
724 // Add record to alarm_events table if alarm is resolved
725 m_relatedEvents->add(event->getId());
726
727 TCHAR valAlarmId[16], valEventId[32], valEventCode[16], valSeverity[16], valSource[16], valTimestamp[16];
728 const TCHAR *values[8] = { valAlarmId, valEventId, valEventCode, event->getName(), valSeverity, valSource, valTimestamp, event->getMessage() };
729 _sntprintf(valAlarmId, 16, _T("%d"), (int)m_alarmId);
730 _sntprintf(valEventId, 32, UINT64_FMT, event->getId());
731 _sntprintf(valEventCode, 16, _T("%d"), (int)event->getCode());
732 _sntprintf(valSeverity, 16, _T("%d"), (int)event->getSeverity());
733 _sntprintf(valSource, 16, _T("%d"), event->getSourceId());
734 _sntprintf(valTimestamp, 16, _T("%u"), (UINT32)event->getTimeStamp());
735 static int sqlTypes[8] = { DB_SQLTYPE_INTEGER, DB_SQLTYPE_BIGINT, DB_SQLTYPE_INTEGER, DB_SQLTYPE_VARCHAR, DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER, DB_SQLTYPE_VARCHAR };
736 QueueSQLRequest(_T("INSERT INTO alarm_events (alarm_id,event_id,event_code,event_name,severity,source_object_id,event_timestamp,message) VALUES (?,?,?,?,?,?,?,?)"),
737 8, sqlTypes, values);
738 }
739 }
740
741 /**
742 * Resolve and possibly terminate alarm with given ID
743 * Should return RCC which can be sent to client
744 */
745 UINT32 NXCORE_EXPORTABLE ResolveAlarmById(UINT32 alarmId, NXCPMessage *msg, ClientSession *session, bool terminate)
746 {
747 IntegerArray<UINT32> list(1);
748 list.add(alarmId);
749 return ResolveAlarmsById(&list, msg, session, terminate);
750 }
751
752 /**
753 * Resolve and possibly terminate alarms with given ID
754 * Should return RCC which can be sent to client
755 */
756 UINT32 NXCORE_EXPORTABLE ResolveAlarmsById(IntegerArray<UINT32> *alarmIds, NXCPMessage *msg, ClientSession *session, bool terminate)
757 {
758 UINT32 base = VID_ALARM_BULK_TERMINATE_BASE, dwObject, result = RCC_INVALID_ALARM_ID;
759 IntegerArray<UINT32> accessRightFail, idCheckFail, openInHelpdesk;
760 NXCPMessage notification;
761
762 MutexLock(m_mutex);
763 for(int i = 0; i < alarmIds->size(); i++)
764 {
765 int n;
766 for(n = 0; n < m_alarmList->size(); n++)
767 {
768 Alarm *alarm = m_alarmList->get(n);
769 NetObj *object = GetAlarmSourceObject(alarmIds->get(i), true);
770 if (alarm->getAlarmId() == alarmIds->get(i))
771 {
772 // If alarm is open in helpdesk, it cannot be terminated
773 if (alarm->getHelpDeskState() != ALARM_HELPDESK_OPEN)
774 {
775 dwObject = alarm->getSourceObject();
776 if (session != NULL)
777 {
778 // If user does not have the required object access rights, the alarm cannot be terminated
779 if (!object->checkAccessRights(session->getUserId(), terminate ? OBJECT_ACCESS_TERM_ALARMS : OBJECT_ACCESS_UPDATE_ALARMS))
780 {
781 accessRightFail.add(alarmIds->get(i));
782 continue;
783 }
784
785 WriteAuditLog(AUDIT_OBJECTS, TRUE, session->getUserId(), session->getWorkstation(), session->getId(), dwObject,
786 _T("%s alarm %d (%s) on object %s"), terminate ? _T("Terminated") : _T("Resolved"),
787 alarm->getAlarmId(), alarm->getMessage(), GetObjectName(dwObject, _T("")));
788 }
789
790 alarm->resolve((session != NULL) ? session->getUserId() : 0, NULL, terminate);
791 result = RCC_SUCCESS;
792 if (terminate)
793 {
794 notification.setField(base, alarm->getAlarmId());
795 base++;
796 m_alarmList->remove(n);
797 n--;
798 }
799 }
800 else
801 {
802 openInHelpdesk.add(alarmIds->get(i));
803 }
804 break;
805 }
806 }
807 if (n == m_alarmList->size())
808 {
809 idCheckFail.add(alarmIds->get(i));
810 }
811 }
812
813 // if alarms are terminated, this session notification method is used
814 if (terminate)
815 {
816 notification.setCode(CMD_ALARM_BULK_TERMINATE);
817 notification.setField(VID_NOTIFICATION_CODE, NX_NOTIFY_ALARM_TERMINATED);
818
819 UINT32 numRecords = base - VID_ALARM_BULK_TERMINATE_BASE;
820
821 notification.setField(VID_NUM_RECORDS, numRecords);
822 EnumerateClientSessions(SendBulkAlarmTerminateNotification, &notification);
823 result = RCC_SUCCESS;
824 MutexUnlock(m_mutex);
825 }
826
827 // Add fields if there were alarms that could not be terminated
828 if (msg != NULL)
829 {
830 if (accessRightFail.size() > 0)
831 {
832 msg->setFieldFromInt32Array(VID_ACCESS_RIGHT_FAIL, &accessRightFail);
833 }
834 if (openInHelpdesk.size() > 0)
835 {
836 msg->setFieldFromInt32Array(VID_OPEN_IN_HELPDESK, &openInHelpdesk);
837 }
838 if (idCheckFail.size() > 0)
839 {
840 msg->setFieldFromInt32Array(VID_ID_CHECK_FAIL, &idCheckFail);
841 }
842 }
843
844 UpdateObjectStatus(dwObject);
845 return result;
846 }
847
848 /**
849 * Resolve and possibly terminate all alarms with given key
850 */
851 void NXCORE_EXPORTABLE ResolveAlarmByKey(const TCHAR *pszKey, bool useRegexp, bool terminate, Event *pEvent)
852 {
853 UINT32 *pdwObjectList = (UINT32 *)malloc(sizeof(UINT32) * m_alarmList->size());
854
855 MutexLock(m_mutex);
856 int numObjects = 0;
857 for(int i = 0; i < m_alarmList->size(); i++)
858 {
859 Alarm *alarm = m_alarmList->get(i);
860 if ((useRegexp ? RegexpMatch(alarm->getKey(), pszKey, TRUE) : !_tcscmp(pszKey, alarm->getKey())) &&
861 (alarm->getHelpDeskState() != ALARM_HELPDESK_OPEN))
862 {
863 // Add alarm's source object to update list
864 int j;
865 for(j = 0; j < numObjects; j++)
866 {
867 if (pdwObjectList[j] == alarm->getSourceObject())
868 break;
869 }
870 if (j == numObjects)
871 {
872 pdwObjectList[numObjects++] = alarm->getSourceObject();
873 }
874
875 // Resolve or terminate alarm
876 alarm->resolve(0, pEvent, terminate);
877 if (terminate)
878 {
879 m_alarmList->remove(i);
880 i--;
881 }
882 }
883 }
884 MutexUnlock(m_mutex);
885
886 // Update status of objects
887 for(int i = 0; i < numObjects; i++)
888 UpdateObjectStatus(pdwObjectList[i]);
889 free(pdwObjectList);
890 }
891
892 /**
893 * Resolve and possibly terminate alarm with given helpdesk reference.
894 * Automatically change alarm's helpdesk state to "closed"
895 */
896 UINT32 NXCORE_EXPORTABLE ResolveAlarmByHDRef(const TCHAR *hdref, ClientSession *session, bool terminate)
897 {
898 UINT32 objectId = 0;
899 UINT32 rcc = RCC_INVALID_ALARM_ID;
900
901 MutexLock(m_mutex);
902 for(int i = 0; i < m_alarmList->size(); i++)
903 {
904 Alarm *alarm = m_alarmList->get(i);
905 if (!_tcscmp(alarm->getHelpDeskRef(), hdref))
906 {
907 objectId = alarm->getSourceObject();
908 if (session != NULL)
909 {
910 WriteAuditLog(AUDIT_OBJECTS, TRUE, session->getUserId(), session->getWorkstation(), session->getId(), objectId,
911 _T("%s alarm %d (%s) on object %s"), terminate ? _T("Terminated") : _T("Resolved"),
912 alarm->getAlarmId(), alarm->getMessage(), GetObjectName(objectId, _T("")));
913 }
914
915 alarm->resolve((session != NULL) ? session->getUserId() : 0, NULL, terminate);
916 if (terminate)
917 {
918 m_alarmList->remove(i);
919 }
920 DbgPrintf(5, _T("Alarm with helpdesk reference \"%s\" %s"), hdref, terminate ? _T("terminated") : _T("resolved"));
921 rcc = RCC_SUCCESS;
922 break;
923 }
924 }
925 MutexUnlock(m_mutex);
926
927 if (objectId != 0)
928 UpdateObjectStatus(objectId);
929 return rcc;
930 }
931
932 /**
933 * Resolve alarm by helpdesk reference
934 */
935 UINT32 NXCORE_EXPORTABLE ResolveAlarmByHDRef(const TCHAR *hdref)
936 {
937 return ResolveAlarmByHDRef(hdref, NULL, false);
938 }
939
940 /**
941 * Terminate alarm by helpdesk reference
942 */
943 UINT32 NXCORE_EXPORTABLE TerminateAlarmByHDRef(const TCHAR *hdref)
944 {
945 return ResolveAlarmByHDRef(hdref, NULL, true);
946 }
947
948 /**
949 * Open issue in helpdesk system
950 */
951 UINT32 Alarm::openHelpdeskIssue(ClientSession *session, TCHAR *hdref)
952 {
953 UINT32 rcc;
954 if (m_helpDeskState == ALARM_HELPDESK_IGNORED)
955 {
956 /* TODO: unlock alarm list before call */
957 const TCHAR *nodeName = GetObjectName(m_sourceObject, _T("[unknown]"));
958 int messageLen = (int)(_tcslen(nodeName) + _tcslen(m_message) + 32) * sizeof(TCHAR);
959 TCHAR *message = (TCHAR *)malloc(messageLen);
960 _sntprintf(message, messageLen, _T("%s: %s"), nodeName, m_message);
961 rcc = CreateHelpdeskIssue(message, m_helpDeskRef);
962 free(message);
963 if (rcc == RCC_SUCCESS)
964 {
965 m_helpDeskState = ALARM_HELPDESK_OPEN;
966 NotifyClients(NX_NOTIFY_ALARM_CHANGED, this);
967 updateInDatabase();
968 nx_strncpy(hdref, m_helpDeskRef, MAX_HELPDESK_REF_LEN);
969 DbgPrintf(5, _T("Helpdesk issue created for alarm %d, reference \"%s\""), m_alarmId, m_helpDeskRef);
970 }
971 }
972 else
973 {
974 rcc = RCC_OUT_OF_STATE_REQUEST;
975 }
976 return rcc;
977 }
978
979 /**
980 * Open issue in helpdesk system
981 */
982 UINT32 OpenHelpdeskIssue(UINT32 alarmId, ClientSession *session, TCHAR *hdref)
983 {
984 UINT32 rcc = RCC_INVALID_ALARM_ID;
985 *hdref = 0;
986
987 MutexLock(m_mutex);
988 for(int i = 0; i < m_alarmList->size(); i++)
989 {
990 Alarm *alarm = m_alarmList->get(i);
991 if (alarm->getAlarmId() == alarmId)
992 {
993 rcc = alarm->openHelpdeskIssue(session, hdref);
994 break;
995 }
996 }
997 MutexUnlock(m_mutex);
998 return rcc;
999 }
1000
1001 /**
1002 * Get helpdesk issue URL for given alarm
1003 */
1004 UINT32 GetHelpdeskIssueUrlFromAlarm(UINT32 alarmId, UINT32 userId, TCHAR *url, size_t size, ClientSession *session)
1005 {
1006 UINT32 rcc = RCC_INVALID_ALARM_ID;
1007
1008 MutexLock(m_mutex);
1009 for(int i = 0; i < m_alarmList->size(); i++)
1010 {
1011 Alarm *alarm = m_alarmList->get(i);
1012 if (alarm->checkCategoryAcl(userId, session))
1013 {
1014 rcc = RCC_ACCESS_DENIED;
1015 break;
1016 }
1017 if (alarm->getAlarmId() == alarmId)
1018 {
1019 if ((alarm->getHelpDeskState() != ALARM_HELPDESK_IGNORED) && (alarm->getHelpDeskRef()[0] != 0))
1020 {
1021 rcc = GetHelpdeskIssueUrl(alarm->getHelpDeskRef(), url, size);
1022 }
1023 else
1024 {
1025 rcc = RCC_OUT_OF_STATE_REQUEST;
1026 }
1027 break;
1028 }
1029 }
1030 MutexUnlock(m_mutex);
1031 return rcc;
1032 }
1033
1034 /**
1035 * Unlink helpdesk issue from alarm
1036 */
1037 UINT32 UnlinkHelpdeskIssueById(UINT32 alarmId, ClientSession *session)
1038 {
1039 UINT32 rcc = RCC_INVALID_ALARM_ID;
1040
1041 MutexLock(m_mutex);
1042 for(int i = 0; i < m_alarmList->size(); i++)
1043 {
1044 Alarm *alarm = m_alarmList->get(i);
1045 if (alarm->getAlarmId() == alarmId)
1046 {
1047 if (session != NULL)
1048 {
1049 WriteAuditLog(AUDIT_OBJECTS, TRUE, session->getUserId(), session->getWorkstation(), session->getId(),
1050 alarm->getSourceObject(), _T("Helpdesk issue %s unlinked from alarm %d (%s) on object %s"),
1051 alarm->getHelpDeskRef(), alarm->getAlarmId(), alarm->getMessage(),
1052 GetObjectName(alarm->getSourceObject(), _T("")));
1053 }
1054 alarm->unlinkFromHelpdesk();
1055 NotifyClients(NX_NOTIFY_ALARM_CHANGED, alarm);
1056 alarm->updateInDatabase();
1057 rcc = RCC_SUCCESS;
1058 break;
1059 }
1060 }
1061 MutexUnlock(m_mutex);
1062
1063 return rcc;
1064 }
1065
1066 /**
1067 * Unlink helpdesk issue from alarm
1068 */
1069 UINT32 UnlinkHelpdeskIssueByHDRef(const TCHAR *hdref, ClientSession *session)
1070 {
1071 UINT32 rcc = RCC_INVALID_ALARM_ID;
1072
1073 MutexLock(m_mutex);
1074 for(int i = 0; i < m_alarmList->size(); i++)
1075 {
1076 Alarm *alarm = m_alarmList->get(i);
1077 if (!_tcscmp(alarm->getHelpDeskRef(), hdref))
1078 {
1079 if (session != NULL)
1080 {
1081 WriteAuditLog(AUDIT_OBJECTS, TRUE, session->getUserId(), session->getWorkstation(), session->getId(),
1082 alarm->getSourceObject(), _T("Helpdesk issue %s unlinked from alarm %d (%s) on object %s"),
1083 alarm->getHelpDeskRef(), alarm->getAlarmId(), alarm->getMessage(),
1084 GetObjectName(alarm->getSourceObject(), _T("")));
1085 }
1086 alarm->unlinkFromHelpdesk();
1087 NotifyClients(NX_NOTIFY_ALARM_CHANGED, alarm);
1088 alarm->updateInDatabase();
1089 rcc = RCC_SUCCESS;
1090 break;
1091 }
1092 }
1093 MutexUnlock(m_mutex);
1094
1095 return rcc;
1096 }
1097
1098 /**
1099 * Delete alarm with given ID
1100 */
1101 void NXCORE_EXPORTABLE DeleteAlarm(UINT32 alarmId, bool objectCleanup)
1102 {
1103 DWORD dwObject;
1104
1105 // Delete alarm from in-memory list
1106 if (!objectCleanup) // otherwise already locked
1107 MutexLock(m_mutex);
1108 for(int i = 0; i < m_alarmList->size(); i++)
1109 {
1110 Alarm *alarm = m_alarmList->get(i);
1111 if (alarm->getAlarmId() == alarmId)
1112 {
1113 dwObject = alarm->getSourceObject();
1114 NotifyClients(NX_NOTIFY_ALARM_DELETED, alarm);
1115 m_alarmList->remove(i);
1116 break;
1117 }
1118 }
1119 if (!objectCleanup)
1120 MutexUnlock(m_mutex);
1121
1122 // Delete from database
1123 if (!objectCleanup)
1124 {
1125 TCHAR szQuery[256];
1126
1127 _sntprintf(szQuery, 256, _T("DELETE FROM alarms WHERE alarm_id=%d"), (int)alarmId);
1128 QueueSQLRequest(szQuery);
1129 _sntprintf(szQuery, 256, _T("DELETE FROM alarm_events WHERE alarm_id=%d"), (int)alarmId);
1130 QueueSQLRequest(szQuery);
1131
1132 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1133 DeleteAlarmNotes(hdb, alarmId);
1134 DBConnectionPoolReleaseConnection(hdb);
1135
1136 UpdateObjectStatus(dwObject);
1137 }
1138 }
1139
1140 /**
1141 * Delete all alarms of given object. Intended to be called only
1142 * on final stage of object deletion.
1143 */
1144 bool DeleteObjectAlarms(UINT32 objectId, DB_HANDLE hdb)
1145 {
1146 MutexLock(m_mutex);
1147
1148 // go through from end because m_alarmList->size() is decremented by DeleteAlarm()
1149 for(int i = m_alarmList->size() - 1; i >= 0; i--)
1150 {
1151 Alarm *alarm = m_alarmList->get(i);
1152 if (alarm->getSourceObject() == objectId)
1153 {
1154 DeleteAlarm(alarm->getAlarmId(), true);
1155 }
1156 }
1157
1158 MutexUnlock(m_mutex);
1159
1160 // Delete all object alarms from database
1161 bool success = false;
1162 DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT alarm_id FROM alarms WHERE source_object_id=?"));
1163 if (hStmt != NULL)
1164 {
1165 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, objectId);
1166 DB_RESULT hResult = DBSelectPrepared(hStmt);
1167 if (hResult != NULL)
1168 {
1169 success = true;
1170 int count = DBGetNumRows(hResult);
1171 for(int i = 0; i < count; i++)
1172 {
1173 UINT32 alarmId = DBGetFieldULong(hResult, i, 0);
1174 DeleteAlarmNotes(hdb, alarmId);
1175 DeleteAlarmEvents(hdb, alarmId);
1176 }
1177 DBFreeResult(hResult);
1178 }
1179 DBFreeStatement(hStmt);
1180 }
1181
1182 if (success)
1183 {
1184 hStmt = DBPrepare(hdb, _T("DELETE FROM alarms WHERE source_object_id=?"));
1185 if (hStmt != NULL)
1186 {
1187 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, objectId);
1188 success = DBExecute(hStmt) ? true : false;
1189 DBFreeStatement(hStmt);
1190 }
1191 }
1192 return success;
1193 }
1194
1195 /**
1196 * Send all alarms to client
1197 */
1198 void SendAlarmsToClient(UINT32 dwRqId, ClientSession *pSession)
1199 {
1200 DWORD dwUserId = pSession->getUserId();
1201
1202 // Prepare message
1203 NXCPMessage msg;
1204 msg.setCode(CMD_ALARM_DATA);
1205 msg.setId(dwRqId);
1206
1207 MutexLock(m_mutex);
1208 for(int i = 0; i < m_alarmList->size(); i++)
1209 {
1210 Alarm *alarm = m_alarmList->get(i);
1211 NetObj *pObject = FindObjectById(alarm->getSourceObject());
1212 if (pObject != NULL)
1213 {
1214 if (pObject->checkAccessRights(dwUserId, OBJECT_ACCESS_READ_ALARMS) && alarm->checkCategoryAcl(dwUserId, pSession))
1215 {
1216 alarm->fillMessage(&msg);
1217 pSession->sendMessage(&msg);
1218 msg.deleteAllFields();
1219 }
1220 }
1221 }
1222 MutexUnlock(m_mutex);
1223
1224 // Send end-of-list indicator
1225 msg.setField(VID_ALARM_ID, (UINT32)0);
1226 pSession->sendMessage(&msg);
1227 }
1228
1229 /**
1230 * Get alarm with given ID into NXCP message
1231 * Should return RCC that can be sent to client
1232 */
1233 UINT32 NXCORE_EXPORTABLE GetAlarm(UINT32 alarmId, UINT32 userId, NXCPMessage *msg, ClientSession *session)
1234 {
1235 UINT32 dwRet = RCC_INVALID_ALARM_ID;
1236
1237 MutexLock(m_mutex);
1238 for(int i = 0; i < m_alarmList->size(); i++)
1239 {
1240 Alarm *alarm = m_alarmList->get(i);
1241 if (alarm->checkCategoryAcl(userId, session))
1242 {
1243 dwRet = RCC_ACCESS_DENIED;
1244 break;
1245 }
1246 if (alarm->getAlarmId() == alarmId)
1247 {
1248 alarm->fillMessage(msg);
1249 dwRet = RCC_SUCCESS;
1250 break;
1251 }
1252 }
1253 MutexUnlock(m_mutex);
1254
1255 return dwRet;
1256 }
1257
1258 /**
1259 * Get all related events for alarm with given ID into NXCP message
1260 * Should return RCC that can be sent to client
1261 */
1262 UINT32 NXCORE_EXPORTABLE GetAlarmEvents(UINT32 alarmId, UINT32 userId, NXCPMessage *msg, ClientSession *session)
1263 {
1264 UINT32 dwRet = RCC_INVALID_ALARM_ID;
1265
1266 MutexLock(m_mutex);
1267 for(int i = 0; i < m_alarmList->size(); i++)
1268 {
1269 if (m_alarmList->get(i)->checkCategoryAcl(userId, session))
1270 {
1271 dwRet = RCC_ACCESS_DENIED;
1272 break;
1273 }
1274 if (m_alarmList->get(i)->getAlarmId() == alarmId)
1275 {
1276 dwRet = RCC_SUCCESS;
1277 break;
1278 }
1279 }
1280
1281 MutexUnlock(m_mutex);
1282
1283 // we don't call FillAlarmEventsMessage from within loop
1284 // to prevent alarm list lock for a long time
1285 if (dwRet == RCC_SUCCESS)
1286 FillAlarmEventsMessage(msg, alarmId);
1287
1288 return dwRet;
1289 }
1290
1291 /**
1292 * Get source object for given alarm id
1293 */
1294 NetObj NXCORE_EXPORTABLE *GetAlarmSourceObject(UINT32 alarmId, bool alreadyLocked)
1295 {
1296 UINT32 dwObjectId = 0;
1297
1298 if(!alreadyLocked)
1299 MutexLock(m_mutex);
1300 for(int i = 0; i < m_alarmList->size(); i++)
1301 {
1302 Alarm *alarm = m_alarmList->get(i);
1303 if (alarm->getAlarmId() == alarmId)
1304 {
1305 dwObjectId = alarm->getSourceObject();
1306 break;
1307 }
1308 }
1309
1310 if(!alreadyLocked)
1311 MutexUnlock(m_mutex);
1312 return (dwObjectId != 0) ? FindObjectById(dwObjectId) : NULL;
1313 }
1314
1315 /**
1316 * Get source object for given alarm helpdesk reference
1317 */
1318 NetObj NXCORE_EXPORTABLE *GetAlarmSourceObject(const TCHAR *hdref)
1319 {
1320 UINT32 dwObjectId = 0;
1321
1322 MutexLock(m_mutex);
1323 for(int i = 0; i < m_alarmList->size(); i++)
1324 {
1325 Alarm *alarm = m_alarmList->get(i);
1326 if (!_tcscmp(alarm->getHelpDeskRef(), hdref))
1327 {
1328 dwObjectId = alarm->getSourceObject();
1329 break;
1330 }
1331 }
1332 MutexUnlock(m_mutex);
1333 return (dwObjectId != 0) ? FindObjectById(dwObjectId) : NULL;
1334 }
1335
1336 /**
1337 * Get most critical status among active alarms for given object
1338 * Will return STATUS_UNKNOWN if there are no active alarms
1339 */
1340 int GetMostCriticalStatusForObject(UINT32 dwObjectId)
1341 {
1342 int iStatus = STATUS_UNKNOWN;
1343
1344 MutexLock(m_mutex);
1345 for(int i = 0; i < m_alarmList->size(); i++)
1346 {
1347 Alarm *alarm = m_alarmList->get(i);
1348 if ((alarm->getSourceObject() == dwObjectId) &&
1349 ((alarm->getState() & ALARM_STATE_MASK) < ALARM_STATE_RESOLVED) &&
1350 ((alarm->getCurrentSeverity() > iStatus) || (iStatus == STATUS_UNKNOWN)))
1351 {
1352 iStatus = (int)alarm->getCurrentSeverity();
1353 }
1354 }
1355 MutexUnlock(m_mutex);
1356 return iStatus;
1357 }
1358
1359 /**
1360 * Fill message with alarm stats
1361 */
1362 void GetAlarmStats(NXCPMessage *pMsg)
1363 {
1364 UINT32 dwCount[5];
1365
1366 MutexLock(m_mutex);
1367 pMsg->setField(VID_NUM_ALARMS, m_alarmList->size());
1368 memset(dwCount, 0, sizeof(UINT32) * 5);
1369 for(int i = 0; i < m_alarmList->size(); i++)
1370 dwCount[m_alarmList->get(i)->getCurrentSeverity()]++;
1371 MutexUnlock(m_mutex);
1372 pMsg->setFieldFromInt32Array(VID_ALARMS_BY_SEVERITY, 5, dwCount);
1373 }
1374
1375 /**
1376 * Watchdog thread
1377 */
1378 static THREAD_RESULT THREAD_CALL WatchdogThread(void *arg)
1379 {
1380 while(1)
1381 {
1382 if (ConditionWait(m_condShutdown, 1000))
1383 break;
1384
1385 MutexLock(m_mutex);
1386 time_t now = time(NULL);
1387 for(int i = 0; i < m_alarmList->size(); i++)
1388 {
1389 Alarm *alarm = m_alarmList->get(i);
1390 if ((alarm->getTimeout() > 0) &&
1391 ((alarm->getState() & ALARM_STATE_MASK) == ALARM_STATE_OUTSTANDING) &&
1392 (((time_t)alarm->getLastChangeTime() + (time_t)alarm->getTimeout()) < now))
1393 {
1394 DbgPrintf(5, _T("Alarm timeout: alarm_id=%u, last_change=%u, timeout=%u, now=%u"),
1395 alarm->getAlarmId(), alarm->getLastChangeTime(),
1396 alarm->getTimeout(), (UINT32)now);
1397
1398 PostEvent(alarm->getTimeoutEvent(), alarm->getSourceObject(), "dssd",
1399 alarm->getAlarmId(), alarm->getMessage(), alarm->getKey(), alarm->getSourceEventCode());
1400 alarm->clearTimeout(); // Disable repeated timeout events
1401 alarm->updateInDatabase();
1402 }
1403
1404 if ((alarm->getAckTimeout() != 0) &&
1405 ((alarm->getState() & ALARM_STATE_STICKY) != 0) &&
1406 (((time_t)alarm->getAckTimeout() <= now)))
1407 {
1408 DbgPrintf(5, _T("Alarm acknowledgment timeout: alarm_id=%u, timeout=%u, now=%u"),
1409 alarm->getAlarmId(), alarm->getAckTimeout(), (UINT32)now);
1410
1411 PostEvent(alarm->getTimeoutEvent(), alarm->getSourceObject(), "dssd",
1412 alarm->getAlarmId(), alarm->getMessage(), alarm->getKey(), alarm->getSourceEventCode());
1413 alarm->onAckTimeoutExpiration();
1414 alarm->updateInDatabase();
1415 NotifyClients(NX_NOTIFY_ALARM_CHANGED, alarm);
1416 }
1417 }
1418 MutexUnlock(m_mutex);
1419 }
1420 return THREAD_OK;
1421 }
1422
1423 /**
1424 * Check if given alram/note id pair is valid
1425 */
1426 static bool IsValidNoteId(UINT32 alarmId, UINT32 noteId)
1427 {
1428 bool isValid = false;
1429 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1430 DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT note_id FROM alarm_notes WHERE alarm_id=? AND note_id=?"));
1431 if (hStmt != NULL)
1432 {
1433 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, alarmId);
1434 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, noteId);
1435 DB_RESULT hResult = DBSelectPrepared(hStmt);
1436 if (hResult != NULL)
1437 {
1438 isValid = (DBGetNumRows(hResult) > 0);
1439 DBFreeResult(hResult);
1440 }
1441 DBFreeStatement(hStmt);
1442 }
1443 DBConnectionPoolReleaseConnection(hdb);
1444 return isValid;
1445 }
1446
1447 /**
1448 * Update alarm's comment
1449 */
1450 UINT32 Alarm::updateAlarmComment(UINT32 commentId, const TCHAR *text, UINT32 userId, bool syncWithHelpdesk)
1451 {
1452 bool newNote = false;
1453 UINT32 rcc;
1454
1455 if (commentId != 0)
1456 {
1457 if (IsValidNoteId(m_alarmId, commentId))
1458 {
1459 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1460 DB_STATEMENT hStmt = DBPrepare(hdb, _T("UPDATE alarm_notes SET change_time=?,user_id=?,note_text=? WHERE note_id=?"));
1461 if (hStmt != NULL)
1462 {
1463 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (UINT32)time(NULL));
1464 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, userId);
1465 DBBind(hStmt, 3, DB_SQLTYPE_TEXT, text, DB_BIND_STATIC);
1466 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, commentId);
1467 rcc = DBExecute(hStmt) ? RCC_SUCCESS : RCC_DB_FAILURE;
1468 DBFreeStatement(hStmt);
1469 }
1470 else
1471 {
1472 rcc = RCC_DB_FAILURE;
1473 }
1474 DBConnectionPoolReleaseConnection(hdb);
1475 }
1476 else
1477 {
1478 rcc = RCC_INVALID_ALARM_NOTE_ID;
1479 }
1480 }
1481 else
1482 {
1483 // new note
1484 newNote = true;
1485 commentId = CreateUniqueId(IDG_ALARM_NOTE);
1486 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1487 DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO alarm_notes (note_id,alarm_id,change_time,user_id,note_text) VALUES (?,?,?,?,?)"));
1488 if (hStmt != NULL)
1489 {
1490 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, commentId);
1491 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_alarmId);
1492 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (UINT32)time(NULL));
1493 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, userId);
1494 DBBind(hStmt, 5, DB_SQLTYPE_TEXT, text, DB_BIND_STATIC);
1495 rcc = DBExecute(hStmt) ? RCC_SUCCESS : RCC_DB_FAILURE;
1496 DBFreeStatement(hStmt);
1497 }
1498 else
1499 {
1500 rcc = RCC_DB_FAILURE;
1501 }
1502 DBConnectionPoolReleaseConnection(hdb);
1503 }
1504 if (rcc == RCC_SUCCESS)
1505 {
1506 if (newNote)
1507 m_commentCount++;
1508 NotifyClients(NX_NOTIFY_ALARM_CHANGED, this);
1509 if (syncWithHelpdesk && (m_helpDeskState == ALARM_HELPDESK_OPEN))
1510 {
1511 AddHelpdeskIssueComment(m_helpDeskRef, text);
1512 }
1513 }
1514
1515 return rcc;
1516 }
1517
1518 /**
1519 * Add alarm's comment by helpdesk reference
1520 */
1521 UINT32 AddAlarmComment(const TCHAR *hdref, const TCHAR *text, UINT32 userId)
1522 {
1523 UINT32 rcc = RCC_INVALID_ALARM_ID;
1524
1525 MutexLock(m_mutex);
1526 for(int i = 0; i < m_alarmList->size(); i++)
1527 {
1528 Alarm *alarm = m_alarmList->get(i);
1529 if (!_tcscmp(alarm->getHelpDeskRef(), hdref))
1530 {
1531 rcc = alarm->updateAlarmComment(0, text, userId, false);
1532 break;
1533 }
1534 }
1535 MutexUnlock(m_mutex);
1536
1537 return rcc;
1538 }
1539
1540 /**
1541 * Update alarm's comment
1542 */
1543 UINT32 UpdateAlarmComment(UINT32 alarmId, UINT32 noteId, const TCHAR *text, UINT32 userId)
1544 {
1545 UINT32 rcc = RCC_INVALID_ALARM_ID;
1546
1547 MutexLock(m_mutex);
1548 for(int i = 0; i < m_alarmList->size(); i++)
1549 {
1550 Alarm *alarm = m_alarmList->get(i);
1551 if (alarm->getAlarmId() == alarmId)
1552 {
1553 rcc = alarm->updateAlarmComment(noteId, text, userId, true);
1554 break;
1555 }
1556 }
1557 MutexUnlock(m_mutex);
1558
1559 return rcc;
1560 }
1561
1562 /**
1563 * Delete comment
1564 */
1565 UINT32 Alarm::deleteComment(UINT32 commentId)
1566 {
1567 UINT32 rcc;
1568 if (IsValidNoteId(m_alarmId, commentId))
1569 {
1570 DB_HANDLE db = DBConnectionPoolAcquireConnection();
1571 DB_STATEMENT stmt = DBPrepare(db, _T("DELETE FROM alarm_notes WHERE note_id=?"));
1572 if (stmt != NULL)
1573 {
1574 DBBind(stmt, 1, DB_SQLTYPE_INTEGER, commentId);
1575 rcc = DBExecute(stmt) ? RCC_SUCCESS : RCC_DB_FAILURE;
1576 DBFreeStatement(stmt);
1577 }
1578 else
1579 {
1580 rcc = RCC_DB_FAILURE;
1581 }
1582 DBConnectionPoolReleaseConnection(db);
1583 }
1584 else
1585 {
1586 rcc = RCC_INVALID_ALARM_NOTE_ID;
1587 }
1588 if (rcc == RCC_SUCCESS)
1589 {
1590 m_commentCount--;
1591 NotifyClients(NX_NOTIFY_ALARM_CHANGED, this);
1592 }
1593 return rcc;
1594 }
1595
1596 /**
1597 * Delete comment
1598 */
1599 UINT32 DeleteAlarmCommentByID(UINT32 alarmId, UINT32 noteId)
1600 {
1601 UINT32 rcc = RCC_INVALID_ALARM_ID;
1602
1603 MutexLock(m_mutex);
1604 for(int i = 0; i < m_alarmList->size(); i++)
1605 {
1606 Alarm *alarm = m_alarmList->get(i);
1607 if (alarm->getAlarmId() == alarmId)
1608 {
1609 rcc = alarm->deleteComment(noteId);
1610 break;
1611 }
1612 }
1613 MutexUnlock(m_mutex);
1614
1615 return rcc;
1616 }
1617
1618 /**
1619 * Get alarm's comments
1620 */
1621 UINT32 GetAlarmComments(UINT32 alarmId, NXCPMessage *msg)
1622 {
1623 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1624 UINT32 rcc = RCC_DB_FAILURE;
1625
1626 DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT note_id,change_time,user_id,note_text FROM alarm_notes WHERE alarm_id=?"));
1627 if (hStmt != NULL)
1628 {
1629 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, alarmId);
1630 DB_RESULT hResult = DBSelectPrepared(hStmt);
1631 if (hResult != NULL)
1632 {
1633 int count = DBGetNumRows(hResult);
1634 msg->setField(VID_NUM_ELEMENTS, (UINT32)count);
1635
1636 UINT32 varId = VID_ELEMENT_LIST_BASE;
1637 for(int i = 0; i < count; i++)
1638 {
1639 msg->setField(varId++, DBGetFieldULong(hResult, i, 0));
1640 msg->setField(varId++, alarmId);
1641 msg->setField(varId++, DBGetFieldULong(hResult, i, 1));
1642 UINT32 userId = DBGetFieldULong(hResult, i, 2);
1643 msg->setField(varId++, userId);
1644
1645 TCHAR *text = DBGetField(hResult, i, 3, NULL, 0);
1646 msg->setField(varId++, CHECK_NULL_EX(text));
1647 safe_free(text);
1648
1649 TCHAR userName[MAX_USER_NAME];
1650 if (ResolveUserId(userId, userName, MAX_USER_NAME))
1651 {
1652 msg->setField(varId++, userName);
1653 }
1654 else
1655 {
1656 varId++;
1657 }
1658
1659 varId += 4;
1660 }
1661 DBFreeResult(hResult);
1662 rcc = RCC_SUCCESS;
1663 }
1664 DBFreeStatement(hStmt);
1665 }
1666
1667 DBConnectionPoolReleaseConnection(hdb);
1668 return rcc;
1669 }
1670
1671 /**
1672 * Get alarms for given object. If objectId set to 0, all alarms will be returned.
1673 * This method returns copies of alarm objects, so changing them will not
1674 * affect alarm states. Returned array must be destroyed by the caller.
1675 *
1676 * @param objectId object ID or 0 to get all alarms
1677 * @return array of active alarms for given object
1678 */
1679 ObjectArray<Alarm> NXCORE_EXPORTABLE *GetAlarms(UINT32 objectId)
1680 {
1681 ObjectArray<Alarm> *result = new ObjectArray<Alarm>(16, 16, true);
1682
1683 MutexLock(m_mutex);
1684 for(int i = 0; i < m_alarmList->size(); i++)
1685 {
1686 Alarm *alarm = m_alarmList->get(i);
1687 if ((objectId == 0) || (alarm->getSourceObject() == objectId))
1688 {
1689 result->add(new Alarm(alarm, true));
1690 }
1691 }
1692 MutexUnlock(m_mutex);
1693 return result;
1694 }
1695
1696 /**
1697 * NXSL extension: Find alarm by ID
1698 */
1699 int F_FindAlarmById(int argc, NXSL_Value **argv, NXSL_Value **result, NXSL_VM *vm)
1700 {
1701 if (!argv[0]->isInteger())
1702 return NXSL_ERR_NOT_INTEGER;
1703
1704 UINT32 alarmId = argv[0]->getValueAsUInt32();
1705 Alarm *alarm = NULL;
1706
1707 MutexLock(m_mutex);
1708 for(int i = 0; i < m_alarmList->size(); i++)
1709 {
1710 Alarm *a = m_alarmList->get(i);
1711 if (a->getAlarmId() == alarmId)
1712 {
1713 alarm = new Alarm(a, false);
1714 break;
1715 }
1716 }
1717 MutexUnlock(m_mutex);
1718
1719 *result = (alarm != NULL) ? new NXSL_Value(new NXSL_Object(&g_nxslAlarmClass, alarm)) : new NXSL_Value();
1720 return 0;
1721 }
1722
1723 /**
1724 * NXSL extension: Find alarm by key
1725 */
1726 int F_FindAlarmByKey(int argc, NXSL_Value **argv, NXSL_Value **result, NXSL_VM *vm)
1727 {
1728 if (!argv[0]->isString())
1729 return NXSL_ERR_NOT_STRING;
1730
1731 const TCHAR *key = argv[0]->getValueAsCString();
1732 Alarm *alarm = NULL;
1733
1734 MutexLock(m_mutex);
1735 for(int i = 0; i < m_alarmList->size(); i++)
1736 {
1737 Alarm *a = m_alarmList->get(i);
1738 if (!_tcscmp(a->getKey(), key))
1739 {
1740 alarm = new Alarm(a, false);
1741 break;
1742 }
1743 }
1744 MutexUnlock(m_mutex);
1745
1746 *result = (alarm != NULL) ? new NXSL_Value(new NXSL_Object(&g_nxslAlarmClass, alarm)) : new NXSL_Value();
1747 return 0;
1748 }
1749
1750 /**
1751 * Initialize alarm manager at system startup
1752 */
1753 bool InitAlarmManager()
1754 {
1755 m_alarmList = new ObjectArray<Alarm>(64, 64, true);
1756 m_mutex = MutexCreate();
1757 m_condShutdown = ConditionCreate(FALSE);
1758 m_hWatchdogThread = INVALID_THREAD_HANDLE;
1759
1760 DB_RESULT hResult;
1761
1762 // Load active alarms into memory
1763 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1764 hResult = DBSelect(hdb, _T("SELECT alarm_id,source_object_id,")
1765 _T("source_event_code,source_event_id,message,")
1766 _T("original_severity,current_severity,")
1767 _T("alarm_key,creation_time,last_change_time,")
1768 _T("hd_state,hd_ref,ack_by,repeat_count,")
1769 _T("alarm_state,timeout,timeout_event,resolved_by,")
1770 _T("ack_timeout,dci_id,alarm_category_ids FROM alarms WHERE alarm_state<>3"));
1771 if (hResult == NULL)
1772 return false;
1773
1774 int count = DBGetNumRows(hResult);
1775 if (count > 0)
1776 {
1777 for(int i = 0; i < count; i++)
1778 {
1779 m_alarmList->add(new Alarm(hdb, hResult, i));
1780 }
1781 }
1782
1783 DBFreeResult(hResult);
1784 DBConnectionPoolReleaseConnection(hdb);
1785
1786 m_hWatchdogThread = ThreadCreateEx(WatchdogThread, 0, NULL);
1787 return true;
1788 }
1789
1790 /**
1791 * Alarm manager destructor
1792 */
1793 void ShutdownAlarmManager()
1794 {
1795 ConditionSet(m_condShutdown);
1796 ThreadJoin(m_hWatchdogThread);
1797 MutexDestroy(m_mutex);
1798 delete m_alarmList;
1799 }