"Fossies" - the Fresh Open Source Software Archive

Member "netxms-3.1.300/src/server/core/alarm.cpp" (7 Jan 2020, 65444 Bytes) of package /linux/misc/netxms-3.1.300.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "alarm.cpp" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3.0.2357_vs_3.1.241.

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