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