"Fossies" - the Fresh Open Source Software Archive

Member "netxms-3.8.405/src/server/core/node.cpp" (8 Jun 2021, 402851 Bytes) of package /linux/misc/netxms-3.8.405.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 "node.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.8.382_vs_3.8.405.

    1 /*
    2 ** NetXMS - Network Management System
    3 ** Copyright (C) 2003-2021 Raden Solutions
    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: node.cpp
   20 **
   21 **/
   22 
   23 #include "nxcore.h"
   24 #include <agent_tunnel.h>
   25 #include <entity_mib.h>
   26 #include <ethernet_ip.h>
   27 
   28 #define DEBUG_TAG_DC_AGENT_CACHE    _T("dc.agent.cache")
   29 #define DEBUG_TAG_ICMP_POLL         _T("poll.icmp")
   30 #define DEBUG_TAG_NODE_INTERFACES   _T("node.iface")
   31 #define DEBUG_TAG_ROUTES_POLL       _T("poll.routes")
   32 #define DEBUG_TAG_TOPOLOGY_POLL     _T("poll.topology")
   33 #define DEBUG_TAG_SNMP_TRAP_FLOOD   _T("snmp.trap.flood")
   34 
   35 /**
   36  * Performance counters
   37  */
   38 extern VolatileCounter64 g_snmpTrapsReceived;
   39 extern VolatileCounter64 g_syslogMessagesReceived;
   40 extern VolatileCounter64 g_windowsEventsReceived;
   41 extern uint32_t g_averageDCIQueuingTime;
   42 
   43 /**
   44  * Poller thread pool
   45  */
   46 extern ThreadPool *g_pollerThreadPool;
   47 
   48 /**
   49  * Unbind agent tunnel from node
   50  */
   51 uint32_t UnbindAgentTunnel(uint32_t nodeId, uint32_t userId);
   52 
   53 /**
   54  * Software package management functions
   55  */
   56 int PackageNameVersionComparator(const SoftwarePackage **p1, const SoftwarePackage **p2);
   57 ObjectArray<SoftwarePackage> *CalculatePackageChanges(ObjectArray<SoftwarePackage> *oldSet, ObjectArray<SoftwarePackage> *newSet);
   58 
   59 /**
   60  * Hardware inventory management functions
   61  */
   62 int HardwareComponentComparator(const HardwareComponent **c1, const HardwareComponent **c2);
   63 ObjectArray<HardwareComponent> *CalculateHardwareChanges(ObjectArray<HardwareComponent> *oldSet, ObjectArray<HardwareComponent> *newSet);
   64 
   65 /**
   66  * Get syncer run time statistic
   67  */
   68 int64_t GetSyncerRunTime(StatisticType statType);
   69 
   70 /**
   71  * Poll cancellation checkpoint
   72  */
   73 #define POLL_CANCELLATION_CHECKPOINT() \
   74          do { if (g_flags & AF_SHUTDOWN) { pollerUnlock(); return; } } while(0)
   75 
   76 /**
   77  * Poll cancellation checkpoint with additional hook
   78  */
   79 #define POLL_CANCELLATION_CHECKPOINT_EX(hook) \
   80          do { if (g_flags & AF_SHUTDOWN) { hook; pollerUnlock(); return; } } while(0)
   81 
   82 /**
   83  * Node class default constructor
   84  */
   85 Node::Node() : super(), m_discoveryPollState(_T("discovery")),
   86          m_topologyPollState(_T("topology")), m_routingPollState(_T("routing")), m_icmpPollState(_T("icmp)"))
   87 {
   88    m_status = STATUS_UNKNOWN;
   89    m_type = NODE_TYPE_UNKNOWN;
   90    m_subType[0] = 0;
   91    m_hypervisorType[0] = 0;
   92    m_capabilities = 0;
   93    m_zoneUIN = 0;
   94    m_agentPort = AGENT_LISTEN_PORT;
   95    m_agentCacheMode = AGENT_CACHE_DEFAULT;
   96    m_agentSecret[0] = 0;
   97    m_iStatusPollType = POLL_ICMP_PING;
   98    m_snmpVersion = SNMP_VERSION_2C;
   99    m_snmpPort = SNMP_DEFAULT_PORT;
  100    m_snmpSecurity = new SNMP_SecurityContext("public");
  101    m_snmpObjectId = nullptr;
  102    m_downSince = 0;
  103    m_bootTime = 0;
  104    m_agentUpTime = 0;
  105    m_hAgentAccessMutex = MutexCreate();
  106    m_hSmclpAccessMutex = MutexCreate();
  107    m_mutexRTAccess = MutexCreate();
  108    m_mutexTopoAccess = MutexCreate();
  109    m_proxyConnections = new ProxyAgentConnection[MAX_PROXY_TYPE];
  110    m_pendingDataConfigurationSync = 0;
  111    m_smclpConnection = nullptr;
  112    m_lastAgentTrapId = 0;
  113    m_lastSNMPTrapId = 0;
  114    m_lastSyslogMessageId = 0;
  115    m_lastWindowsEventId = 0;
  116    m_lastAgentPushRequestId = 0;
  117    m_agentCertSubject = nullptr;
  118    m_agentCertMappingMethod = MAP_CERTIFICATE_BY_CN;
  119    m_agentCertMappingData = nullptr;
  120    m_agentVersion[0] = 0;
  121    m_platformName[0] = 0;
  122    m_sysDescription = nullptr;
  123    m_sysName = nullptr;
  124    m_sysContact = nullptr;
  125    m_sysLocation = nullptr;
  126    m_lldpNodeId = nullptr;
  127    m_lldpLocalPortInfo = nullptr;
  128    m_agentParameters = nullptr;
  129    m_agentTables = nullptr;
  130    m_driverParameters = nullptr;
  131    m_pollerNode = 0;
  132    m_agentProxy = 0;
  133    m_snmpProxy = 0;
  134    m_eipProxy = 0;
  135    m_icmpProxy = 0;
  136    memset(m_lastEvents, 0, sizeof(m_lastEvents));
  137    m_routingLoopEvents = new ObjectArray<RoutingLoopEvent>(0, 16, Ownership::True);
  138    m_routingTable = nullptr;
  139    m_failTimeAgent = TIMESTAMP_NEVER;
  140    m_failTimeSNMP = TIMESTAMP_NEVER;
  141    m_failTimeEtherNetIP = TIMESTAMP_NEVER;
  142    m_recoveryTime = TIMESTAMP_NEVER;
  143    m_lastAgentCommTime = TIMESTAMP_NEVER;
  144    m_lastAgentConnectAttempt = TIMESTAMP_NEVER;
  145    m_agentRestartTime = TIMESTAMP_NEVER;
  146    m_vrrpInfo = nullptr;
  147    m_topology = nullptr;
  148    m_topologyRebuildTimestamp = TIMESTAMP_NEVER;
  149    m_pendingState = -1;
  150    m_pollCountAgent = 0;
  151    m_pollCountSNMP = 0;
  152    m_pollCountEtherNetIP = 0;
  153    m_requiredPollCount = 0; // Use system default
  154    m_pollsAfterIpUpdate = 0;
  155    m_nUseIfXTable = IFXTABLE_DEFAULT;  // Use system default
  156    m_fdb = nullptr;
  157    m_wirelessStations = nullptr;
  158    m_adoptedApCount = 0;
  159    m_totalApCount = 0;
  160    m_driver = nullptr;
  161    m_driverData = nullptr;
  162    m_softwarePackages = nullptr;
  163    m_hardwareComponents = nullptr;
  164    m_winPerfObjects = nullptr;
  165    memset(m_baseBridgeAddress, 0, MAC_ADDR_LENGTH);
  166    m_physicalContainer = 0;
  167    m_rackPosition = 0;
  168    m_rackHeight = 1;
  169    m_syslogMessageCount = 0;
  170    m_snmpTrapCount = 0;
  171    m_snmpTrapLastTotal = 0;
  172    m_snmpTrapStormLastCheckTime = 0;
  173    m_snmpTrapStormActualDuration = 0;
  174    m_sshLogin[0] = 0;
  175    m_sshPassword[0] = 0;
  176    m_sshKeyId = 0;
  177    m_sshPort = SSH_PORT;
  178    m_sshProxy = 0;
  179    m_portNumberingScheme = NDD_PN_UNKNOWN;
  180    m_portRowCount = 0;
  181    m_agentCompressionMode = NODE_AGENT_COMPRESSION_DEFAULT;
  182    m_rackOrientation = FILL;
  183    m_icmpStatCollectionMode = IcmpStatCollectionMode::DEFAULT;
  184    m_icmpStatCollectors = nullptr;
  185    m_chassisPlacementConf = nullptr;
  186    m_eipPort = ETHERNET_IP_DEFAULT_PORT;
  187    m_cipDeviceType = 0;
  188    m_cipState = 0;
  189    m_cipStatus = 0;
  190    m_cipVendorCode = 0;
  191 }
  192 
  193 /**
  194  * Create new node from new node data
  195  */
  196 Node::Node(const NewNodeData *newNodeData, UINT32 flags)  : super(), m_discoveryPollState(_T("discovery")),
  197          m_topologyPollState(_T("topology")), m_routingPollState(_T("routing")), m_icmpPollState(_T("icmp)"))
  198 {
  199    m_runtimeFlags |= ODF_CONFIGURATION_POLL_PENDING;
  200    m_primaryHostName = newNodeData->ipAddr.toString();
  201    m_status = STATUS_UNKNOWN;
  202    m_type = NODE_TYPE_UNKNOWN;
  203    m_subType[0] = 0;
  204    m_hypervisorType[0] = 0;
  205    m_ipAddress = newNodeData->ipAddr;
  206    m_capabilities = 0;
  207    m_flags = flags;
  208    m_zoneUIN = newNodeData->zoneUIN;
  209    m_agentPort = newNodeData->agentPort;
  210    m_agentCacheMode = AGENT_CACHE_DEFAULT;
  211    m_agentSecret[0] = 0;
  212    m_iStatusPollType = POLL_ICMP_PING;
  213    m_snmpVersion = SNMP_VERSION_2C;
  214    m_snmpPort = newNodeData->snmpPort;
  215    if (newNodeData->snmpSecurity != nullptr)
  216       m_snmpSecurity = new SNMP_SecurityContext(newNodeData->snmpSecurity);
  217    else
  218       m_snmpSecurity = new SNMP_SecurityContext("public");
  219    if (newNodeData->name[0] != 0)
  220       _tcslcpy(m_name, newNodeData->name, MAX_OBJECT_NAME);
  221    else
  222       newNodeData->ipAddr.toString(m_name);    // Make default name from IP address
  223    m_snmpObjectId = nullptr;
  224    m_downSince = 0;
  225    m_bootTime = 0;
  226    m_agentUpTime = 0;
  227    m_hAgentAccessMutex = MutexCreate();
  228    m_hSmclpAccessMutex = MutexCreate();
  229    m_mutexRTAccess = MutexCreate();
  230    m_mutexTopoAccess = MutexCreate();
  231    m_proxyConnections = new ProxyAgentConnection[MAX_PROXY_TYPE];
  232    m_pendingDataConfigurationSync = 0;
  233    m_smclpConnection = nullptr;
  234    m_lastAgentTrapId = 0;
  235    m_lastSNMPTrapId = 0;
  236    m_lastSyslogMessageId = 0;
  237    m_lastWindowsEventId = 0;
  238    m_lastAgentPushRequestId = 0;
  239    m_agentCertSubject = nullptr;
  240    m_agentCertMappingMethod = MAP_CERTIFICATE_BY_CN;
  241    m_agentCertMappingData = nullptr;
  242    m_agentVersion[0] = 0;
  243    m_platformName[0] = 0;
  244    m_sysDescription = nullptr;
  245    m_sysName = nullptr;
  246    m_sysContact = nullptr;
  247    m_sysLocation = nullptr;
  248    m_lldpNodeId = nullptr;
  249    m_lldpLocalPortInfo = nullptr;
  250    m_agentParameters = nullptr;
  251    m_agentTables = nullptr;
  252    m_driverParameters = nullptr;
  253    m_pollerNode = 0;
  254    m_agentProxy = newNodeData->agentProxyId;
  255    m_snmpProxy = newNodeData->snmpProxyId;
  256    m_eipProxy = newNodeData->eipProxyId;
  257    m_icmpProxy = newNodeData->icmpProxyId;
  258    memset(m_lastEvents, 0, sizeof(m_lastEvents));
  259    m_routingLoopEvents = new ObjectArray<RoutingLoopEvent>(0, 16, Ownership::True);
  260    m_isHidden = true;
  261    m_routingTable = nullptr;
  262    m_failTimeAgent = TIMESTAMP_NEVER;
  263    m_failTimeSNMP = TIMESTAMP_NEVER;
  264    m_failTimeEtherNetIP = TIMESTAMP_NEVER;
  265    m_recoveryTime = TIMESTAMP_NEVER;
  266    m_lastAgentCommTime = TIMESTAMP_NEVER;
  267    m_lastAgentConnectAttempt = TIMESTAMP_NEVER;
  268    m_agentRestartTime = TIMESTAMP_NEVER;
  269    m_vrrpInfo = nullptr;
  270    m_topology = nullptr;
  271    m_topologyRebuildTimestamp = TIMESTAMP_NEVER;
  272    m_pendingState = -1;
  273    m_pollCountAgent = 0;
  274    m_pollCountSNMP = 0;
  275    m_pollCountEtherNetIP = 0;
  276    m_requiredPollCount = 0; // Use system default
  277    m_pollsAfterIpUpdate = 0;
  278    m_nUseIfXTable = IFXTABLE_DEFAULT;  // Use system default
  279    m_fdb = nullptr;
  280    m_wirelessStations = nullptr;
  281    m_adoptedApCount = 0;
  282    m_totalApCount = 0;
  283    m_driver = nullptr;
  284    m_driverData = nullptr;
  285    m_softwarePackages = nullptr;
  286    m_hardwareComponents = nullptr;
  287    m_winPerfObjects = nullptr;
  288    memset(m_baseBridgeAddress, 0, MAC_ADDR_LENGTH);
  289    m_physicalContainer = 0;
  290    m_rackPosition = 0;
  291    m_rackHeight = 1;
  292    m_syslogMessageCount = 0;
  293    m_snmpTrapCount = 0;
  294    m_snmpTrapLastTotal = 0;
  295    m_snmpTrapStormLastCheckTime = 0;
  296    m_snmpTrapStormActualDuration = 0;
  297    _tcslcpy(m_sshLogin, newNodeData->sshLogin, MAX_SSH_LOGIN_LEN);
  298    _tcslcpy(m_sshPassword, newNodeData->sshPassword, MAX_SSH_PASSWORD_LEN);
  299    m_sshKeyId = 0;
  300    m_sshPort = newNodeData->sshPort;
  301    m_sshProxy = newNodeData->sshProxyId;
  302    m_portNumberingScheme = NDD_PN_UNKNOWN;
  303    m_portRowCount = 0;
  304    m_agentCompressionMode = NODE_AGENT_COMPRESSION_DEFAULT;
  305    m_rackOrientation = FILL;
  306    m_agentId = newNodeData->agentId;
  307    m_icmpStatCollectionMode = IcmpStatCollectionMode::DEFAULT;
  308    m_icmpStatCollectors = nullptr;
  309    setCreationTime();
  310    m_chassisPlacementConf = nullptr;
  311    m_eipPort = newNodeData->eipPort;
  312    m_cipDeviceType = 0;
  313    m_cipState = 0;
  314    m_cipStatus = 0;
  315    m_cipVendorCode = 0;
  316 }
  317 
  318 /**
  319  * Node destructor
  320  */
  321 Node::~Node()
  322 {
  323    delete m_driverData;
  324    MutexDestroy(m_hAgentAccessMutex);
  325    MutexDestroy(m_hSmclpAccessMutex);
  326    MutexDestroy(m_mutexRTAccess);
  327    MutexDestroy(m_mutexTopoAccess);
  328    delete[] m_proxyConnections;
  329    delete m_smclpConnection;
  330    delete m_agentParameters;
  331    delete m_agentTables;
  332    delete m_driverParameters;
  333    MemFree(m_snmpObjectId);
  334    MemFree(m_sysDescription);
  335    delete m_routingTable;
  336    delete m_vrrpInfo;
  337    delete m_topology;
  338    delete m_snmpSecurity;
  339    if (m_fdb != nullptr)
  340       m_fdb->decRefCount();
  341    delete m_wirelessStations;
  342    MemFree(m_lldpNodeId);
  343    delete m_lldpLocalPortInfo;
  344    delete m_softwarePackages;
  345    delete m_hardwareComponents;
  346    delete m_winPerfObjects;
  347    MemFree(m_sysName);
  348    MemFree(m_sysContact);
  349    MemFree(m_sysLocation);
  350    delete m_routingLoopEvents;
  351    MemFree(m_agentCertSubject);
  352    MemFree(m_agentCertMappingData);
  353    delete m_icmpStatCollectors;
  354    MemFree(m_chassisPlacementConf);
  355 }
  356 
  357 /**
  358  * Create object from database data
  359  */
  360 bool Node::loadFromDatabase(DB_HANDLE hdb, UINT32 dwId)
  361 {
  362    int i, iNumRows;
  363    bool bResult = false;
  364 
  365    m_id = dwId;
  366 
  367    if (!loadCommonProperties(hdb) || !super::loadFromDatabase(hdb, dwId))
  368    {
  369       DbgPrintf(2, _T("Cannot load common properties for node object %d"), dwId);
  370       return false;
  371    }
  372 
  373    DB_STATEMENT hStmt = DBPrepare(hdb,
  374       _T("SELECT primary_name,primary_ip,snmp_version,secret,agent_port,status_poll_type,snmp_oid,agent_version,")
  375       _T("platform_name,poller_node_id,zone_guid,proxy_node,snmp_proxy,required_polls,uname,use_ifxtable,")
  376       _T("snmp_port,community,usm_auth_password,usm_priv_password,usm_methods,snmp_sys_name,bridge_base_addr,")
  377       _T("down_since,boot_time,driver_name,icmp_proxy,agent_cache_mode,snmp_sys_contact,snmp_sys_location,")
  378       _T("physical_container_id,rack_image_front,rack_position,rack_height,last_agent_comm_time,syslog_msg_count,")
  379       _T("snmp_trap_count,node_type,node_subtype,ssh_login,ssh_password,ssh_proxy,port_rows,port_numbering_scheme,")
  380       _T("agent_comp_mode,tunnel_id,lldp_id,capabilities,fail_time_snmp,fail_time_agent,rack_orientation,")
  381       _T("rack_image_rear,agent_id,agent_cert_subject,hypervisor_type,hypervisor_info,icmp_poll_mode,")
  382       _T("chassis_placement_config,vendor,product_code,product_name,product_version,serial_number,cip_device_type,")
  383       _T("cip_status,cip_state,eip_proxy,eip_port,hardware_id,cip_vendor_code,agent_cert_mapping_method,")
  384       _T("agent_cert_mapping_data,snmp_engine_id,ssh_port,ssh_key_id FROM nodes WHERE id=?"));
  385    if (hStmt == nullptr)
  386       return false;
  387 
  388    DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, dwId);
  389    DB_RESULT hResult = DBSelectPrepared(hStmt);
  390    if (hResult == nullptr)
  391    {
  392       DBFreeStatement(hStmt);
  393       return false;     // Query failed
  394    }
  395 
  396    if (DBGetNumRows(hResult) == 0)
  397    {
  398       DBFreeResult(hResult);
  399       DBFreeStatement(hStmt);
  400       DbgPrintf(2, _T("Missing record in \"nodes\" table for node object %d"), dwId);
  401       return false;
  402    }
  403 
  404    m_primaryHostName = DBGetFieldAsSharedString(hResult, 0, 0);
  405    m_ipAddress = DBGetFieldInetAddr(hResult, 0, 1);
  406    m_snmpVersion = static_cast<SNMP_Version>(DBGetFieldLong(hResult, 0, 2));
  407    DBGetField(hResult, 0, 3, m_agentSecret, MAX_SECRET_LENGTH);
  408    m_agentPort = static_cast<uint16_t>(DBGetFieldLong(hResult, 0, 4));
  409    m_iStatusPollType = DBGetFieldLong(hResult, 0, 5);
  410    m_snmpObjectId = DBGetField(hResult, 0, 6, nullptr, 0);
  411    if ((m_snmpObjectId != nullptr) && (*m_snmpObjectId == 0))
  412       MemFreeAndNull(m_snmpObjectId);
  413    DBGetField(hResult, 0, 7, m_agentVersion, MAX_AGENT_VERSION_LEN);
  414    DBGetField(hResult, 0, 8, m_platformName, MAX_PLATFORM_NAME_LEN);
  415    m_pollerNode = DBGetFieldULong(hResult, 0, 9);
  416    m_zoneUIN = DBGetFieldULong(hResult, 0, 10);
  417    m_agentProxy = DBGetFieldULong(hResult, 0, 11);
  418    m_snmpProxy = DBGetFieldULong(hResult, 0, 12);
  419    m_requiredPollCount = DBGetFieldLong(hResult, 0, 13);
  420    m_sysDescription = DBGetField(hResult, 0, 14, nullptr, 0);
  421    m_nUseIfXTable = (BYTE)DBGetFieldLong(hResult, 0, 15);
  422    m_snmpPort = static_cast<uint16_t>(DBGetFieldLong(hResult, 0, 16));
  423 
  424    // SNMP authentication parameters
  425    char snmpAuthObject[256], snmpAuthPassword[256], snmpPrivPassword[256], snmpEngineId[256];
  426    DBGetFieldA(hResult, 0, 17, snmpAuthObject, 256);
  427    DBGetFieldA(hResult, 0, 18, snmpAuthPassword, 256);
  428    DBGetFieldA(hResult, 0, 19, snmpPrivPassword, 256);
  429    int snmpMethods = DBGetFieldLong(hResult, 0, 20);
  430    DBGetFieldA(hResult, 0, 72, snmpEngineId, 256);
  431    delete m_snmpSecurity;
  432    if (m_snmpVersion == SNMP_VERSION_3)
  433    {
  434       m_snmpSecurity = new SNMP_SecurityContext(snmpAuthObject, snmpAuthPassword, snmpPrivPassword,
  435                static_cast<SNMP_AuthMethod>(snmpMethods & 0xFF), static_cast<SNMP_EncryptionMethod>(snmpMethods >> 8));
  436       if (snmpEngineId[0] != 0)
  437       {
  438          BYTE engineId[128];
  439          size_t engineIdLen = StrToBinA(snmpEngineId, engineId, 128);
  440          if (engineIdLen > 0)
  441             m_snmpSecurity->setAuthoritativeEngine(SNMP_Engine(engineId, engineIdLen, 0, 0));
  442       }
  443       m_snmpSecurity->recalculateKeys();
  444    }
  445    else
  446    {
  447       // This will create security context with V2C security model
  448       // USM fields will be loaded but keys will not be calculated
  449       m_snmpSecurity = new SNMP_SecurityContext(snmpAuthObject);
  450       m_snmpSecurity->setAuthMethod(static_cast<SNMP_AuthMethod>(snmpMethods & 0xFF));
  451       m_snmpSecurity->setAuthPassword(snmpAuthPassword);
  452       m_snmpSecurity->setPrivMethod(static_cast<SNMP_EncryptionMethod>(snmpMethods >> 8));
  453       m_snmpSecurity->setPrivPassword(snmpPrivPassword);
  454    }
  455 
  456    m_sysName = DBGetField(hResult, 0, 21, nullptr, 0);
  457 
  458    TCHAR baseAddr[16];
  459    TCHAR *value = DBGetField(hResult, 0, 22, baseAddr, 16);
  460    if (value != nullptr)
  461       StrToBin(value, m_baseBridgeAddress, MAC_ADDR_LENGTH);
  462 
  463    m_downSince = DBGetFieldLong(hResult, 0, 23);
  464    m_bootTime = DBGetFieldLong(hResult, 0, 24);
  465 
  466    // Setup driver
  467    TCHAR driverName[34];
  468    DBGetField(hResult, 0, 25, driverName, 34);
  469    StrStrip(driverName);
  470    if (driverName[0] != 0)
  471       m_driver = FindDriverByName(driverName);
  472 
  473    m_icmpProxy = DBGetFieldULong(hResult, 0, 26);
  474    m_agentCacheMode = (INT16)DBGetFieldLong(hResult, 0, 27);
  475    if ((m_agentCacheMode != AGENT_CACHE_ON) && (m_agentCacheMode != AGENT_CACHE_OFF))
  476       m_agentCacheMode = AGENT_CACHE_DEFAULT;
  477 
  478    m_sysContact = DBGetField(hResult, 0, 28, nullptr, 0);
  479    m_sysLocation = DBGetField(hResult, 0, 29, nullptr, 0);
  480 
  481    m_physicalContainer = DBGetFieldULong(hResult, 0, 30);
  482    m_rackImageFront = DBGetFieldGUID(hResult, 0, 31);
  483    m_rackPosition = (INT16)DBGetFieldLong(hResult, 0, 32);
  484    m_rackHeight = (INT16)DBGetFieldLong(hResult, 0, 33);
  485    m_lastAgentCommTime = DBGetFieldLong(hResult, 0, 34);
  486    m_syslogMessageCount = DBGetFieldInt64(hResult, 0, 35);
  487    m_snmpTrapCount = DBGetFieldInt64(hResult, 0, 36);
  488    m_snmpTrapLastTotal = m_snmpTrapCount;
  489    m_type = (NodeType)DBGetFieldLong(hResult, 0, 37);
  490    DBGetField(hResult, 0, 38, m_subType, MAX_NODE_SUBTYPE_LENGTH);
  491    DBGetField(hResult, 0, 39, m_sshLogin, MAX_SSH_LOGIN_LEN);
  492    DBGetField(hResult, 0, 40, m_sshPassword, MAX_SSH_PASSWORD_LEN);
  493    m_sshKeyId = DBGetFieldLong(hResult, 0, 74);
  494    m_sshPort = static_cast<uint16_t>(DBGetFieldLong(hResult, 0, 73));
  495    m_sshProxy = DBGetFieldULong(hResult, 0, 41);
  496    m_portRowCount = DBGetFieldULong(hResult, 0, 42);
  497    m_portNumberingScheme = DBGetFieldULong(hResult, 0, 43);
  498    m_agentCompressionMode = (INT16)DBGetFieldLong(hResult, 0, 44);
  499    m_tunnelId = DBGetFieldGUID(hResult, 0, 45);
  500    m_lldpNodeId = DBGetField(hResult, 0, 46, nullptr, 0);
  501    if ((m_lldpNodeId != nullptr) && (*m_lldpNodeId == 0))
  502       MemFreeAndNull(m_lldpNodeId);
  503    m_capabilities = DBGetFieldULong(hResult, 0, 47);
  504    m_failTimeSNMP = DBGetFieldLong(hResult, 0, 48);
  505    m_failTimeAgent = DBGetFieldLong(hResult, 0, 49);
  506    m_rackOrientation = static_cast<RackOrientation>(DBGetFieldLong(hResult, 0, 50));
  507    m_rackImageRear = DBGetFieldGUID(hResult, 0, 51);
  508    m_agentId = DBGetFieldGUID(hResult, 0, 52);
  509    m_agentCertSubject = DBGetField(hResult, 0, 53, nullptr, 0);
  510    if ((m_agentCertSubject != nullptr) && (m_agentCertSubject[0] == 0))
  511       MemFreeAndNull(m_agentCertSubject);
  512    DBGetField(hResult, 0, 54, m_hypervisorType, MAX_HYPERVISOR_TYPE_LENGTH);
  513    m_hypervisorInfo = DBGetFieldAsSharedString(hResult, 0, 55);
  514 
  515    switch(DBGetFieldLong(hResult, 0, 56))
  516    {
  517       case 1:
  518          m_icmpStatCollectionMode = IcmpStatCollectionMode::ON;
  519          break;
  520       case 2:
  521          m_icmpStatCollectionMode = IcmpStatCollectionMode::OFF;
  522          break;
  523       default:
  524          m_icmpStatCollectionMode = IcmpStatCollectionMode::DEFAULT;
  525          break;
  526    }
  527    m_chassisPlacementConf = DBGetField(hResult, 0, 57, nullptr, 0);
  528 
  529    m_vendor = DBGetFieldAsSharedString(hResult, 0, 58);
  530    m_productCode = DBGetFieldAsSharedString(hResult, 0, 59);
  531    m_productName = DBGetFieldAsSharedString(hResult, 0, 60);
  532    m_productVersion = DBGetFieldAsSharedString(hResult, 0, 61);
  533    m_serialNumber = DBGetFieldAsSharedString(hResult, 0, 62);
  534    m_cipDeviceType = static_cast<uint16_t>(DBGetFieldULong(hResult, 0, 63));
  535    m_cipStatus = static_cast<uint16_t>(DBGetFieldULong(hResult, 0, 64));
  536    m_cipState = static_cast<uint8_t>(DBGetFieldULong(hResult, 0, 65));
  537    m_eipProxy = DBGetFieldULong(hResult, 0, 66);
  538    m_eipPort = static_cast<uint16_t>(DBGetFieldULong(hResult, 0, 67));
  539    BYTE hardwareId[HARDWARE_ID_LENGTH];
  540    DBGetFieldByteArray2(hResult, 0, 68, hardwareId, HARDWARE_ID_LENGTH, 0);
  541    m_hardwareId = NodeHardwareId(hardwareId);
  542    m_cipVendorCode = static_cast<uint16_t>(DBGetFieldULong(hResult, 0, 69));
  543    m_agentCertMappingMethod = static_cast<CertificateMappingMethod>(DBGetFieldLong(hResult, 0, 70));
  544    m_agentCertMappingData = DBGetField(hResult, 0, 71, nullptr, 0);
  545    if ((m_agentCertMappingData != nullptr) && (m_agentCertMappingData[0] == 0))
  546       MemFreeAndNull(m_agentCertMappingData);
  547 
  548    DBFreeResult(hResult);
  549    DBFreeStatement(hStmt);
  550 
  551    if (m_isDeleted)
  552    {
  553       return true;
  554    }
  555 
  556    // Link node to subnets
  557    hStmt = DBPrepare(hdb, _T("SELECT subnet_id FROM nsmap WHERE node_id=?"));
  558    if (hStmt == nullptr)
  559       return false;
  560 
  561    DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
  562    hResult = DBSelectPrepared(hStmt);
  563    if (hResult == nullptr)
  564    {
  565       DBFreeStatement(hStmt);
  566       return false;     // Query failed
  567    }
  568 
  569    iNumRows = DBGetNumRows(hResult);
  570    for(i = 0; i < iNumRows; i++)
  571    {
  572       uint32_t subnetId = DBGetFieldULong(hResult, i, 0);
  573       shared_ptr<NetObj> subnet = FindObjectById(subnetId, OBJECT_SUBNET);
  574       if (subnet != nullptr)
  575       {
  576          subnet->addChild(self());
  577          addParent(subnet);
  578       }
  579       else
  580       {
  581          nxlog_write(NXLOG_ERROR, _T("Inconsistent database: node %s [%u] linked to non-existing subnet [%u]"), m_name, m_id, subnetId);
  582       }
  583    }
  584 
  585    DBFreeResult(hResult);
  586    DBFreeStatement(hStmt);
  587 
  588    loadItemsFromDB(hdb);
  589    loadACLFromDB(hdb);
  590 
  591    // Walk through all items in the node and load appropriate thresholds
  592    bResult = true;
  593    for(i = 0; i < m_dcObjects->size(); i++)
  594    {
  595       if (!m_dcObjects->get(i)->loadThresholdsFromDB(hdb))
  596       {
  597          DbgPrintf(3, _T("Cannot load thresholds for DCI %d of node %d (%s)"),
  598                    m_dcObjects->get(i)->getId(), dwId, m_name);
  599          bResult = false;
  600       }
  601    }
  602    loadDCIListForCleanup(hdb);
  603 
  604    updatePhysicalContainerBinding(m_physicalContainer);
  605 
  606    if (bResult)
  607    {
  608       // Load components
  609       hStmt = DBPrepare(hdb, _T("SELECT component_index,parent_index,position,component_class,if_index,name,description,model,serial_number,vendor,firmware FROM node_components WHERE node_id=?"));
  610       if (hStmt != nullptr)
  611       {
  612          DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
  613          hResult = DBSelectPrepared(hStmt);
  614          if (hResult != nullptr)
  615          {
  616             int count = DBGetNumRows(hResult);
  617             if (count > 0)
  618             {
  619                ObjectArray<Component> elements(count);
  620                for(int i = 0; i < count; i++)
  621                {
  622                   TCHAR name[256], description[256], model[256], serial[64], vendor[64], firmware[128];
  623                   elements.add(new Component(
  624                            DBGetFieldULong(hResult, i, 0), // index
  625                            DBGetFieldULong(hResult, i, 3), // class
  626                            DBGetFieldULong(hResult, i, 1), // parent index
  627                            DBGetFieldULong(hResult, i, 2), // position
  628                            DBGetFieldULong(hResult, i, 4), // ifIndex
  629                            DBGetField(hResult, i, 5, name, 256),
  630                            DBGetField(hResult, i, 6, description, 256),
  631                            DBGetField(hResult, i, 7, model, 256),
  632                            DBGetField(hResult, i, 8, serial, 64),
  633                            DBGetField(hResult, i, 9, vendor, 64),
  634                            DBGetField(hResult, i, 10, firmware, 128)
  635                            ));
  636                }
  637 
  638                Component *root = nullptr;
  639                for(int i = 0; i < elements.size(); i++)
  640                   if (elements.get(i)->getParentIndex() == 0)
  641                   {
  642                      root = elements.get(i);
  643                      break;
  644                   }
  645 
  646                if (root != nullptr)
  647                {
  648                   root->buildTree(&elements);
  649                   m_components = make_shared<ComponentTree>(root);
  650                }
  651                else
  652                {
  653                   nxlog_debug(6, _T("Node::loadFromDatabase(%s [%u]): root element for component tree not found"), m_name, m_id);
  654                   elements.setOwner(Ownership::True);   // cause element destruction on exit
  655                }
  656             }
  657             DBFreeResult(hResult);
  658          }
  659          else
  660          {
  661             bResult = false;
  662          }
  663          DBFreeStatement(hStmt);
  664       }
  665       else
  666       {
  667          bResult = false;
  668       }
  669 
  670       if (!bResult)
  671          DbgPrintf(3, _T("Cannot load components for node %d (%s)"), m_id, m_name);
  672    }
  673 
  674    if (bResult)
  675    {
  676       // Load software packages
  677       hStmt = DBPrepare(hdb, _T("SELECT name,version,vendor,install_date,url,description FROM software_inventory WHERE node_id=?"));
  678       if (hStmt != nullptr)
  679       {
  680          DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
  681          hResult = DBSelectPrepared(hStmt);
  682          if (hResult != nullptr)
  683          {
  684             int count = DBGetNumRows(hResult);
  685             if (count > 0)
  686             {
  687                m_softwarePackages = new ObjectArray<SoftwarePackage>(count, 64, Ownership::True);
  688                for(int i = 0; i < count; i++)
  689                   m_softwarePackages->add(new SoftwarePackage(hResult, i));
  690                m_softwarePackages->sort(PackageNameVersionComparator);
  691             }
  692             DBFreeResult(hResult);
  693          }
  694          else
  695          {
  696             bResult = false;
  697          }
  698          DBFreeStatement(hStmt);
  699       }
  700       else
  701       {
  702          bResult = false;
  703       }
  704 
  705       if (!bResult)
  706          DbgPrintf(3, _T("Cannot load software packages of node %d (%s)"), m_id, m_name);
  707    }
  708 
  709    if (bResult)
  710    {
  711       // Load hardware components
  712       hStmt = DBPrepare(hdb, _T("SELECT category,component_index,hw_type,vendor,model,location,capacity,part_number,serial_number,description FROM hardware_inventory WHERE node_id=?"));
  713       if (hStmt != nullptr)
  714       {
  715          DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
  716          hResult = DBSelectPrepared(hStmt);
  717          if (hResult != nullptr)
  718          {
  719             int count = DBGetNumRows(hResult);
  720             if (count > 0)
  721             {
  722                m_hardwareComponents = new ObjectArray<HardwareComponent>(count, 16, Ownership::True);
  723                for(int i = 0; i < count; i++)
  724                   m_hardwareComponents->add(new HardwareComponent(hResult, i));
  725                m_hardwareComponents->sort(HardwareComponentComparator);
  726             }
  727             DBFreeResult(hResult);
  728          }
  729          else
  730          {
  731             bResult = false;
  732          }
  733          DBFreeStatement(hStmt);
  734       }
  735       else
  736       {
  737          bResult = false;
  738       }
  739 
  740       if (!bResult)
  741          nxlog_debug(3, _T("Cannot load hardware information of node %d (%s)"), m_id, m_name);
  742    }
  743 
  744    if (bResult && isIcmpStatCollectionEnabled())
  745    {
  746       m_icmpStatCollectors = new StringObjectMap<IcmpStatCollector>(Ownership::True);
  747       hStmt = DBPrepare(hdb, _T("SELECT poll_target FROM icmp_statistics WHERE object_id=?"));
  748       if (hStmt != nullptr)
  749       {
  750          DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
  751          hResult = DBSelectPrepared(hStmt);
  752          if (hResult != nullptr)
  753          {
  754             int period = ConfigReadInt(_T("ICMP.StatisticPeriod"), 60);
  755             int count = DBGetNumRows(hResult);
  756             for(int i = 0; i < count; i++)
  757             {
  758                TCHAR target[128];
  759                DBGetField(hResult, i, 0, target, 128);
  760                IcmpStatCollector *c = IcmpStatCollector::loadFromDatabase(hdb, m_id, target, period);
  761                if (c != nullptr)
  762                   m_icmpStatCollectors->set(target, c);
  763                else
  764                   nxlog_debug(3, _T("Cannot load ICMP statistic collector %s for node %s [%u]"), target, m_name, m_id);
  765             }
  766             DBFreeResult(hResult);
  767 
  768             // Check that primary target exist
  769             if (!m_icmpStatCollectors->contains(_T("PRI")))
  770                m_icmpStatCollectors->set(_T("PRI"), new IcmpStatCollector(period));
  771          }
  772          else
  773          {
  774             bResult = false;
  775          }
  776          DBFreeStatement(hStmt);
  777       }
  778       else
  779       {
  780          bResult = false;
  781       }
  782    }
  783 
  784    if (bResult)
  785    {
  786       hStmt = DBPrepare(hdb, _T("SELECT ip_addr FROM icmp_target_address_list WHERE node_id=?"));
  787       if (hStmt != nullptr)
  788       {
  789          DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
  790          hResult = DBSelectPrepared(hStmt);
  791          if (hResult != nullptr)
  792          {
  793             int count = DBGetNumRows(hResult);
  794             for(int i = 0; i < count; i++)
  795             {
  796                InetAddress addr = DBGetFieldInetAddr(hResult, i, 0);
  797                if (addr.isValidUnicast() && !m_icmpTargets.hasAddress(addr))
  798                   m_icmpTargets.add(addr);
  799             }
  800             DBFreeResult(hResult);
  801          }
  802          else
  803          {
  804             bResult = false;
  805          }
  806          DBFreeStatement(hStmt);
  807       }
  808       else
  809       {
  810          bResult = false;
  811       }
  812    }
  813 
  814    return bResult;
  815 }
  816 
  817 /**
  818  * Save component
  819  */
  820 static bool SaveComponent(DB_STATEMENT hStmt, const Component *component)
  821 {
  822    DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, component->getIndex());
  823    DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, component->getParentIndex());
  824    DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, component->getPosition());
  825    DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, component->getClass());
  826    DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, component->getIfIndex());
  827    DBBind(hStmt, 7, DB_SQLTYPE_VARCHAR, component->getName(), DB_BIND_STATIC, 255);
  828    DBBind(hStmt, 8, DB_SQLTYPE_VARCHAR, component->getDescription(), DB_BIND_STATIC, 255);
  829    DBBind(hStmt, 9, DB_SQLTYPE_VARCHAR, component->getModel(), DB_BIND_STATIC, 255);
  830    DBBind(hStmt, 10, DB_SQLTYPE_VARCHAR, component->getSerial(), DB_BIND_STATIC, 63);
  831    DBBind(hStmt, 11, DB_SQLTYPE_VARCHAR, component->getVendor(), DB_BIND_STATIC, 63);
  832    DBBind(hStmt, 12, DB_SQLTYPE_VARCHAR, component->getFirmware(), DB_BIND_STATIC, 127);
  833    if (!DBExecute(hStmt))
  834       return false;
  835 
  836    const ObjectArray<Component> *children = component->getChildren();
  837    for(int i = 0; i < children->size(); i++)
  838       if (!SaveComponent(hStmt, children->get(i)))
  839          return false;
  840    return true;
  841 }
  842 
  843 /**
  844  * Save ICMP statistics collector
  845  */
  846 static EnumerationCallbackResult SaveIcmpStatCollector(const TCHAR *target, const IcmpStatCollector *collector, std::pair<uint32_t, DB_HANDLE> *context)
  847 {
  848    return collector->saveToDatabase(context->second, context->first, target) ? _CONTINUE : _STOP;
  849 }
  850 
  851 /**
  852  * Save object to database
  853  */
  854 bool Node::saveToDatabase(DB_HANDLE hdb)
  855 {
  856    bool success = super::saveToDatabase(hdb);
  857 
  858    if (success && (m_modified & MODIFY_NODE_PROPERTIES))
  859    {
  860       static const TCHAR *columns[] = {
  861          _T("primary_ip"), _T("primary_name"), _T("snmp_port"), _T("capabilities"), _T("snmp_version"), _T("community"),
  862          _T("status_poll_type"), _T("agent_port"), _T("secret"), _T("snmp_oid"), _T("uname"),
  863          _T("agent_version"), _T("platform_name"), _T("poller_node_id"), _T("zone_guid"), _T("proxy_node"), _T("snmp_proxy"),
  864          _T("icmp_proxy"), _T("required_polls"), _T("use_ifxtable"), _T("usm_auth_password"), _T("usm_priv_password"),
  865          _T("usm_methods"), _T("snmp_sys_name"), _T("bridge_base_addr"), _T("down_since"), _T("driver_name"),
  866          _T("rack_image_front"), _T("rack_position"), _T("rack_height"), _T("physical_container_id"), _T("boot_time"), _T("agent_cache_mode"),
  867          _T("snmp_sys_contact"), _T("snmp_sys_location"), _T("last_agent_comm_time"), _T("syslog_msg_count"),
  868          _T("snmp_trap_count"), _T("node_type"), _T("node_subtype"), _T("ssh_login"), _T("ssh_password"), _T("ssh_key_id"), _T("ssh_port"),
  869          _T("ssh_proxy"), _T("port_rows"), _T("port_numbering_scheme"), _T("agent_comp_mode"), _T("tunnel_id"), _T("lldp_id"),
  870          _T("fail_time_snmp"), _T("fail_time_agent"), _T("rack_orientation"), _T("rack_image_rear"), _T("agent_id"),
  871          _T("agent_cert_subject"), _T("hypervisor_type"), _T("hypervisor_info"), _T("icmp_poll_mode"), _T("chassis_placement_config"),
  872          _T("vendor"), _T("product_code"), _T("product_name"), _T("product_version"), _T("serial_number"), _T("cip_device_type"),
  873          _T("cip_status"), _T("cip_state"), _T("eip_proxy"), _T("eip_port"), _T("hardware_id"), _T("cip_vendor_code"),
  874          _T("agent_cert_mapping_method"), _T("agent_cert_mapping_data"), _T("snmp_engine_id"),
  875          nullptr
  876       };
  877 
  878       DB_STATEMENT hStmt = DBPrepareMerge(hdb, _T("nodes"), _T("id"), m_id, columns);
  879       if (hStmt != nullptr)
  880       {
  881          lockProperties();
  882 
  883          int snmpMethods = m_snmpSecurity->getAuthMethod() | (m_snmpSecurity->getPrivMethod() << 8);
  884          TCHAR ipAddr[64], baseAddress[16], cacheMode[16], compressionMode[16], hardwareId[HARDWARE_ID_LENGTH * 2 + 1];
  885 
  886          const TCHAR *icmpPollMode;
  887          switch(m_icmpStatCollectionMode)
  888          {
  889             case IcmpStatCollectionMode::ON:
  890                icmpPollMode = _T("1");
  891                break;
  892             case IcmpStatCollectionMode::OFF:
  893                icmpPollMode = _T("2");
  894                break;
  895             default:
  896                icmpPollMode = _T("0");
  897                break;
  898          }
  899 
  900          const TCHAR *agentCertMappingMethod;
  901          switch(m_agentCertMappingMethod)
  902          {
  903             case MAP_CERTIFICATE_BY_CN:
  904                agentCertMappingMethod = _T("2");
  905                break;
  906             case MAP_CERTIFICATE_BY_PUBKEY:
  907                agentCertMappingMethod = _T("1");
  908                break;
  909             default:
  910                agentCertMappingMethod = _T("0");
  911                break;
  912          }
  913 
  914          DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, m_ipAddress.toString(ipAddr), DB_BIND_STATIC);
  915          DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, m_primaryHostName, DB_BIND_TRANSIENT);
  916          DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (LONG)m_snmpPort);
  917          DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, m_capabilities);
  918          DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (LONG)m_snmpVersion);
  919 #ifdef UNICODE
  920          DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, WideStringFromMBString(m_snmpSecurity->getCommunity()), DB_BIND_DYNAMIC);
  921 #else
  922          DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, m_snmpSecurity->getCommunity(), DB_BIND_STATIC);
  923 #endif
  924          DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, (LONG)m_iStatusPollType);
  925          DBBind(hStmt, 8, DB_SQLTYPE_INTEGER, (LONG)m_agentPort);
  926          DBBind(hStmt, 9, DB_SQLTYPE_VARCHAR, m_agentSecret, DB_BIND_STATIC);
  927          DBBind(hStmt, 10, DB_SQLTYPE_VARCHAR, m_snmpObjectId, DB_BIND_STATIC);
  928          DBBind(hStmt, 11, DB_SQLTYPE_VARCHAR, m_sysDescription, DB_BIND_STATIC);
  929          DBBind(hStmt, 12, DB_SQLTYPE_VARCHAR, m_agentVersion, DB_BIND_STATIC);
  930          DBBind(hStmt, 13, DB_SQLTYPE_VARCHAR, m_platformName, DB_BIND_STATIC);
  931          DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, m_pollerNode);
  932          DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, m_zoneUIN);
  933          DBBind(hStmt, 16, DB_SQLTYPE_INTEGER, m_agentProxy);
  934          DBBind(hStmt, 17, DB_SQLTYPE_INTEGER, m_snmpProxy);
  935          DBBind(hStmt, 18, DB_SQLTYPE_INTEGER, m_icmpProxy);
  936          DBBind(hStmt, 19, DB_SQLTYPE_INTEGER, (LONG)m_requiredPollCount);
  937          DBBind(hStmt, 20, DB_SQLTYPE_INTEGER, (LONG)m_nUseIfXTable);
  938 #ifdef UNICODE
  939          DBBind(hStmt, 21, DB_SQLTYPE_VARCHAR, WideStringFromMBString(m_snmpSecurity->getAuthPassword()), DB_BIND_DYNAMIC);
  940          DBBind(hStmt, 22, DB_SQLTYPE_VARCHAR, WideStringFromMBString(m_snmpSecurity->getPrivPassword()), DB_BIND_DYNAMIC);
  941 #else
  942          DBBind(hStmt, 21, DB_SQLTYPE_VARCHAR, m_snmpSecurity->getAuthPassword(), DB_BIND_STATIC);
  943          DBBind(hStmt, 22, DB_SQLTYPE_VARCHAR, m_snmpSecurity->getPrivPassword(), DB_BIND_STATIC);
  944 #endif
  945          DBBind(hStmt, 23, DB_SQLTYPE_INTEGER, (LONG)snmpMethods);
  946          DBBind(hStmt, 24, DB_SQLTYPE_VARCHAR, m_sysName, DB_BIND_STATIC, 127);
  947          DBBind(hStmt, 25, DB_SQLTYPE_VARCHAR, BinToStr(m_baseBridgeAddress, MAC_ADDR_LENGTH, baseAddress), DB_BIND_STATIC);
  948          DBBind(hStmt, 26, DB_SQLTYPE_INTEGER, (LONG)m_downSince);
  949          DBBind(hStmt, 27, DB_SQLTYPE_VARCHAR, (m_driver != nullptr) ? m_driver->getName() : _T(""), DB_BIND_STATIC);
  950          DBBind(hStmt, 28, DB_SQLTYPE_VARCHAR, m_rackImageFront);   // rack image front
  951          DBBind(hStmt, 29, DB_SQLTYPE_INTEGER, m_rackPosition); // rack position
  952          DBBind(hStmt, 30, DB_SQLTYPE_INTEGER, m_rackHeight);   // device height in rack units
  953          DBBind(hStmt, 31, DB_SQLTYPE_INTEGER, m_physicalContainer);   // rack ID
  954          DBBind(hStmt, 32, DB_SQLTYPE_INTEGER, (LONG)m_bootTime);
  955          DBBind(hStmt, 33, DB_SQLTYPE_VARCHAR, _itot(m_agentCacheMode, cacheMode, 10), DB_BIND_STATIC, 1);
  956          DBBind(hStmt, 34, DB_SQLTYPE_VARCHAR, m_sysContact, DB_BIND_STATIC, 127);
  957          DBBind(hStmt, 35, DB_SQLTYPE_VARCHAR, m_sysLocation, DB_BIND_STATIC, 255);
  958          DBBind(hStmt, 36, DB_SQLTYPE_INTEGER, (LONG)m_lastAgentCommTime);
  959          DBBind(hStmt, 37, DB_SQLTYPE_BIGINT, m_syslogMessageCount);
  960          DBBind(hStmt, 38, DB_SQLTYPE_BIGINT, m_snmpTrapCount);
  961          DBBind(hStmt, 39, DB_SQLTYPE_INTEGER, (INT32)m_type);
  962          DBBind(hStmt, 40, DB_SQLTYPE_VARCHAR, m_subType, DB_BIND_STATIC);
  963          DBBind(hStmt, 41, DB_SQLTYPE_VARCHAR, m_sshLogin, DB_BIND_STATIC);
  964          DBBind(hStmt, 42, DB_SQLTYPE_VARCHAR, m_sshPassword, DB_BIND_STATIC);
  965          DBBind(hStmt, 43, DB_SQLTYPE_INTEGER, m_sshKeyId);
  966          DBBind(hStmt, 44, DB_SQLTYPE_INTEGER, static_cast<int32_t>(m_sshPort));
  967          DBBind(hStmt, 45, DB_SQLTYPE_INTEGER, m_sshProxy);
  968          DBBind(hStmt, 46, DB_SQLTYPE_INTEGER, m_portRowCount);
  969          DBBind(hStmt, 47, DB_SQLTYPE_INTEGER, m_portNumberingScheme);
  970          DBBind(hStmt, 48, DB_SQLTYPE_VARCHAR, _itot(m_agentCompressionMode, compressionMode, 10), DB_BIND_STATIC, 1);
  971          DBBind(hStmt, 49, DB_SQLTYPE_VARCHAR, m_tunnelId);
  972          DBBind(hStmt, 50, DB_SQLTYPE_VARCHAR, m_lldpNodeId, DB_BIND_STATIC);
  973          DBBind(hStmt, 51, DB_SQLTYPE_INTEGER, (LONG)m_failTimeSNMP);
  974          DBBind(hStmt, 52, DB_SQLTYPE_INTEGER, (LONG)m_failTimeAgent);
  975          DBBind(hStmt, 53, DB_SQLTYPE_INTEGER, m_rackOrientation);
  976          DBBind(hStmt, 54, DB_SQLTYPE_VARCHAR, m_rackImageRear);
  977          DBBind(hStmt, 55, DB_SQLTYPE_VARCHAR, m_agentId);
  978          DBBind(hStmt, 56, DB_SQLTYPE_VARCHAR, m_agentCertSubject, DB_BIND_STATIC);
  979          DBBind(hStmt, 57, DB_SQLTYPE_VARCHAR, m_hypervisorType, DB_BIND_STATIC);
  980          DBBind(hStmt, 58, DB_SQLTYPE_VARCHAR, m_hypervisorInfo, DB_BIND_STATIC);
  981          DBBind(hStmt, 59, DB_SQLTYPE_VARCHAR, icmpPollMode, DB_BIND_STATIC);
  982          DBBind(hStmt, 60, DB_SQLTYPE_VARCHAR, m_chassisPlacementConf, DB_BIND_STATIC);
  983          DBBind(hStmt, 61, DB_SQLTYPE_VARCHAR, m_vendor, DB_BIND_STATIC, 127);
  984          DBBind(hStmt, 62, DB_SQLTYPE_VARCHAR, m_productCode, DB_BIND_STATIC, 31);
  985          DBBind(hStmt, 63, DB_SQLTYPE_VARCHAR, m_productName, DB_BIND_STATIC, 127);
  986          DBBind(hStmt, 64, DB_SQLTYPE_VARCHAR, m_productVersion, DB_BIND_STATIC, 15);
  987          DBBind(hStmt, 65, DB_SQLTYPE_VARCHAR, m_serialNumber, DB_BIND_STATIC, 31);
  988          DBBind(hStmt, 66, DB_SQLTYPE_INTEGER, m_cipDeviceType);
  989          DBBind(hStmt, 67, DB_SQLTYPE_INTEGER, m_cipStatus);
  990          DBBind(hStmt, 68, DB_SQLTYPE_INTEGER, m_cipState);
  991          DBBind(hStmt, 69, DB_SQLTYPE_INTEGER, m_eipProxy);
  992          DBBind(hStmt, 70, DB_SQLTYPE_INTEGER, m_eipPort);
  993          DBBind(hStmt, 71, DB_SQLTYPE_VARCHAR, BinToStr(m_hardwareId.value(), HARDWARE_ID_LENGTH, hardwareId), DB_BIND_STATIC);
  994          DBBind(hStmt, 72, DB_SQLTYPE_INTEGER, m_cipVendorCode);
  995          DBBind(hStmt, 73, DB_SQLTYPE_VARCHAR, agentCertMappingMethod, DB_BIND_STATIC);
  996          DBBind(hStmt, 74, DB_SQLTYPE_VARCHAR, m_agentCertMappingData, DB_BIND_STATIC);
  997          if (m_snmpSecurity != nullptr)
  998          {
  999             DBBind(hStmt, 75, DB_SQLTYPE_VARCHAR, m_snmpSecurity->getAuthoritativeEngine().toString(), DB_BIND_TRANSIENT);
 1000          }
 1001          else
 1002          {
 1003             DBBind(hStmt, 75, DB_SQLTYPE_VARCHAR, _T(""), DB_BIND_STATIC);
 1004          }
 1005          DBBind(hStmt, 76, DB_SQLTYPE_INTEGER, m_id);
 1006 
 1007          success = DBExecute(hStmt);
 1008          DBFreeStatement(hStmt);
 1009 
 1010          unlockProperties();
 1011       }
 1012       else
 1013       {
 1014          success = false;
 1015       }
 1016    }
 1017 
 1018    if (success && (m_modified & MODIFY_COMPONENTS))
 1019    {
 1020       success = executeQueryOnObject(hdb, _T("DELETE FROM node_components WHERE node_id=?"));
 1021       lockProperties();
 1022       if (success && (m_components != nullptr))
 1023       {
 1024          const Component *root = m_components->getRoot();
 1025          if (root != nullptr)
 1026          {
 1027             DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO node_components (node_id,component_index,parent_index,position,component_class,if_index,name,description,model,serial_number,vendor,firmware) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"));
 1028             if (hStmt != nullptr)
 1029             {
 1030                DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
 1031                success = SaveComponent(hStmt, root);
 1032                DBFreeStatement(hStmt);
 1033             }
 1034             else
 1035             {
 1036                success = false;
 1037             }
 1038          }
 1039       }
 1040       unlockProperties();
 1041    }
 1042 
 1043    if (success && (m_modified & MODIFY_SOFTWARE_INVENTORY))
 1044    {
 1045       success = executeQueryOnObject(hdb, _T("DELETE FROM software_inventory WHERE node_id=?"));
 1046       lockProperties();
 1047       if ((m_softwarePackages != nullptr) && !m_softwarePackages->isEmpty())
 1048       {
 1049          DB_STATEMENT hStmt = DBPrepare(hdb,
 1050                   _T("INSERT INTO software_inventory (node_id,name,version,vendor,install_date,url,description) VALUES (?,?,?,?,?,?,?)"),
 1051                   m_softwarePackages->size() > 1);
 1052          if (hStmt != nullptr)
 1053          {
 1054             DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
 1055             for(int i = 0; success && (i < m_softwarePackages->size()); i++)
 1056                success = m_softwarePackages->get(i)->saveToDatabase(hStmt);
 1057             DBFreeStatement(hStmt);
 1058          }
 1059          else
 1060          {
 1061             success = false;
 1062          }
 1063       }
 1064       unlockProperties();
 1065    }
 1066 
 1067    if (success && (m_modified & MODIFY_HARDWARE_INVENTORY))
 1068    {
 1069       success = executeQueryOnObject(hdb, _T("DELETE FROM hardware_inventory WHERE node_id=?"));
 1070       lockProperties();
 1071       if (success && (m_hardwareComponents != nullptr) && !m_hardwareComponents->isEmpty())
 1072       {
 1073          DB_STATEMENT hStmt = DBPrepare(hdb,
 1074                   _T("INSERT INTO hardware_inventory (node_id,category,component_index,hw_type,vendor,model,location,capacity,part_number,serial_number,description) VALUES (?,?,?,?,?,?,?,?,?,?,?)"),
 1075                   m_hardwareComponents->size() > 1);
 1076          if (hStmt != nullptr)
 1077          {
 1078             DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
 1079             for(int i = 0; success && i < m_hardwareComponents->size(); i++)
 1080                success = m_hardwareComponents->get(i)->saveToDatabase(hStmt);
 1081             DBFreeStatement(hStmt);
 1082          }
 1083       }
 1084       unlockProperties();
 1085    }
 1086 
 1087    // Save ICMP pollers
 1088    if (success && (m_modified & MODIFY_ICMP_POLL_SETTINGS))
 1089    {
 1090       lockProperties();
 1091 
 1092       success = executeQueryOnObject(hdb, _T("DELETE FROM icmp_statistics WHERE object_id=?"));
 1093       if (success && isIcmpStatCollectionEnabled() && (m_icmpStatCollectors != nullptr) && !m_icmpStatCollectors->isEmpty())
 1094       {
 1095          std::pair<uint32_t, DB_HANDLE> context(m_id, hdb);
 1096          success = (m_icmpStatCollectors->forEach(SaveIcmpStatCollector, &context) == _CONTINUE);
 1097       }
 1098 
 1099       if (success)
 1100          success = executeQueryOnObject(hdb, _T("DELETE FROM icmp_target_address_list WHERE node_id=?"));
 1101 
 1102       if (success && !m_icmpTargets.isEmpty())
 1103       {
 1104          DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO icmp_target_address_list (node_id,ip_addr) VALUES (?,?)"), m_icmpTargets.size() > 1);
 1105          if (hStmt != nullptr)
 1106          {
 1107             DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
 1108             for(int i = 0; success && i < m_icmpTargets.size(); i++)
 1109             {
 1110                DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, m_icmpTargets.get(i));
 1111                success = DBExecute(hStmt);
 1112             }
 1113             DBFreeStatement(hStmt);
 1114          }
 1115          else
 1116          {
 1117             success = false;
 1118          }
 1119       }
 1120 
 1121       unlockProperties();
 1122    }
 1123 
 1124    return success;
 1125 }
 1126 
 1127 /**
 1128  * Save runtime data to database. Called only on server shutdown to save
 1129  * less important but frequently changing runtime data when it is not feasible
 1130  * to mark object as modified on each change of such data.
 1131  */
 1132 bool Node::saveRuntimeData(DB_HANDLE hdb)
 1133 {
 1134    if (!super::saveRuntimeData(hdb))
 1135       return false;
 1136 
 1137    lockProperties();
 1138    if (isIcmpStatCollectionEnabled() && (m_icmpStatCollectors != nullptr) && !m_icmpStatCollectors->isEmpty())
 1139    {
 1140       std::pair<UINT32, DB_HANDLE> context(m_id, hdb);
 1141       if (m_icmpStatCollectors->forEach(SaveIcmpStatCollector, &context) == _STOP)
 1142       {
 1143          unlockProperties();
 1144          return false;
 1145       }
 1146    }
 1147    unlockProperties();
 1148 
 1149    if ((m_lastAgentCommTime == TIMESTAMP_NEVER) && (m_syslogMessageCount == 0) && (m_snmpTrapCount == 0))
 1150       return true;
 1151 
 1152    DB_STATEMENT hStmt = DBPrepare(hdb, _T("UPDATE nodes SET last_agent_comm_time=?,syslog_msg_count=?,snmp_trap_count=?,snmp_engine_id=? WHERE id=?"));
 1153    if (hStmt == nullptr)
 1154       return false;
 1155 
 1156    lockProperties();
 1157    DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (INT32)m_lastAgentCommTime);
 1158    DBBind(hStmt, 2, DB_SQLTYPE_BIGINT, m_syslogMessageCount);
 1159    DBBind(hStmt, 3, DB_SQLTYPE_BIGINT, m_snmpTrapCount);
 1160    if (m_snmpSecurity != nullptr)
 1161       DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, m_snmpSecurity->getAuthoritativeEngine().toString(), DB_BIND_TRANSIENT);
 1162    else
 1163       DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, _T(""), DB_BIND_STATIC);
 1164    DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, m_id);
 1165    unlockProperties();
 1166 
 1167    bool success = DBExecute(hStmt);
 1168    DBFreeStatement(hStmt);
 1169    return success;
 1170 }
 1171 
 1172 /**
 1173  * Delete object from database
 1174  */
 1175 bool Node::deleteFromDatabase(DB_HANDLE hdb)
 1176 {
 1177    bool success = super::deleteFromDatabase(hdb);
 1178    if (success)
 1179       success = executeQueryOnObject(hdb, _T("DELETE FROM nodes WHERE id=?"));
 1180    if (success)
 1181       success = executeQueryOnObject(hdb, _T("DELETE FROM nsmap WHERE node_id=?"));
 1182    if (success)
 1183       success = executeQueryOnObject(hdb, _T("DELETE FROM icmp_statistics WHERE object_id=?"));
 1184    if (success)
 1185       success = executeQueryOnObject(hdb, _T("DELETE FROM icmp_target_address_list WHERE node_id=?"));
 1186    return success;
 1187 }
 1188 
 1189 /**
 1190  * Get ARP cache from node
 1191  */
 1192 shared_ptr<ArpCache> Node::getArpCache(bool forceRead)
 1193 {
 1194    shared_ptr<ArpCache> arpCache;
 1195    if (!forceRead)
 1196    {
 1197       lockProperties();
 1198       if ((m_arpCache != nullptr) && (m_arpCache->timestamp() > time(nullptr) - 3600))
 1199       {
 1200          arpCache = m_arpCache;
 1201       }
 1202       unlockProperties();
 1203       if (arpCache != nullptr)
 1204          return arpCache;
 1205    }
 1206 
 1207    if (m_capabilities & NC_IS_LOCAL_MGMT)
 1208    {
 1209       arpCache = GetLocalArpCache();
 1210    }
 1211    else if (m_capabilities & NC_IS_NATIVE_AGENT)
 1212    {
 1213       shared_ptr<AgentConnectionEx> conn = getAgentConnection();
 1214       if (conn != nullptr)
 1215       {
 1216          arpCache = conn->getArpCache();
 1217       }
 1218    }
 1219    else if ((m_capabilities & NC_IS_SNMP) && (m_driver != nullptr))
 1220    {
 1221       SNMP_Transport *transport = createSnmpTransport();
 1222       if (transport != nullptr)
 1223       {
 1224          arpCache = m_driver->getArpCache(transport, m_driverData);
 1225          delete transport;
 1226       }
 1227    }
 1228 
 1229    if (arpCache != nullptr)
 1230    {
 1231       nxlog_debug_tag(DEBUG_TAG_TOPO_ARP, 6, _T("Read ARP cache from node %s [%u] (%d entries)"), m_name, m_id, arpCache->size());
 1232       arpCache->dumpToLog();
 1233 
 1234       lockProperties();
 1235       m_arpCache = arpCache;
 1236       unlockProperties();
 1237    }
 1238    return arpCache;
 1239 }
 1240 
 1241 /**
 1242  * Get list of interfaces from node
 1243  */
 1244 InterfaceList *Node::getInterfaceList()
 1245 {
 1246    InterfaceList *pIfList = nullptr;
 1247 
 1248    if ((m_capabilities & NC_IS_NATIVE_AGENT) && (!(m_flags & NF_DISABLE_NXCP)))
 1249    {
 1250       shared_ptr<AgentConnectionEx> conn = getAgentConnection();
 1251       if (conn != nullptr)
 1252       {
 1253          pIfList = conn->getInterfaceList();
 1254       }
 1255    }
 1256    if ((pIfList == nullptr) && (m_capabilities & NC_IS_LOCAL_MGMT))
 1257    {
 1258       pIfList = GetLocalInterfaceList();
 1259    }
 1260    if ((pIfList == nullptr) && (m_capabilities & NC_IS_SNMP) &&
 1261        (!(m_flags & NF_DISABLE_SNMP)) && (m_driver != nullptr))
 1262    {
 1263       SNMP_Transport *pTransport = createSnmpTransport();
 1264       if (pTransport != nullptr)
 1265       {
 1266          bool useIfXTable;
 1267          if (m_nUseIfXTable == IFXTABLE_DEFAULT)
 1268          {
 1269             useIfXTable = ConfigReadBoolean(_T("Objects.Interfaces.UseIfXTable"), true);
 1270          }
 1271          else
 1272          {
 1273             useIfXTable = (m_nUseIfXTable == IFXTABLE_ENABLED) ? true : false;
 1274          }
 1275 
 1276          int useAliases = ConfigReadInt(_T("Objects.Interfaces.UseAliases"), 0);
 1277          nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 6, _T("Node::getInterfaceList(node=%s [%u]): calling driver (useAliases=%d, useIfXTable=%d)"),
 1278                   m_name, m_id, useAliases, useIfXTable);
 1279          pIfList = m_driver->getInterfaces(pTransport, this, m_driverData, useAliases, useIfXTable);
 1280 
 1281          if ((pIfList != nullptr) && (m_capabilities & NC_IS_BRIDGE))
 1282          {
 1283             BridgeMapPorts(pTransport, pIfList);
 1284          }
 1285          delete pTransport;
 1286       }
 1287       else
 1288       {
 1289          nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 6, _T("Node::getInterfaceList(node=%s [%u]): cannot create SNMP transport"), m_name, m_id);
 1290       }
 1291    }
 1292 
 1293    if (pIfList != nullptr)
 1294    {
 1295       checkInterfaceNames(pIfList);
 1296       addVrrpInterfaces(pIfList);
 1297    }
 1298 
 1299    return pIfList;
 1300 }
 1301 
 1302 /**
 1303  * Add VRRP interfaces to interface list
 1304  */
 1305 void Node::addVrrpInterfaces(InterfaceList *ifList)
 1306 {
 1307    int i, j, k;
 1308    TCHAR buffer[32];
 1309 
 1310    lockProperties();
 1311    if (m_vrrpInfo != nullptr)
 1312    {
 1313       nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 6, _T("Node::addVrrpInterfaces(node=%s [%d]): m_vrrpInfo->size()=%d"), m_name, (int)m_id, m_vrrpInfo->size());
 1314 
 1315       for(i = 0; i < m_vrrpInfo->size(); i++)
 1316       {
 1317          VrrpRouter *router = m_vrrpInfo->getRouter(i);
 1318          nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 6, _T("Node::addVrrpInterfaces(node=%s [%u]): vrouter %d state=%d"), m_name, m_id, i, router->getState());
 1319          if (router->getState() != VRRP_STATE_MASTER)
 1320             continue;   // Do not add interfaces if router is not in master state
 1321 
 1322          // Get netmask for this VR
 1323          int maskBits = 0;
 1324          for(j = 0; j < ifList->size(); j++)
 1325          {
 1326             InterfaceInfo *iface = ifList->get(j);
 1327             if (iface->index == router->getIfIndex())
 1328             {
 1329                for(int k = 0; k < iface->ipAddrList.size(); k++)
 1330                {
 1331                   const InetAddress& addr = iface->ipAddrList.get(k);
 1332                   if (addr.getSubnetAddress().contain(router->getVip(0)))
 1333                   {
 1334                      maskBits = addr.getMaskBits();
 1335                   }
 1336                }
 1337                break;
 1338             }
 1339          }
 1340 
 1341          // Walk through all VR virtual IPs
 1342          for(j = 0; j < router->getVipCount(); j++)
 1343          {
 1344             uint32_t vip = router->getVip(j);
 1345             nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 6, _T("Node::addVrrpInterfaces(node=%s [%u]): checking VIP %s@%d"), m_name, m_id, IpToStr(vip, buffer), i);
 1346             if (vip != 0)
 1347             {
 1348                for(k = 0; k < ifList->size(); k++)
 1349                   if (ifList->get(k)->hasAddress(vip))
 1350                      break;
 1351                if (k == ifList->size())
 1352                {
 1353                   InterfaceInfo *iface = new InterfaceInfo(0);
 1354                   _sntprintf(iface->name, MAX_DB_STRING, _T("vrrp.%u.%u.%d"), router->getId(), router->getIfIndex(), j);
 1355                   memcpy(iface->macAddr, router->getVirtualMacAddr(), MAC_ADDR_LENGTH);
 1356                   InetAddress addr(vip);
 1357                   addr.setMaskBits(maskBits);
 1358                   iface->ipAddrList.add(addr);
 1359                   ifList->add(iface);
 1360                   nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 6, _T("Node::addVrrpInterfaces(node=%s [%u]): added interface %s"), m_name, m_id, iface->name);
 1361                }
 1362             }
 1363          }
 1364       }
 1365    }
 1366    unlockProperties();
 1367 }
 1368 
 1369 /**
 1370  * Find interface by index.
 1371  *
 1372  * @param ifIndex interface index to match
 1373  * @return pointer to interface object or nullptr if appropriate interface couldn't be found
 1374  */
 1375 shared_ptr<Interface> Node::findInterfaceByIndex(UINT32 ifIndex) const
 1376 {
 1377    readLockChildList();
 1378    for(int i = 0; i < getChildList().size(); i++)
 1379       if (getChildList().get(i)->getObjectClass() == OBJECT_INTERFACE)
 1380       {
 1381          auto iface = static_pointer_cast<Interface>(getChildList().getShared(i));
 1382          if (iface->getIfIndex() == ifIndex)
 1383          {
 1384             unlockChildList();
 1385             return iface;
 1386          }
 1387       }
 1388    unlockChildList();
 1389    return shared_ptr<Interface>();
 1390 }
 1391 
 1392 /**
 1393  * Find interface by name or description
 1394  * Returns pointer to interface object or nullptr if appropriate interface couldn't be found
 1395  */
 1396 shared_ptr<Interface> Node::findInterfaceByName(const TCHAR *name) const
 1397 {
 1398    if ((name == nullptr) || (name[0] == 0))
 1399       return shared_ptr<Interface>();
 1400 
 1401    readLockChildList();
 1402    for(int i = 0; i < getChildList().size(); i++)
 1403       if (getChildList().get(i)->getObjectClass() == OBJECT_INTERFACE)
 1404       {
 1405          auto iface = static_pointer_cast<Interface>(getChildList().getShared(i));
 1406          if (!_tcsicmp(iface->getName(), name) || !_tcsicmp(iface->getDescription(), name))
 1407          {
 1408             unlockChildList();
 1409             return iface;
 1410          }
 1411       }
 1412    unlockChildList();
 1413    return shared_ptr<Interface>();
 1414 }
 1415 
 1416 /**
 1417  * Find interface by alias
 1418  * Returns pointer to interface object or nullptr if appropriate interface couldn't be found
 1419  */
 1420 shared_ptr<Interface> Node::findInterfaceByAlias(const TCHAR *alias) const
 1421 {
 1422    if ((alias == nullptr) || (alias[0] == 0))
 1423       return shared_ptr<Interface>();
 1424 
 1425    readLockChildList();
 1426    for(int i = 0; i < getChildList().size(); i++)
 1427       if (getChildList().get(i)->getObjectClass() == OBJECT_INTERFACE)
 1428       {
 1429          auto iface = static_pointer_cast<Interface>(getChildList().getShared(i));
 1430          if (!_tcsicmp(iface->getAlias(), alias))
 1431          {
 1432             unlockChildList();
 1433             return iface;
 1434          }
 1435       }
 1436    unlockChildList();
 1437    return shared_ptr<Interface>();
 1438 }
 1439 
 1440 /**
 1441  * Find interface by physical location
 1442  * Returns pointer to interface object or nullptr if appropriate interface couldn't be found
 1443  */
 1444 shared_ptr<Interface> Node::findInterfaceByLocation(const InterfacePhysicalLocation& location) const
 1445 {
 1446    readLockChildList();
 1447    for(int i = 0; i < getChildList().size(); i++)
 1448       if (getChildList().get(i)->getObjectClass() == OBJECT_INTERFACE)
 1449       {
 1450          auto iface = static_pointer_cast<Interface>(getChildList().getShared(i));
 1451          if (iface->isPhysicalPort() && iface->getPhysicalLocation().equals(location))
 1452          {
 1453             unlockChildList();
 1454             return iface;
 1455          }
 1456       }
 1457    unlockChildList();
 1458    return shared_ptr<Interface>();
 1459 }
 1460 
 1461 /**
 1462  * Find interface by MAC address
 1463  * Returns pointer to interface object or nullptr if appropriate interface couldn't be found
 1464  */
 1465 shared_ptr<Interface> Node::findInterfaceByMAC(const MacAddress& macAddr) const
 1466 {
 1467    shared_ptr<Interface> iface;
 1468    readLockChildList();
 1469    for(int i = 0; i < getChildList().size(); i++)
 1470    {
 1471       NetObj *curr = getChildList().get(i);
 1472       if ((curr->getObjectClass() == OBJECT_INTERFACE) &&
 1473           static_cast<Interface*>(curr)->getMacAddr().equals(macAddr))
 1474       {
 1475          iface = static_pointer_cast<Interface>(getChildList().getShared(i));
 1476          break;
 1477       }
 1478    }
 1479    unlockChildList();
 1480    return iface;
 1481 }
 1482 
 1483 /**
 1484  * Find interface by IP address
 1485  * Returns pointer to interface object or nullptr if appropriate interface couldn't be found
 1486  */
 1487 shared_ptr<Interface> Node::findInterfaceByIP(const InetAddress& addr) const
 1488 {
 1489    shared_ptr<Interface> iface;
 1490 
 1491    if (!addr.isValid())
 1492       return iface;
 1493 
 1494    readLockChildList();
 1495    for(int i = 0; i < getChildList().size(); i++)
 1496    {
 1497       NetObj *curr = getChildList().get(i);
 1498       if ((curr->getObjectClass() == OBJECT_INTERFACE) &&
 1499           static_cast<Interface*>(curr)->getIpAddressList()->hasAddress(addr))
 1500       {
 1501          iface = static_pointer_cast<Interface>(getChildList().getShared(i));
 1502          break;
 1503       }
 1504    }
 1505    unlockChildList();
 1506    return iface;
 1507 }
 1508 
 1509 /**
 1510  * Find interface by IP subnet
 1511  * Returns pointer to interface object or nullptr if appropriate interface couldn't be found
 1512  */
 1513 shared_ptr<Interface> Node::findInterfaceBySubnet(const InetAddress& subnet) const
 1514 {
 1515    shared_ptr<Interface> iface;
 1516    readLockChildList();
 1517    for(int i = 0; i < getChildList().size(); i++)
 1518    {
 1519       NetObj *curr = getChildList().get(i);
 1520       if (curr->getObjectClass() == OBJECT_INTERFACE)
 1521       {
 1522          const InetAddressList *addrList = static_cast<Interface*>(curr)->getIpAddressList();
 1523          for(int j = 0; j < addrList->size(); j++)
 1524          {
 1525             if (subnet.contain(addrList->get(j)))
 1526             {
 1527                iface = static_pointer_cast<Interface>(getChildList().getShared(i));
 1528                goto stop_search;
 1529             }
 1530          }
 1531       }
 1532    }
 1533 stop_search:
 1534    unlockChildList();
 1535    return iface;
 1536 }
 1537 
 1538 /**
 1539  * Find interface by bridge port number
 1540  */
 1541 shared_ptr<Interface> Node::findBridgePort(UINT32 bridgePortNumber) const
 1542 {
 1543    shared_ptr<Interface> iface;
 1544    readLockChildList();
 1545    for(int i = 0; i < getChildList().size(); i++)
 1546    {
 1547       NetObj *curr = getChildList().get(i);
 1548       if ((curr->getObjectClass() == OBJECT_INTERFACE) && (static_cast<Interface*>(curr)->getBridgePortNumber() == bridgePortNumber))
 1549       {
 1550          iface = static_pointer_cast<Interface>(getChildList().getShared(i));
 1551          break;
 1552       }
 1553    }
 1554    unlockChildList();
 1555    return iface;
 1556 }
 1557 
 1558 /**
 1559  * Find connection point for node
 1560  */
 1561 shared_ptr<NetObj> Node::findConnectionPoint(UINT32 *localIfId, BYTE *localMacAddr, int *type)
 1562 {
 1563    shared_ptr<NetObj> cp;
 1564    readLockChildList();
 1565    for(int i = 0; i < getChildList().size(); i++)
 1566    {
 1567       if (getChildList().get(i)->getObjectClass() == OBJECT_INTERFACE)
 1568       {
 1569          auto iface = static_cast<Interface*>(getChildList().get(i));
 1570          cp = FindInterfaceConnectionPoint(iface->getMacAddr(), type);
 1571          if (cp != nullptr)
 1572          {
 1573             *localIfId = iface->getId();
 1574             memcpy(localMacAddr, iface->getMacAddr().value(), MAC_ADDR_LENGTH);
 1575             break;
 1576          }
 1577       }
 1578    }
 1579    unlockChildList();
 1580    return cp;
 1581 }
 1582 
 1583 /**
 1584  * Find attached access point by MAC address
 1585  */
 1586 shared_ptr<AccessPoint> Node::findAccessPointByMAC(const MacAddress& macAddr) const
 1587 {
 1588    shared_ptr<AccessPoint> ap;
 1589    readLockChildList();
 1590    for(int i = 0; i < getChildList().size(); i++)
 1591    {
 1592       NetObj *curr = getChildList().get(i);
 1593       if ((curr->getObjectClass() == OBJECT_ACCESSPOINT) &&
 1594           static_cast<AccessPoint*>(curr)->getMacAddr().equals(macAddr))
 1595       {
 1596          ap = static_pointer_cast<AccessPoint>(getChildList().getShared(i));
 1597          break;
 1598       }
 1599    }
 1600    unlockChildList();
 1601    return ap;
 1602 }
 1603 
 1604 /**
 1605  * Find access point by radio ID (radio interface index)
 1606  */
 1607 shared_ptr<AccessPoint> Node::findAccessPointByRadioId(int rfIndex) const
 1608 {
 1609    shared_ptr<AccessPoint> ap;
 1610    readLockChildList();
 1611    for(int i = 0; i < getChildList().size(); i++)
 1612    {
 1613       NetObj *curr = getChildList().get(i);
 1614       if ((curr->getObjectClass() == OBJECT_ACCESSPOINT) &&
 1615           static_cast<AccessPoint*>(curr)->isMyRadio(rfIndex))
 1616       {
 1617          ap = static_pointer_cast<AccessPoint>(getChildList().getShared(i));
 1618          break;
 1619       }
 1620    }
 1621    unlockChildList();
 1622    return ap;
 1623 }
 1624 
 1625 /**
 1626  * Find attached access point by BSSID
 1627  */
 1628 shared_ptr<AccessPoint> Node::findAccessPointByBSSID(const BYTE *bssid) const
 1629 {
 1630    shared_ptr<AccessPoint> ap;
 1631    readLockChildList();
 1632    for(int i = 0; i < getChildList().size(); i++)
 1633    {
 1634       NetObj *curr = getChildList().get(i);
 1635       if ((curr->getObjectClass() == OBJECT_ACCESSPOINT) &&
 1636           (static_cast<AccessPoint*>(curr)->getMacAddr().equals(bssid) || static_cast<AccessPoint*>(curr)->isMyRadio(bssid)))
 1637       {
 1638          ap = static_pointer_cast<AccessPoint>(getChildList().getShared(i));
 1639          break;
 1640       }
 1641    }
 1642    unlockChildList();
 1643    return ap;
 1644 }
 1645 
 1646 /**
 1647  * Check if given IP address is one of node's interfaces
 1648  */
 1649 bool Node::isMyIP(const InetAddress& addr) const
 1650 {
 1651    readLockChildList();
 1652    for(int i = 0; i < getChildList().size(); i++)
 1653    {
 1654       NetObj *curr = getChildList().get(i);
 1655       if ((curr->getObjectClass() == OBJECT_INTERFACE) &&
 1656           static_cast<Interface*>(curr)->getIpAddressList()->hasAddress(addr))
 1657       {
 1658          unlockChildList();
 1659          return true;
 1660       }
 1661    }
 1662    unlockChildList();
 1663    return false;
 1664 }
 1665 
 1666 /**
 1667  * Create interface object. Can return nullptr if interface creation hook
 1668  * blocks interface creation.
 1669  */
 1670 shared_ptr<Interface> Node::createInterfaceObject(InterfaceInfo *info, bool manuallyCreated, bool fakeInterface, bool syntheticMask)
 1671 {
 1672    shared_ptr<Interface> iface;
 1673    if (info->name[0] != 0)
 1674    {
 1675       iface = MakeSharedNObject<Interface>(info->name, (info->description[0] != 0) ? info->description : info->name,
 1676                info->index, info->ipAddrList, info->type, m_zoneUIN);
 1677    }
 1678    else
 1679    {
 1680       iface = MakeSharedNObject<Interface>(info->ipAddrList, m_zoneUIN, syntheticMask);
 1681    }
 1682    iface->setAlias(info->alias);
 1683    iface->setMacAddr(MacAddress(info->macAddr, MAC_ADDR_LENGTH), false);
 1684    iface->setBridgePortNumber(info->bridgePort);
 1685    iface->setPhysicalLocation(info->location);
 1686    iface->setPhysicalPortFlag(info->isPhysicalPort);
 1687    iface->setManualCreationFlag(manuallyCreated);
 1688    iface->setSystemFlag(info->isSystem);
 1689    iface->setMTU(info->mtu);
 1690    iface->setSpeed(info->speed);
 1691    iface->setIfTableSuffix(info->ifTableSuffixLength, info->ifTableSuffix);
 1692 
 1693    // Expand interface name
 1694    TCHAR expandedName[MAX_OBJECT_NAME];
 1695    iface->expandName(iface->getName(), expandedName);
 1696    iface->setName(expandedName);
 1697 
 1698    int defaultExpectedState = ConfigReadInt(_T("Objects.Interfaces.DefaultExpectedState"), IF_DEFAULT_EXPECTED_STATE_UP);
 1699    switch(defaultExpectedState)
 1700    {
 1701       case IF_DEFAULT_EXPECTED_STATE_AUTO:
 1702          iface->setExpectedState(fakeInterface ? IF_EXPECTED_STATE_UP : IF_EXPECTED_STATE_AUTO);
 1703          break;
 1704       case IF_DEFAULT_EXPECTED_STATE_IGNORE:
 1705          iface->setExpectedState(IF_EXPECTED_STATE_IGNORE);
 1706          break;
 1707       default:
 1708          iface->setExpectedState(IF_EXPECTED_STATE_UP);
 1709          break;
 1710    }
 1711 
 1712    // Call hook script if interface is automatically created
 1713    if (!manuallyCreated)
 1714    {
 1715       ScriptVMHandle vm = CreateServerScriptVM(_T("Hook::CreateInterface"), self());
 1716       if (!vm.isValid())
 1717       {
 1718          if (vm.failureReason() == ScriptVMFailureReason::SCRIPT_IS_EMPTY)
 1719             nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 7, _T("Node::createInterfaceObject(%s [%u]): hook script \"Hook::CreateInterface\" is empty"), m_name, m_id);
 1720          else
 1721             nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 7, _T("Node::createInterfaceObject(%s [%u]): hook script \"Hook::CreateInterface\" not found"), m_name, m_id);
 1722          return iface;
 1723       }
 1724 
 1725       bool pass = true;
 1726       NXSL_Value *argv = iface->createNXSLObject(vm);
 1727       if (vm->run(1, &argv))
 1728       {
 1729          pass = vm->getResult()->getValueAsBoolean();
 1730       }
 1731       else
 1732       {
 1733          nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 4, _T("Node::createInterfaceObject(%s [%u]): hook script execution error: %s"), m_name, m_id, vm->getErrorText());
 1734          PostSystemEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", _T("Hook::CreateInterface"), vm->getErrorText(), 0);
 1735       }
 1736       vm.destroy();
 1737       nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 6, _T("Node::createInterfaceObject(%s [%u]): interface \"%s\" (ifIndex=%d) %s by filter"),
 1738                 m_name, m_id, iface->getName(), info->index, pass ? _T("accepted") : _T("rejected"));
 1739       if (!pass)
 1740       {
 1741          sendPollerMsg(POLLER_WARNING _T("   Creation of interface object \"%s\" blocked by filter\r\n"), iface->getName());
 1742          iface.reset();
 1743       }
 1744    }
 1745    return iface;
 1746 }
 1747 
 1748 /**
 1749  * Create new interface - convenience wrapper
 1750  */
 1751 shared_ptr<Interface> Node::createNewInterface(const InetAddress& ipAddr, const MacAddress& macAddr, bool fakeInterface)
 1752 {
 1753    InterfaceInfo info(1);
 1754    info.ipAddrList.add(ipAddr);
 1755    if (macAddr.isValid() && (macAddr.length() == MAC_ADDR_LENGTH))
 1756       memcpy(info.macAddr, macAddr.value(), MAC_ADDR_LENGTH);
 1757    return createNewInterface(&info, false, fakeInterface);
 1758 }
 1759 
 1760 /**
 1761  * Create new interface
 1762  */
 1763 shared_ptr<Interface> Node::createNewInterface(InterfaceInfo *info, bool manuallyCreated, bool fakeInterface)
 1764 {
 1765    bool bSyntheticMask = false;
 1766    TCHAR buffer[64];
 1767 
 1768    nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 5, _T("Node::createNewInterface(\"%s\", %d, %d, bp=%d, chassis=%u, module=%u, pic=%u, port=%u) called for node %s [%d]"),
 1769          info->name, info->index, info->type, info->bridgePort, info->location.chassis, info->location.module,
 1770          info->location.pic, info->location.port, m_name, m_id);
 1771    for(int i = 0; i < info->ipAddrList.size(); i++)
 1772    {
 1773       const InetAddress& addr = info->ipAddrList.get(i);
 1774       nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 5, _T("Node::createNewInterface(%s): IP address %s/%d"), info->name, addr.toString(buffer), addr.getMaskBits());
 1775    }
 1776 
 1777    SharedObjectArray<Subnet> bindList;
 1778    InetAddressList createList;
 1779 
 1780    // Find subnet(s) to place this node to
 1781    if (info->type != IFTYPE_SOFTWARE_LOOPBACK)
 1782    {
 1783       shared_ptr<Cluster> pCluster = getMyCluster();
 1784       for(int i = 0; i < info->ipAddrList.size(); i++)
 1785       {
 1786          InetAddress addr = info->ipAddrList.get(i);
 1787          bool addToSubnet = addr.isValidUnicast() && ((pCluster == nullptr) || !pCluster->isSyncAddr(addr));
 1788          nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 5, _T("Node::createNewInterface: node=%s [%d] ip=%s/%d cluster=%s [%d] add=%s"),
 1789                    m_name, m_id, addr.toString(buffer), addr.getMaskBits(),
 1790                    (pCluster != nullptr) ? pCluster->getName() : _T("(null)"),
 1791                    (pCluster != nullptr) ? pCluster->getId() : 0, addToSubnet ? _T("yes") : _T("no"));
 1792          if (addToSubnet)
 1793          {
 1794             shared_ptr<Subnet> pSubnet = FindSubnetForNode(m_zoneUIN, addr);
 1795             if (pSubnet == nullptr)
 1796             {
 1797                // Check if netmask is 0 (detect), and if yes, create
 1798                // new subnet with default mask
 1799                if (addr.getMaskBits() == 0)
 1800                {
 1801                   bSyntheticMask = true;
 1802                   addr.setMaskBits((addr.getFamily() == AF_INET) ? ConfigReadInt(_T("DefaultSubnetMaskIPv4"), 24) : ConfigReadInt(_T("DefaultSubnetMaskIPv6"), 64));
 1803                   info->ipAddrList.replace(addr);
 1804                }
 1805 
 1806                // Create new subnet object
 1807                if (addr.getHostBits() > 0)
 1808                {
 1809                   if (AdjustSubnetBaseAddress(addr, m_zoneUIN))
 1810                   {
 1811                      info->ipAddrList.replace(addr);  // mask could be adjusted
 1812                      createList.add(addr);
 1813                   }
 1814                }
 1815             }
 1816             else
 1817             {
 1818                // Set correct netmask if we were asked for it
 1819                if (addr.getMaskBits() == 0)
 1820                {
 1821                   bSyntheticMask = pSubnet->isSyntheticMask();
 1822                   addr.setMaskBits(pSubnet->getIpAddress().getMaskBits());
 1823                   info->ipAddrList.replace(addr);
 1824                }
 1825             }
 1826             if (pSubnet != nullptr)
 1827             {
 1828                bindList.add(pSubnet);
 1829             }
 1830          }  // addToSubnet
 1831       } // loop by address list
 1832    }
 1833 
 1834    // Insert to objects' list and generate event
 1835    shared_ptr<Interface>  iface = createInterfaceObject(info, manuallyCreated, fakeInterface, bSyntheticMask);
 1836    if (iface == nullptr)
 1837       return iface;
 1838 
 1839    NetObjInsert(iface, true, false);
 1840    addInterface(iface);
 1841    if (!m_isHidden)
 1842       iface->unhide();
 1843    if (!iface->isSystem())
 1844    {
 1845       const InetAddress& addr = iface->getFirstIpAddress();
 1846       PostSystemEvent(EVENT_INTERFACE_ADDED, m_id, "dsAdd", iface->getId(),
 1847                 iface->getName(), &addr, addr.getMaskBits(), iface->getIfIndex());
 1848    }
 1849 
 1850    if (!iface->isExcludedFromTopology())
 1851    {
 1852       for(int i = 0; i < bindList.size(); i++)
 1853          bindList.get(i)->addNode(self());
 1854 
 1855       for(int i = 0; i < createList.size(); i++)
 1856       {
 1857          InetAddress addr = InetAddress(createList.get(i));
 1858          createSubnet(addr, bSyntheticMask);
 1859       }
 1860    }
 1861    return iface;
 1862 }
 1863 
 1864 /**
 1865  * Delete interface from node
 1866  */
 1867 void Node::deleteInterface(Interface *iface)
 1868 {
 1869    nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 5, _T("Node::deleteInterface(node=%s [%d], interface=%s [%d])"), m_name, m_id, iface->getName(), iface->getId());
 1870 
 1871    // Check if we should unlink node from interface's subnet
 1872    if (!iface->isExcludedFromTopology())
 1873    {
 1874       const ObjectArray<InetAddress>& list = iface->getIpAddressList()->getList();
 1875       for(int i = 0; i < list.size(); i++)
 1876       {
 1877          bool doUnlink = true;
 1878          const InetAddress *addr = list.get(i);
 1879 
 1880          readLockChildList();
 1881          for(int j = 0; j < getChildList().size(); j++)
 1882          {
 1883             NetObj *curr = getChildList().get(j);
 1884             if ((curr->getObjectClass() == OBJECT_INTERFACE) && (curr != iface) &&
 1885                 static_cast<Interface*>(curr)->getIpAddressList()->findSameSubnetAddress(*addr).isValid())
 1886             {
 1887                doUnlink = false;
 1888                break;
 1889             }
 1890          }
 1891          unlockChildList();
 1892 
 1893          if (doUnlink)
 1894          {
 1895             // Last interface in subnet, should unlink node
 1896             shared_ptr<Subnet> subnet = FindSubnetByIP(m_zoneUIN, addr->getSubnetAddress());
 1897             if (subnet != nullptr)
 1898             {
 1899                deleteParent(*subnet);
 1900                subnet->deleteChild(*this);
 1901             }
 1902             nxlog_debug_tag(DEBUG_TAG_NODE_INTERFACES, 5, _T("Node::deleteInterface(node=%s [%d], interface=%s [%d]): unlinked from subnet %s [%d]"),
 1903                       m_name, m_id, iface->getName(), iface->getId(),
 1904                       (subnet != nullptr) ? subnet->getName() : _T("(null)"),
 1905                       (subnet != nullptr) ? subnet->getId() : 0);
 1906          }
 1907       }
 1908    }
 1909    iface->deleteObject();
 1910 }
 1911 
 1912 /**
 1913  * Set object's management status
 1914  */
 1915 bool Node::setMgmtStatus(bool isManaged)
 1916 {
 1917    if (!super::setMgmtStatus(isManaged))
 1918       return false;
 1919 
 1920    if (IsZoningEnabled())
 1921    {
 1922       shared_ptr<Zone> zone = FindZoneByProxyId(m_id);
 1923       if (zone != nullptr)
 1924       {
 1925          zone->updateProxyStatus(self(), true);
 1926       }
 1927    }
 1928 
 1929    // call to onDataCollectionChange will force data collection
 1930    // configuration upload to this node or relevant proxy nodes
 1931    onDataCollectionChange();
 1932 
 1933    CALL_ALL_MODULES(pfOnNodeMgmtStatusChange, (self(), isManaged));
 1934    return true;
 1935 }
 1936 
 1937 /**
 1938  * Calculate node status based on child objects status
 1939  */
 1940 void Node::calculateCompoundStatus(BOOL bForcedRecalc)
 1941 {
 1942    int iOldStatus = m_status;
 1943    static UINT32 dwEventCodes[] = { EVENT_NODE_NORMAL, EVENT_NODE_WARNING,
 1944       EVENT_NODE_MINOR, EVENT_NODE_MAJOR, EVENT_NODE_CRITICAL,
 1945       EVENT_NODE_UNKNOWN, EVENT_NODE_UNMANAGED };
 1946 
 1947    super::calculateCompoundStatus(bForcedRecalc);
 1948    if (m_status != iOldStatus)
 1949       PostSystemEvent(dwEventCodes[m_status], m_id, "d", iOldStatus);
 1950 }
 1951 
 1952 /**
 1953  * Delete node in background
 1954  */
 1955 static void BackgroundDeleteNode(const shared_ptr<Node>& node)
 1956 {
 1957    node->deleteObject();
 1958 }
 1959 
 1960 /**
 1961  * Perform status poll on node
 1962  */
 1963 void Node::statusPoll(PollerInfo *poller, ClientSession *pSession, UINT32 rqId)
 1964 {
 1965    lockProperties();
 1966    if (m_isDeleteInitiated || IsShutdownInProgress())
 1967    {
 1968       m_statusPollState.complete(0);
 1969       unlockProperties();
 1970       return;
 1971    }
 1972    // Poller can be called directly - in that case poll flag will not be set
 1973    unlockProperties();
 1974 
 1975    checkTrapShouldBeProcessed();
 1976 
 1977    ObjectQueue<Event> *eventQueue = new ObjectQueue<Event>(64, Ownership::True);     // Delayed event queue
 1978    poller->setStatus(_T("wait for lock"));
 1979    pollerLock(status);
 1980 
 1981    POLL_CANCELLATION_CHECKPOINT_EX(delete eventQueue);
 1982 
 1983    uint32_t oldCapabilities = m_capabilities;
 1984    uint32_t oldState = m_state;
 1985 
 1986    poller->setStatus(_T("preparing"));
 1987    m_pollRequestor = pSession;
 1988    m_pollRequestId = rqId;
 1989 
 1990    if (m_status == STATUS_UNMANAGED)
 1991    {
 1992       sendPollerMsg(POLLER_WARNING _T("Node %s is unmanaged, status poll aborted\r\n"), m_name);
 1993       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node %s [%u] is unmanaged, status poll aborted"), m_name, m_id);
 1994       delete eventQueue;
 1995       pollerUnlock();
 1996       return;
 1997    }
 1998 
 1999    sendPollerMsg(_T("Starting status poll for node %s\r\n"), m_name);
 2000    nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Starting status poll for node %s (ID: %d)"), m_name, m_id);
 2001 
 2002    // Read capability expiration time and current time
 2003    time_t capabilityExpirationTime = static_cast<time_t>(ConfigReadULong(_T("Objects.Nodes.CapabilityExpirationTime"), 604800));
 2004    time_t capabilityExpirationGracePeriod = static_cast<time_t>(ConfigReadULong(_T("Objects.Nodes.CapabilityExpirationTime"), 3600));
 2005    time_t now = time(nullptr);
 2006 
 2007    bool agentConnected = false;
 2008    bool resyncDataCollectionConfiguration = false;
 2009    bool forceResolveHostName = false;
 2010 
 2011    int retryCount = 5;
 2012 
 2013 restart_status_poll:
 2014    if (g_primaryIpUpdateMode == PrimaryIPUpdateMode::ALWAYS)
 2015    {
 2016       if (m_pollsAfterIpUpdate >= g_pollsBetweenPrimaryIpUpdate)
 2017       {
 2018          poller->setStatus(_T("updating primary IP"));
 2019          updatePrimaryIpAddr();
 2020          m_pollsAfterIpUpdate = 0;
 2021       }
 2022       else
 2023       {
 2024          m_pollsAfterIpUpdate++;
 2025       }
 2026    }
 2027    else if (forceResolveHostName)
 2028    {
 2029       poller->setStatus(_T("updating primary IP"));
 2030       updatePrimaryIpAddr();
 2031       forceResolveHostName = false;
 2032    }
 2033 
 2034    uint32_t requiredPolls = (m_requiredPollCount > 0) ? m_requiredPollCount : g_requiredPolls;
 2035 
 2036    // Check SNMP agent connectivity
 2037    if ((m_capabilities & NC_IS_SNMP) && (!(m_flags & NF_DISABLE_SNMP)) && m_ipAddress.isValidUnicast())
 2038    {
 2039       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): check SNMP"), m_name);
 2040       SNMP_Transport *pTransport = createSnmpTransport();
 2041       if (pTransport != nullptr)
 2042       {
 2043          poller->setStatus(_T("check SNMP"));
 2044          sendPollerMsg(_T("Checking SNMP agent connectivity\r\n"));
 2045          SharedString testOid = getCustomAttribute(_T("snmp.testoid"));
 2046          if (testOid.isEmpty())
 2047          {
 2048             testOid = _T(".1.3.6.1.2.1.1.2.0");
 2049          }
 2050 
 2051          TCHAR buffer[256];
 2052          uint32_t snmpErr = SnmpGet(m_snmpVersion, pTransport, testOid, nullptr, 0, buffer, sizeof(buffer), 0);
 2053          if ((snmpErr == SNMP_ERR_SUCCESS) || (snmpErr == SNMP_ERR_NO_OBJECT))
 2054          {
 2055             if (m_state & NSF_SNMP_UNREACHABLE)
 2056             {
 2057                m_pollCountSNMP++;
 2058                if (m_pollCountSNMP >= requiredPolls)
 2059                {
 2060                   m_state &= ~NSF_SNMP_UNREACHABLE;
 2061                   PostSystemEventEx(eventQueue, EVENT_SNMP_OK, m_id, nullptr);
 2062                   sendPollerMsg(POLLER_INFO _T("Connectivity with SNMP agent restored\r\n"));
 2063                   nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): Connectivity with SNMP agent restored"), m_name);
 2064                   m_pollCountSNMP = 0;
 2065                }
 2066 
 2067             }
 2068             else
 2069             {
 2070                m_pollCountSNMP = 0;
 2071             }
 2072 
 2073             // Update authoritative engine data for SNMPv3
 2074             if ((pTransport->getSnmpVersion() == SNMP_VERSION_3) && (pTransport->getAuthoritativeEngine() != nullptr))
 2075             {
 2076                lockProperties();
 2077                m_snmpSecurity->setAuthoritativeEngine(*pTransport->getAuthoritativeEngine());
 2078                m_snmpSecurity->recalculateKeys();
 2079                unlockProperties();
 2080             }
 2081          }
 2082          else if ((snmpErr == SNMP_ERR_ENGINE_ID) && (m_snmpVersion == SNMP_VERSION_3) && (retryCount > 0))
 2083          {
 2084             // Reset authoritative engine data
 2085             lockProperties();
 2086             m_snmpSecurity->setAuthoritativeEngine(SNMP_Engine());
 2087             unlockProperties();
 2088             delete pTransport;
 2089             retryCount--;
 2090             goto restart_status_poll;
 2091          }
 2092          else
 2093          {
 2094             if ((snmpErr == SNMP_ERR_COMM) && pTransport->isProxyTransport() && (retryCount > 0))
 2095             {
 2096                nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): got communication error on proxy transport, checking connection to proxy"), m_name);
 2097                uint32_t proxyNodeId = getEffectiveSnmpProxy();
 2098                shared_ptr<NetObj> proxyNode = FindObjectById(proxyNodeId, OBJECT_NODE);
 2099                if (proxyNode != nullptr)
 2100                {
 2101                   shared_ptr<AgentConnectionEx> pconn = static_cast<Node*>(proxyNode.get())->acquireProxyConnection(SNMP_PROXY, true);
 2102                   if (pconn != nullptr)
 2103                   {
 2104                      nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): proxy connection for SNMP is valid"), m_name);
 2105                      retryCount--;
 2106                      delete pTransport;
 2107                      goto restart_status_poll;
 2108                   }
 2109                   else
 2110                   {
 2111                      nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): cannot acquire proxy connection for SNMP"), m_name);
 2112                   }
 2113                }
 2114                else
 2115                {
 2116                   nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): cannot find SNMP proxy node object %u"), m_name, proxyNodeId);
 2117                }
 2118             }
 2119 
 2120             sendPollerMsg(POLLER_ERROR _T("SNMP agent unreachable\r\n"));
 2121             nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): SNMP agent unreachable (poll count %d of %d)"), m_name, m_pollCountSNMP, requiredPolls);
 2122             if (m_state & NSF_SNMP_UNREACHABLE)
 2123             {
 2124                if ((now > m_failTimeSNMP + capabilityExpirationTime) && (!(m_state & DCSF_UNREACHABLE)) && (now > m_recoveryTime + capabilityExpirationGracePeriod))
 2125                {
 2126                   m_capabilities &= ~NC_IS_SNMP;
 2127                   m_state &= ~NSF_SNMP_UNREACHABLE;
 2128                   MemFreeAndNull(m_snmpObjectId);
 2129                   sendPollerMsg(POLLER_WARNING _T("Attribute isSNMP set to FALSE\r\n"));
 2130                   nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 4, _T("StatusPoll(%s): Attribute isSNMP set to FALSE"), m_name);
 2131                }
 2132             }
 2133             else
 2134             {
 2135                m_pollCountSNMP++;
 2136                if (m_pollCountSNMP >= requiredPolls)
 2137                {
 2138                   if (g_primaryIpUpdateMode == PrimaryIPUpdateMode::ON_FAILURE)
 2139                   {
 2140                      InetAddress addr = ResolveHostName(m_zoneUIN, m_primaryHostName);
 2141                      if (!m_ipAddress.equals(addr))
 2142                      {
 2143                         nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): primary IP address changed, restarting poll"), m_name);
 2144                         forceResolveHostName = true;
 2145                         goto restart_status_poll;
 2146                      }
 2147                   }
 2148 
 2149                   m_state |= NSF_SNMP_UNREACHABLE;
 2150                   PostSystemEventEx(eventQueue, EVENT_SNMP_FAIL, m_id, nullptr);
 2151                   m_failTimeSNMP = now;
 2152                   m_pollCountSNMP = 0;
 2153                }
 2154             }
 2155          }
 2156          delete pTransport;
 2157       }
 2158       else
 2159       {
 2160          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): cannot create SNMP transport"), m_name);
 2161       }
 2162       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): SNMP check finished"), m_name);
 2163    }
 2164 
 2165    POLL_CANCELLATION_CHECKPOINT_EX(delete eventQueue);
 2166 
 2167    // Check native agent connectivity
 2168    if ((m_capabilities & NC_IS_NATIVE_AGENT) && (!(m_flags & NF_DISABLE_NXCP)))
 2169    {
 2170       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): checking agent"), m_name);
 2171       poller->setStatus(_T("check agent"));
 2172       sendPollerMsg(_T("Checking NetXMS agent connectivity\r\n"));
 2173 
 2174       uint32_t error, socketError;
 2175       bool newConnection;
 2176       agentLock();
 2177       if (connectToAgent(&error, &socketError, &newConnection, true))
 2178       {
 2179          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 7, _T("StatusPoll(%s): connected to agent"), m_name);
 2180          if (m_state & NSF_AGENT_UNREACHABLE)
 2181          {
 2182             m_pollCountAgent++;
 2183             if (m_pollCountAgent >= requiredPolls)
 2184             {
 2185                m_state &= ~NSF_AGENT_UNREACHABLE;
 2186                PostSystemEventEx(eventQueue, EVENT_AGENT_OK, m_id, nullptr);
 2187                sendPollerMsg(POLLER_INFO _T("Connectivity with NetXMS agent restored\r\n"));
 2188                m_pollCountAgent = 0;
 2189 
 2190                // Reset connection time of all proxy connections so they can be re-established immediately
 2191                for(int i = 0; i < MAX_PROXY_TYPE; i++)
 2192                {
 2193                   m_proxyConnections[i].lock();
 2194                   m_proxyConnections[i].setLastConnectTime(0);
 2195                   m_proxyConnections[i].unlock();
 2196                }
 2197             }
 2198          }
 2199          else
 2200          {
 2201             m_pollCountAgent = 0;
 2202          }
 2203          agentConnected = true;
 2204       }
 2205       else
 2206       {
 2207          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): agent unreachable, error=%d, socketError=%d. Poll count %d of %d"),
 2208                   m_name, (int)error, (int)socketError, m_pollCountAgent, requiredPolls);
 2209          sendPollerMsg(POLLER_ERROR _T("Cannot connect to NetXMS agent (%s)\r\n"), AgentErrorCodeToText(error));
 2210          if (m_state & NSF_AGENT_UNREACHABLE)
 2211          {
 2212             if ((now > m_failTimeAgent + capabilityExpirationTime) && !(m_state & DCSF_UNREACHABLE) && (now > m_recoveryTime + capabilityExpirationGracePeriod))
 2213             {
 2214                m_capabilities &= ~NC_IS_NATIVE_AGENT;
 2215                m_state &= ~NSF_AGENT_UNREACHABLE;
 2216                m_platformName[0] = 0;
 2217                m_agentVersion[0] = 0;
 2218                sendPollerMsg(POLLER_WARNING _T("Attribute isNetXMSAgent set to FALSE\r\n"));
 2219             }
 2220          }
 2221          else
 2222          {
 2223             m_pollCountAgent++;
 2224             if (m_pollCountAgent >= requiredPolls)
 2225             {
 2226                if (g_primaryIpUpdateMode == PrimaryIPUpdateMode::ON_FAILURE)
 2227                {
 2228                   InetAddress addr = ResolveHostName(m_zoneUIN, m_primaryHostName);
 2229                   if (!m_ipAddress.equals(addr))
 2230                   {
 2231                      nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): primary IP address changed, restarting poll"), m_name);
 2232                      forceResolveHostName = true;
 2233                      agentUnlock();
 2234                      goto restart_status_poll;
 2235                   }
 2236                }
 2237 
 2238                m_state |= NSF_AGENT_UNREACHABLE;
 2239                PostSystemEventEx(eventQueue, EVENT_AGENT_FAIL, m_id, nullptr);
 2240                m_failTimeAgent = now;
 2241                m_pollCountAgent = 0;
 2242             }
 2243          }
 2244       }
 2245       agentUnlock();
 2246       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 7, _T("StatusPoll(%s): agent check finished"), m_name);
 2247 
 2248       // If file update connection is active, send NOP command to prevent disconnection by idle timeout
 2249       lockProperties();
 2250       shared_ptr<AgentConnection> fileUpdateConnection = m_fileUpdateConnection;
 2251       unlockProperties();
 2252       if (fileUpdateConnection != nullptr)
 2253       {
 2254          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): sending keepalive command on file monitoring connection"), m_name);
 2255          fileUpdateConnection->nop();
 2256       }
 2257    }
 2258 
 2259    POLL_CANCELLATION_CHECKPOINT_EX(delete eventQueue);
 2260 
 2261    // Check EtherNet/IP connectivity
 2262    if ((m_capabilities & NC_IS_ETHERNET_IP) && (!(m_flags & NF_DISABLE_ETHERNET_IP)))
 2263    {
 2264       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): checking EtherNet/IP"), m_name);
 2265       poller->setStatus(_T("check EtherNet/IP"));
 2266       sendPollerMsg(_T("Checking EtherNet/IP connectivity\r\n"));
 2267 
 2268       CIP_Identity *identity = nullptr;
 2269       EIP_Status status;
 2270 
 2271       uint32_t eipProxy = getEffectiveEtherNetIPProxy();
 2272       if (eipProxy != 0)
 2273       {
 2274          // TODO: implement proxy request
 2275          status = EIP_Status::callFailure(EIP_CALL_COMM_ERROR);
 2276       }
 2277       else
 2278       {
 2279          identity = EIP_ListIdentity(m_ipAddress, m_eipPort, 5000, &status);
 2280       }
 2281 
 2282       if (identity != nullptr)
 2283       {
 2284          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 7, _T("StatusPoll(%s): connected to device via EtherNet/IP"), m_name);
 2285          if (m_state & NSF_ETHERNET_IP_UNREACHABLE)
 2286          {
 2287             m_pollCountEtherNetIP++;
 2288             if (m_pollCountEtherNetIP >= requiredPolls)
 2289             {
 2290                m_state &= ~NSF_ETHERNET_IP_UNREACHABLE;
 2291                PostSystemEventEx(eventQueue, EVENT_ETHERNET_IP_OK, m_id, nullptr);
 2292                sendPollerMsg(POLLER_INFO _T("EtherNet/IP connectivity restored\r\n"));
 2293                m_pollCountEtherNetIP = 0;
 2294             }
 2295          }
 2296          else
 2297          {
 2298             m_pollCountAgent = 0;
 2299          }
 2300 
 2301          m_cipState = identity->state;
 2302          m_cipStatus = identity->status;
 2303 
 2304          MemFree(identity);
 2305       }
 2306       else
 2307       {
 2308          String reason = status.failureReason();
 2309          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): EtherNet/IP unreachable (%s), poll count %d of %d"),
 2310                   m_name, reason.cstr(), m_pollCountEtherNetIP, requiredPolls);
 2311          sendPollerMsg(POLLER_ERROR _T("Cannot connect to device via EtherNet/IP (%s)\r\n"), reason.cstr());
 2312          if (m_state & NSF_ETHERNET_IP_UNREACHABLE)
 2313          {
 2314             if ((now > m_failTimeEtherNetIP + capabilityExpirationTime) && !(m_state & DCSF_UNREACHABLE) && (now > m_recoveryTime + capabilityExpirationGracePeriod))
 2315             {
 2316                m_capabilities &= ~NC_IS_ETHERNET_IP;
 2317                m_state &= ~NSF_ETHERNET_IP_UNREACHABLE;
 2318                sendPollerMsg(POLLER_WARNING _T("Attribute isEtherNetIP set to FALSE\r\n"));
 2319             }
 2320          }
 2321          else
 2322          {
 2323             m_pollCountEtherNetIP++;
 2324             if (m_pollCountEtherNetIP >= requiredPolls)
 2325             {
 2326                m_state |= NSF_ETHERNET_IP_UNREACHABLE;
 2327                PostSystemEventEx(eventQueue, EVENT_ETHERNET_IP_UNREACHABLE, m_id, nullptr);
 2328                m_failTimeEtherNetIP = now;
 2329                m_pollCountEtherNetIP = 0;
 2330             }
 2331          }
 2332       }
 2333 
 2334       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 7, _T("StatusPoll(%s): EtherNet/IP check finished"), m_name);
 2335    }
 2336 
 2337    poller->setStatus(_T("prepare polling list"));
 2338 
 2339    POLL_CANCELLATION_CHECKPOINT_EX(delete eventQueue);
 2340 
 2341    // Find service poller node object
 2342    shared_ptr<Node> pollerNode;
 2343    lockProperties();
 2344    if (m_pollerNode != 0)
 2345    {
 2346       uint32_t id = m_pollerNode;
 2347       unlockProperties();
 2348       pollerNode = static_pointer_cast<Node>(FindObjectById(id, OBJECT_NODE));
 2349    }
 2350    else
 2351    {
 2352       unlockProperties();
 2353    }
 2354 
 2355    // If nothing found, use management server
 2356    if (pollerNode == nullptr)
 2357    {
 2358       pollerNode = static_pointer_cast<Node>(FindObjectById(g_dwMgmtNode, OBJECT_NODE));
 2359    }
 2360 
 2361    // Create polling list
 2362    SharedObjectArray<NetObj> pollList(32, 32);
 2363    readLockChildList();
 2364    for(int i = 0; i < getChildList().size(); i++)
 2365    {
 2366       shared_ptr<NetObj> curr = getChildList().getShared(i);
 2367       if (curr->getStatus() != STATUS_UNMANAGED)
 2368          pollList.add(curr);
 2369    }
 2370    unlockChildList();
 2371 
 2372    // Poll interfaces and services
 2373    poller->setStatus(_T("child poll"));
 2374    DbgPrintf(7, _T("StatusPoll(%s): starting child object poll"), m_name);
 2375    shared_ptr<Cluster> cluster = getMyCluster();
 2376    SNMP_Transport *snmp = createSnmpTransport();
 2377    for(int i = 0; i < pollList.size(); i++)
 2378    {
 2379       NetObj *curr = pollList.get(i);
 2380       switch(curr->getObjectClass())
 2381       {
 2382          case OBJECT_INTERFACE:
 2383             nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 7, _T("StatusPoll(%s): polling interface %d [%s]"), m_name, curr->getId(), curr->getName());
 2384             static_cast<Interface*>(curr)->statusPoll(pSession, rqId, eventQueue, cluster.get(), snmp, m_icmpProxy);
 2385             break;
 2386          case OBJECT_NETWORKSERVICE:
 2387             nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 7, _T("StatusPoll(%s): polling network service %d [%s]"), m_name, curr->getId(), curr->getName());
 2388             static_cast<NetworkService*>(curr)->statusPoll(pSession, rqId, pollerNode, eventQueue);
 2389             break;
 2390          case OBJECT_ACCESSPOINT:
 2391             if (snmp != nullptr)
 2392             {
 2393                nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 7, _T("StatusPoll(%s): polling access point %d [%s]"), m_name, curr->getId(), curr->getName());
 2394                static_cast<AccessPoint*>(curr)->statusPollFromController(pSession, rqId, eventQueue, this, snmp);
 2395             }
 2396             break;
 2397          default:
 2398             nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 7, _T("StatusPoll(%s): skipping object %d [%s] class %d"), m_name, curr->getId(), curr->getName(), curr->getObjectClass());
 2399             break;
 2400       }
 2401 
 2402       POLL_CANCELLATION_CHECKPOINT_EX({ delete eventQueue; delete snmp; });
 2403    }
 2404    delete snmp;
 2405    nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 7, _T("StatusPoll(%s): finished child object poll"), m_name);
 2406 
 2407    // Check if entire node is down
 2408    // This check is disabled for nodes without IP address
 2409    // The only exception is node without valid address connected via agent tunnel
 2410    if (m_ipAddress.isValidUnicast() || agentConnected)
 2411    {
 2412       bool allDown = true;
 2413       readLockChildList();
 2414       for(int i = 0; i < getChildList().size(); i++)
 2415       {
 2416          NetObj *curr = getChildList().get(i);
 2417          if ((curr->getObjectClass() == OBJECT_INTERFACE) &&
 2418              (((Interface *)curr)->getAdminState() != IF_ADMIN_STATE_DOWN) &&
 2419              (((Interface *)curr)->getConfirmedOperState() == IF_OPER_STATE_UP) &&
 2420              (curr->getStatus() != STATUS_UNMANAGED))
 2421          {
 2422             allDown = false;
 2423             break;
 2424          }
 2425       }
 2426       unlockChildList();
 2427       if (allDown && (m_capabilities & NC_IS_NATIVE_AGENT) && !(m_flags & NF_DISABLE_NXCP))
 2428       {
 2429          if (m_state & NSF_AGENT_UNREACHABLE)
 2430          {
 2431             // Use TCP ping to check if node actually unreachable if possible
 2432             if (m_ipAddress.isValidUnicast())
 2433             {
 2434                nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): using TCP ping on primary IP address"), m_name);
 2435                sendPollerMsg(_T("Checking primary IP address with TCP ping on agent's port\r\n"));
 2436                TcpPingResult r = TcpPing(m_ipAddress, m_agentPort, 1000);
 2437                if ((r == TCP_PING_SUCCESS) || (r == TCP_PING_REJECT))
 2438                {
 2439                   nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): agent is unreachable but IP address is likely reachable (TCP ping returns %d)"), m_name, r);
 2440                   sendPollerMsg(POLLER_INFO _T("   Primary IP address is responding to TCP ping\r\n"));
 2441                   allDown = false;
 2442                }
 2443                else
 2444                {
 2445                   sendPollerMsg(POLLER_ERROR _T("   Primary IP address is not responding to TCP ping\r\n"));
 2446                }
 2447             }
 2448          }
 2449          else
 2450          {
 2451             allDown = false;
 2452          }
 2453       }
 2454       if (allDown && (m_capabilities & NC_IS_SNMP) &&
 2455           !(m_flags & NF_DISABLE_SNMP) && !(m_state & NSF_SNMP_UNREACHABLE))
 2456       {
 2457          allDown = false;
 2458       }
 2459       if (allDown && (m_capabilities & NC_IS_ETHERNET_IP) && !(m_flags & NF_DISABLE_ETHERNET_IP))
 2460       {
 2461          if (m_state & NSF_ETHERNET_IP_UNREACHABLE)
 2462          {
 2463             // Use TCP ping to check if node actually unreachable if possible
 2464             if (m_ipAddress.isValidUnicast())
 2465             {
 2466                nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): using TCP ping on primary IP address"), m_name);
 2467                sendPollerMsg(_T("Checking primary IP address with TCP ping on EtherNet/IP port\r\n"));
 2468                TcpPingResult r = TcpPing(m_ipAddress, m_eipPort, 1000);
 2469                if ((r == TCP_PING_SUCCESS) || (r == TCP_PING_REJECT))
 2470                {
 2471                   nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): EtherNet/IP is unreachable but IP address is likely reachable (TCP ping returns %d)"), m_name, r);
 2472                   sendPollerMsg(POLLER_INFO _T("   Primary IP address is responding to TCP ping\r\n"));
 2473                   allDown = false;
 2474                }
 2475                else
 2476                {
 2477                   sendPollerMsg(POLLER_ERROR _T("   Primary IP address is not responding to TCP ping\r\n"));
 2478                }
 2479             }
 2480          }
 2481          else
 2482          {
 2483             allDown = false;
 2484          }
 2485       }
 2486       if (allDown && (m_flags & NF_PING_PRIMARY_IP) && m_ipAddress.isValidUnicast())
 2487       {
 2488          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): using ICMP ping on primary IP address"), m_name);
 2489          sendPollerMsg(_T("Checking primary IP address with ICMP ping\r\n"));
 2490          if (IcmpPing(m_ipAddress, 3, g_icmpPingTimeout, nullptr, g_icmpPingSize, false) == ICMP_SUCCESS)
 2491          {
 2492             nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): primary IP address responds to ICMP ping, considering node as reachable"), m_name);
 2493             sendPollerMsg(POLLER_INFO _T("   Primary IP address is responding to ICMP ping\r\n"));
 2494             allDown = false;
 2495          }
 2496          else
 2497          {
 2498             sendPollerMsg(POLLER_ERROR _T("   Primary IP address is not responding to ICMP ping\r\n"));
 2499          }
 2500       }
 2501 
 2502       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): allDown=%s, statFlags=0x%08X"), m_name, allDown ? _T("true") : _T("false"), m_state);
 2503       if (allDown)
 2504       {
 2505          if (!(m_state & DCSF_UNREACHABLE))
 2506          {
 2507             m_state |= DCSF_UNREACHABLE;
 2508             m_downSince = time(nullptr);
 2509             poller->setStatus(_T("check network path"));
 2510             NetworkPathCheckResult patchCheckResult = checkNetworkPath(rqId);
 2511             nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("StatusPoll(%s): network path check result: (rootCauseFound=%s, reason=%d, node=%u, iface=%u)"),
 2512                      m_name, patchCheckResult.rootCauseFound ? _T("true") : _T("false"), static_cast<int>(patchCheckResult.reason),
 2513                      patchCheckResult.rootCauseNodeId, patchCheckResult.rootCauseInterfaceId);
 2514             if (patchCheckResult.rootCauseFound)
 2515             {
 2516                m_state |= DCSF_NETWORK_PATH_PROBLEM;
 2517 
 2518                // Set interfaces and network services to UNKNOWN state
 2519                readLockChildList();
 2520                for(int i = 0; i < getChildList().size(); i++)
 2521                {
 2522                   NetObj *curr = getChildList().get(i);
 2523                   if ((curr->getObjectClass() == OBJECT_INTERFACE) || (curr->getObjectClass() == OBJECT_NETWORKSERVICE))
 2524                   {
 2525                      curr->resetStatus();
 2526                   }
 2527                }
 2528                unlockChildList();
 2529 
 2530                // Clear delayed event queue
 2531                delete_and_null(eventQueue);
 2532 
 2533                static const TCHAR *pnames[] = {
 2534                         _T("reasonCode"), _T("reason"), _T("rootCauseNodeId"), _T("rootCauseNodeName"),
 2535                         _T("rootCauseInterfaceId"), _T("rootCauseInterfaceName"), _T("description")
 2536                };
 2537                static const TCHAR *reasonNames[] = {
 2538                         _T("None"), _T("Router down"), _T("Switch down"), _T("Wireless AP down"),
 2539                         _T("Proxy node down"), _T("Proxy agent unreachable"), _T("VPN tunnel down"),
 2540                         _T("Routing loop"), _T("Interface disabled")
 2541                };
 2542 
 2543                TCHAR description[1024];
 2544                switch(patchCheckResult.reason)
 2545                {
 2546                   case NetworkPathFailureReason::INTERFACE_DISABLED:
 2547                      _sntprintf(description, 1024, _T("Interface %s on node %s is disabled"),
 2548                               GetObjectName(patchCheckResult.rootCauseInterfaceId, _T("UNKNOWN")),
 2549                               GetObjectName(patchCheckResult.rootCauseNodeId, _T("UNKNOWN")));
 2550                      break;
 2551                   case NetworkPathFailureReason::PROXY_AGENT_UNREACHABLE:
 2552                      _sntprintf(description, 1024, _T("Agent on proxy node %s is unreachable"), GetObjectName(patchCheckResult.rootCauseNodeId, _T("UNKNOWN")));
 2553                      break;
 2554                   case NetworkPathFailureReason::PROXY_NODE_DOWN:
 2555                      _sntprintf(description, 1024, _T("Proxy node %s is down"), GetObjectName(patchCheckResult.rootCauseNodeId, _T("UNKNOWN")));
 2556                      break;
 2557                   case NetworkPathFailureReason::ROUTER_DOWN:
 2558                      _sntprintf(description, 1024, _T("Intermediate router %s is down"), GetObjectName(patchCheckResult.rootCauseNodeId, _T("UNKNOWN")));
 2559                      break;
 2560                   case NetworkPathFailureReason::ROUTING_LOOP:
 2561                      _sntprintf(description, 1024, _T("Routing loop detected on intermediate router %s"), GetObjectName(patchCheckResult.rootCauseNodeId, _T("UNKNOWN")));
 2562                      break;
 2563                   case NetworkPathFailureReason::SWITCH_DOWN:
 2564                      _sntprintf(description, 1024, _T("Intermediate switch %s is down"), GetObjectName(patchCheckResult.rootCauseNodeId, _T("UNKNOWN")));
 2565                      break;
 2566                   case NetworkPathFailureReason::VPN_TUNNEL_DOWN:
 2567                      _sntprintf(description, 1024, _T("VPN tunnel %s on node %s is down"),
 2568                               GetObjectName(patchCheckResult.rootCauseInterfaceId, _T("UNKNOWN")),
 2569                               GetObjectName(patchCheckResult.rootCauseNodeId, _T("UNKNOWN")));
 2570                      break;
 2571                   case NetworkPathFailureReason::WIRELESS_AP_DOWN:
 2572                      _sntprintf(description, 1024, _T("Wireless access point %s is down"), GetObjectName(patchCheckResult.rootCauseNodeId, _T("UNKNOWN")));
 2573                      break;
 2574                   default:
 2575                      _tcscpy(description, reasonNames[static_cast<int32_t>(patchCheckResult.reason)]);
 2576                      break;
 2577                }
 2578 
 2579                PostSystemEventWithNames(EVENT_NODE_UNREACHABLE, m_id, "dsisiss", pnames, static_cast<int32_t>(patchCheckResult.reason),
 2580                         reasonNames[static_cast<int32_t>(patchCheckResult.reason)], patchCheckResult.rootCauseNodeId,
 2581                         GetObjectName(patchCheckResult.rootCauseNodeId, _T("")), patchCheckResult.rootCauseInterfaceId,
 2582                         GetObjectName(patchCheckResult.rootCauseInterfaceId, _T("")), description);
 2583                sendPollerMsg(POLLER_WARNING _T("Detected network path problem (%s)\r\n"), description);
 2584             }
 2585             else if ((m_flags & (NF_DISABLE_NXCP | NF_DISABLE_SNMP | NF_DISABLE_ICMP | NF_DISABLE_ETHERNET_IP)) == (NF_DISABLE_NXCP | NF_DISABLE_SNMP | NF_DISABLE_ICMP | NF_DISABLE_ETHERNET_IP))
 2586             {
 2587                sendPollerMsg(POLLER_WARNING _T("All poller methods (NetXMS agent, SNMP, ICMP and EtherNet/IP) are currently disabled\r\n"));
 2588             }
 2589             else
 2590             {
 2591                PostSystemEvent(EVENT_NODE_DOWN, m_id, nullptr);
 2592             }
 2593 
 2594             g_monitoringList.removeDisconnectedNode(m_id);
 2595             sendPollerMsg(POLLER_ERROR _T("Node is unreachable\r\n"));
 2596             resyncDataCollectionConfiguration = true; // Will cause removal of all remotely collected DCIs from proxy
 2597          }
 2598          else
 2599          {
 2600             if ((m_state & DCSF_NETWORK_PATH_PROBLEM) && !checkNetworkPath(rqId).rootCauseFound)
 2601             {
 2602                if ((m_flags & (NF_DISABLE_NXCP | NF_DISABLE_SNMP | NF_DISABLE_ICMP | NF_DISABLE_ETHERNET_IP)) == (NF_DISABLE_NXCP | NF_DISABLE_SNMP | NF_DISABLE_ICMP | NF_DISABLE_ETHERNET_IP))
 2603                {
 2604                   sendPollerMsg(POLLER_WARNING _T("All poller methods (NetXMS agent, SNMP, ICMP and EtherNet/IP) are currently disabled\r\n"));
 2605                }
 2606                else
 2607                {
 2608                   PostSystemEvent(EVENT_NODE_DOWN, m_id, nullptr);
 2609                }
 2610 
 2611                m_state &= ~DCSF_NETWORK_PATH_PROBLEM;
 2612             }
 2613             sendPollerMsg(POLLER_WARNING _T("Node is still unreachable\r\n"));
 2614          }
 2615       }
 2616       else
 2617       {
 2618          m_downSince = 0;
 2619          if (m_state & DCSF_UNREACHABLE)
 2620          {
 2621             int reason = (m_state & DCSF_NETWORK_PATH_PROBLEM) ? 1 : 0;
 2622             m_state &= ~(DCSF_UNREACHABLE | DCSF_NETWORK_PATH_PROBLEM);
 2623             PostSystemEvent(EVENT_NODE_UP, m_id, "d", reason);
 2624             sendPollerMsg(POLLER_INFO _T("Node recovered from unreachable state\r\n"));
 2625             resyncDataCollectionConfiguration = true; // Will cause addition of all remotely collected DCIs on proxy
 2626             // Set recovery time to provide grace period for capability expiration
 2627             m_recoveryTime = now;
 2628             goto restart_status_poll;
 2629          }
 2630          else
 2631          {
 2632             sendPollerMsg(POLLER_INFO _T("Node is connected\r\n"));
 2633          }
 2634       }
 2635    }
 2636 
 2637    POLL_CANCELLATION_CHECKPOINT_EX(delete eventQueue);
 2638 
 2639    // Get uptime and update boot time
 2640    if (!(m_state & DCSF_UNREACHABLE))
 2641    {
 2642       TCHAR buffer[MAX_RESULT_LENGTH];
 2643       if (getMetricFromAgent(_T("System.Uptime"), buffer, MAX_RESULT_LENGTH) == DCE_SUCCESS)
 2644       {
 2645          m_bootTime = time(nullptr) - _tcstol(buffer, nullptr, 0);
 2646          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): boot time set to %u from agent"), m_name, m_id, (UINT32)m_bootTime);
 2647       }
 2648       else if (getMetricFromSNMP(m_snmpPort, SNMP_VERSION_DEFAULT, _T(".1.3.6.1.2.1.1.3.0"), buffer, MAX_RESULT_LENGTH, SNMP_RAWTYPE_NONE) == DCE_SUCCESS)
 2649       {
 2650          m_bootTime = time(nullptr) - _tcstol(buffer, nullptr, 0) / 100;   // sysUpTime is in hundredths of a second
 2651          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): boot time set to %u from SNMP"), m_name, m_id, (UINT32)m_bootTime);
 2652       }
 2653       else
 2654       {
 2655          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): unable to get system uptime"), m_name, m_id);
 2656       }
 2657    }
 2658    else
 2659    {
 2660       m_bootTime = 0;
 2661    }
 2662 
 2663    // Get agent uptime to check if it was restarted
 2664    if (!(m_state & DCSF_UNREACHABLE) && isNativeAgent())
 2665    {
 2666       TCHAR buffer[MAX_RESULT_LENGTH];
 2667       if (getMetricFromAgent(_T("Agent.Uptime"), buffer, MAX_RESULT_LENGTH) == DCE_SUCCESS)
 2668       {
 2669          time_t oldAgentuptime = m_agentUpTime;
 2670          m_agentUpTime = _tcstol(buffer, nullptr, 0);
 2671          if (oldAgentuptime > m_agentUpTime)
 2672          {
 2673             //cancel file monitoring locally(on agent it is canceled if agent have fallen)
 2674             g_monitoringList.removeDisconnectedNode(m_id);
 2675          }
 2676       }
 2677       else
 2678       {
 2679          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): unable to get agent uptime"), m_name, m_id);
 2680          g_monitoringList.removeDisconnectedNode(m_id);
 2681          m_agentUpTime = 0;
 2682       }
 2683    }
 2684    else
 2685    {
 2686       m_agentUpTime = 0;
 2687    }
 2688 
 2689    // Get geolocation
 2690    if (!(m_state & DCSF_UNREACHABLE))
 2691    {
 2692       bool geoLocationRetrieved = false;
 2693       if (isNativeAgent())
 2694       {
 2695          TCHAR buffer[MAX_RESULT_LENGTH];
 2696          if (getMetricFromAgent(_T("GPS.LocationData"), buffer, MAX_RESULT_LENGTH) == DCE_SUCCESS)
 2697          {
 2698             GeoLocation location = GeoLocation::parseAgentData(buffer);
 2699             if (location.getType() != GL_UNSET)
 2700             {
 2701                nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): location set to %s, %s from agent"), m_name, m_id, location.getLatitudeAsString(), location.getLongitudeAsString());
 2702                setGeoLocation(location);
 2703                geoLocationRetrieved = true;
 2704             }
 2705          }
 2706       }
 2707       if (!geoLocationRetrieved && isSNMPSupported() && (m_driver != nullptr))
 2708       {
 2709          SNMP_Transport *transport = createSnmpTransport();
 2710          if (transport != nullptr)
 2711          {
 2712             GeoLocation location = m_driver->getGeoLocation(transport, this, m_driverData);
 2713             if (location.getType() != GL_UNSET)
 2714             {
 2715                nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): location set to %s, %s from SNMP device driver"), m_name, m_id, location.getLatitudeAsString(), location.getLongitudeAsString());
 2716                setGeoLocation(location);
 2717                geoLocationRetrieved = true;
 2718             }
 2719             delete transport;
 2720          }
 2721       }
 2722       if (!geoLocationRetrieved)
 2723       {
 2724          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): unable to get system location"), m_name, m_id);
 2725       }
 2726    }
 2727 
 2728    // Get agent log and agent local database status
 2729    if (!(m_state & DCSF_UNREACHABLE) && isNativeAgent())
 2730    {
 2731       TCHAR buffer[MAX_RESULT_LENGTH];
 2732       if (getMetricFromAgent(_T("Agent.LogFile.Status"), buffer, MAX_RESULT_LENGTH) == DCE_SUCCESS)
 2733       {
 2734          uint32_t status = _tcstol(buffer, nullptr, 0);
 2735          if (status != 0)
 2736             PostSystemEvent(EVENT_AGENT_LOG_PROBLEM, m_id, "ds", status, _T("could not open"));
 2737       }
 2738       else
 2739       {
 2740          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): unable to get agent log status"), m_name, m_id);
 2741       }
 2742 
 2743       if (getMetricFromAgent(_T("Agent.LocalDatabase.Status"), buffer, MAX_RESULT_LENGTH) == DCE_SUCCESS)
 2744       {
 2745          uint32_t status = _tcstol(buffer, nullptr, 0);
 2746          const TCHAR *statusDescription[3]= {
 2747                                        _T("normal"),
 2748                                        _T("could not open database"),
 2749                                        _T("could not update database"),
 2750                                        };
 2751          if (status != 0)
 2752             PostSystemEvent(EVENT_AGENT_LOCAL_DATABASE_PROBLEM, m_id, "ds", status, statusDescription[status]);
 2753       }
 2754       else
 2755       {
 2756          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): unable to get agent local database status"), m_name, m_id);
 2757       }
 2758    }
 2759 
 2760    // Get user agent capability
 2761    if (!(m_state & DCSF_UNREACHABLE) && isNativeAgent())
 2762    {
 2763       TCHAR buffer[MAX_RESULT_LENGTH];
 2764       if (getMetricFromAgent(_T("Agent.IsUserAgentInstalled"), buffer, MAX_RESULT_LENGTH) == DCE_SUCCESS)
 2765       {
 2766          uint32_t status = _tcstol(buffer, nullptr, 0);
 2767          if (status != 0)
 2768             m_capabilities |= NC_HAS_USER_AGENT;
 2769          else
 2770             m_capabilities &= ~NC_HAS_USER_AGENT;
 2771       }
 2772       else
 2773       {
 2774          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): cannot get user agent information"), m_name, m_id);
 2775       }
 2776    }
 2777 
 2778    // Send delayed events and destroy delayed event queue
 2779    if (eventQueue != nullptr)
 2780    {
 2781       ResendEvents(eventQueue);
 2782       delete eventQueue;
 2783    }
 2784 
 2785    POLL_CANCELLATION_CHECKPOINT();
 2786 
 2787    // Call hooks in loaded modules
 2788    ENUMERATE_MODULES(pfStatusPollHook)
 2789    {
 2790       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("StatusPoll(%s [%d]): calling hook in module %s"), m_name, m_id, CURRENT_MODULE.szName);
 2791       CURRENT_MODULE.pfStatusPollHook(this, pSession, rqId, poller);
 2792    }
 2793 
 2794    // Execute hook script
 2795    poller->setStatus(_T("hook"));
 2796    executeHookScript(_T("StatusPoll"), rqId);
 2797 
 2798    if (resyncDataCollectionConfiguration)
 2799    {
 2800       // call to onDataCollectionChange will force data collection
 2801       // configuration upload to this node or relevant proxy nodes
 2802       onDataCollectionChange();
 2803    }
 2804 
 2805    poller->setStatus(_T("cleanup"));
 2806 
 2807    if (oldCapabilities != m_capabilities)
 2808       PostSystemEvent(EVENT_NODE_CAPABILITIES_CHANGED, m_id, "xx", oldCapabilities, m_capabilities);
 2809 
 2810    if (oldState != m_state)
 2811    {
 2812       markAsModified(MODIFY_RUNTIME); // only notify clients
 2813    }
 2814 
 2815    if (oldCapabilities != m_capabilities)
 2816    {
 2817       markAsModified(MODIFY_NODE_PROPERTIES);
 2818    }
 2819 
 2820    calculateCompoundStatus();
 2821 
 2822    if (IsZoningEnabled())
 2823    {
 2824       shared_ptr<Zone> zone = FindZoneByProxyId(m_id);
 2825       if (zone != nullptr)
 2826       {
 2827          zone->updateProxyStatus(self(), true);
 2828       }
 2829    }
 2830 
 2831    sendPollerMsg(_T("Finished status poll for node %s\r\n"), m_name);
 2832    sendPollerMsg(_T("Node status after poll is %s\r\n"), GetStatusAsText(m_status, true));
 2833 
 2834    lockProperties();
 2835    m_pollRequestor = nullptr;
 2836    unlockProperties();
 2837 
 2838    pollerUnlock();
 2839    nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Finished status poll for node %s (ID: %d)"), m_name, m_id);
 2840 
 2841    // Check if the node has to be deleted due to long downtime
 2842    if (rqId == 0)
 2843    {
 2844       time_t unreachableDeleteDays = (time_t)ConfigReadInt(_T("DeleteUnreachableNodesPeriod"), 0);
 2845       if ((unreachableDeleteDays > 0) && (m_downSince > 0) &&
 2846           (time(nullptr) - m_downSince > unreachableDeleteDays * 24 * 3600))
 2847       {
 2848          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 2, _T("Delete node %s [%u] because it is unreachable for more than %d days"),
 2849                   m_name, m_id, static_cast<int>(unreachableDeleteDays));
 2850          ThreadPoolExecute(g_mainThreadPool, BackgroundDeleteNode, self());
 2851       }
 2852    }
 2853 }
 2854 
 2855 /**
 2856  * Check single element of network path
 2857  */
 2858 NetworkPathCheckResult Node::checkNetworkPathElement(uint32_t nodeId, const TCHAR *nodeType, bool isProxy, bool isSwitch, uint32_t requestId, bool secondPass)
 2859 {
 2860    shared_ptr<Node> node = static_pointer_cast<Node>(FindObjectById(nodeId, OBJECT_NODE));
 2861    if (node == nullptr)
 2862       return NetworkPathCheckResult();
 2863 
 2864    nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("Node::checkNetworkPathElement(%s [%d]): found %s: %s [%d]"), m_name, m_id, nodeType, node->getName(), node->getId());
 2865 
 2866    if (secondPass && (node->m_statusPollState.getLastCompleted() < time(nullptr) - 1))
 2867    {
 2868       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("Node::checkNetworkPathElement(%s [%d]): forced status poll on node %s [%d]"),
 2869                 m_name, m_id, node->getName(), node->getId());
 2870       node->startForcedStatusPoll();
 2871       PollerInfo *poller = RegisterPoller(PollerType::STATUS, node);
 2872       poller->startExecution();
 2873       node->statusPoll(poller, nullptr, 0);
 2874       delete poller;
 2875    }
 2876 
 2877    if (node->isDown())
 2878    {
 2879       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPathElement(%s [%d]): %s %s [%d] is down"),
 2880                 m_name, m_id, nodeType, node->getName(), node->getId());
 2881       sendPollerMsg(POLLER_WARNING _T("   %s %s is down\r\n"), nodeType, node->getName());
 2882       return NetworkPathCheckResult(isProxy ? NetworkPathFailureReason::PROXY_NODE_DOWN : (isSwitch ? NetworkPathFailureReason::SWITCH_DOWN : NetworkPathFailureReason::ROUTER_DOWN), node->getId());
 2883    }
 2884    if (isProxy && node->isNativeAgent() && (node->getState() & NSF_AGENT_UNREACHABLE))
 2885    {
 2886       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPathElement(%s [%d]): agent on %s %s [%d] is down"),
 2887                 m_name, m_id, nodeType, node->getName(), node->getId());
 2888       sendPollerMsg(POLLER_WARNING _T("   Agent on %s %s is down\r\n"), nodeType, node->getName());
 2889       return NetworkPathCheckResult(NetworkPathFailureReason::PROXY_AGENT_UNREACHABLE, node->getId());
 2890    }
 2891    return NetworkPathCheckResult();
 2892 }
 2893 
 2894 /**
 2895  * Check network path between node and management server to detect possible intermediate node failure - layer 2
 2896  *
 2897  * @return true if network path problems found
 2898  */
 2899 NetworkPathCheckResult Node::checkNetworkPathLayer2(uint32_t requestId, bool secondPass)
 2900 {
 2901    if (IsShutdownInProgress())
 2902       return NetworkPathCheckResult();
 2903 
 2904    time_t now = time(nullptr);
 2905 
 2906    // Check proxy node(s)
 2907    if (IsZoningEnabled() && (m_zoneUIN != 0))
 2908    {
 2909       shared_ptr<Zone> zone = FindZoneByUIN(m_zoneUIN);
 2910       if ((zone != nullptr) && !zone->isProxyNode(m_id))
 2911       {
 2912          NetworkPathCheckResult result = checkNetworkPathElement(zone->getProxyNodeId(this), _T("zone proxy"), true, false, requestId, secondPass);
 2913          if (result.rootCauseFound)
 2914             return result;
 2915       }
 2916    }
 2917 
 2918    // Check directly connected switch
 2919    sendPollerMsg(_T("Checking ethernet connectivity...\r\n"));
 2920    shared_ptr<Interface> iface = findInterfaceByIP(m_ipAddress);
 2921    if (iface != nullptr)
 2922    {
 2923       if  (iface->getPeerNodeId() != 0)
 2924       {
 2925          nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("Node::checkNetworkPath(%s [%d]): found interface object for primary IP: %s [%d]"), m_name, m_id, iface->getName(), iface->getId());
 2926          NetworkPathCheckResult result = checkNetworkPathElement(iface->getPeerNodeId(), _T("upstream switch"), false, true, requestId, secondPass);
 2927          if (result.rootCauseFound)
 2928             return result;
 2929 
 2930          shared_ptr<NetObj> switchNode = FindObjectById(iface->getPeerNodeId(), OBJECT_NODE);
 2931          shared_ptr<Interface> switchIface = static_pointer_cast<Interface>(FindObjectById(iface->getPeerInterfaceId(), OBJECT_INTERFACE));
 2932          if ((switchNode != nullptr) && (switchIface != nullptr) &&
 2933              ((switchIface->getAdminState() == IF_ADMIN_STATE_DOWN) || (switchIface->getAdminState() == IF_ADMIN_STATE_TESTING)))
 2934          {
 2935             nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%d]): upstream interface %s [%d] on switch %s [%d] is administratively down"),
 2936                         m_name, m_id, switchIface->getName(), switchIface->getId(), switchNode->getName(), switchNode->getId());
 2937             sendPollerMsg(POLLER_WARNING _T("   Upstream interface %s on node %s is administratively down\r\n"), switchIface->getName(), switchNode->getName());
 2938             result.rootCauseFound = true;
 2939             result.reason = NetworkPathFailureReason::INTERFACE_DISABLED;
 2940             result.rootCauseNodeId = switchNode->getId();
 2941             result.rootCauseInterfaceId = switchIface->getId();
 2942             return result;
 2943          }
 2944       }
 2945       else
 2946       {
 2947          int type = 0;
 2948          shared_ptr<NetObj> cp = FindInterfaceConnectionPoint(iface->getMacAddr(), &type);
 2949          if (cp != nullptr)
 2950          {
 2951             nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6,
 2952                      _T("Node::checkNetworkPath(%s [%d]): found connection point: %s [%d]"), m_name, m_id, cp->getName(), cp->getId());
 2953             if (secondPass)
 2954             {
 2955                shared_ptr<Node> node = (cp->getObjectClass() == OBJECT_INTERFACE) ? static_cast<Interface*>(cp.get())->getParentNode() : static_cast<AccessPoint*>(cp.get())->getParentNode();
 2956                if ((node != nullptr) && !node->isDown() && (node->m_statusPollState.getLastCompleted() < now - 1))
 2957                {
 2958                   nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("Node::checkNetworkPath(%s [%d]): forced status poll on node %s [%d]"),
 2959                               m_name, m_id, node->getName(), node->getId());
 2960                   node->startForcedStatusPoll();
 2961                   node->statusPollWorkerEntry(RegisterPoller(PollerType::STATUS, node), nullptr, 0);
 2962                }
 2963             }
 2964 
 2965             if (cp->getObjectClass() == OBJECT_INTERFACE)
 2966             {
 2967                Interface *iface = static_cast<Interface*>(cp.get());
 2968                if ((iface->getAdminState() == IF_ADMIN_STATE_DOWN) || (iface->getAdminState() == IF_ADMIN_STATE_TESTING))
 2969                {
 2970                   String parentNodeName = iface->getParentNodeName();
 2971                   nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%u]): upstream interface %s [%u] on switch %s [%u] is administratively down"),
 2972                               m_name, m_id, iface->getName(), iface->getId(), parentNodeName.cstr(), iface->getParentNodeId());
 2973                   sendPollerMsg(POLLER_WARNING _T("   Upstream interface %s on node %s is administratively down\r\n"),
 2974                                 iface->getName(), parentNodeName.cstr());
 2975                   return NetworkPathCheckResult(NetworkPathFailureReason::INTERFACE_DISABLED, iface->getParentNodeId(), iface->getId());
 2976                }
 2977             }
 2978             else if (cp->getObjectClass() == OBJECT_ACCESSPOINT)
 2979             {
 2980                AccessPoint *ap = static_cast<AccessPoint*>(cp.get());
 2981                if (ap->getStatus() == STATUS_CRITICAL)   // FIXME: how to correctly determine if AP is down?
 2982                {
 2983                   nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%d]): wireless access point %s [%d] is down"),
 2984                               m_name, m_id, ap->getName(), ap->getId());
 2985                   sendPollerMsg(POLLER_WARNING _T("   Wireless access point %s is down\r\n"), ap->getName());
 2986                   return NetworkPathCheckResult(NetworkPathFailureReason::WIRELESS_AP_DOWN, ap->getId());
 2987                }
 2988             }
 2989          }
 2990       }
 2991    }
 2992    else
 2993    {
 2994       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%d]): cannot find interface object for primary IP"), m_name, m_id);
 2995    }
 2996    return NetworkPathCheckResult();
 2997 }
 2998 
 2999 /**
 3000  * Check network path between node and management server to detect possible intermediate node failure - layer 3
 3001  *
 3002  * @return true if network path problems found
 3003  */
 3004 NetworkPathCheckResult Node::checkNetworkPathLayer3(uint32_t requestId, bool secondPass)
 3005 {
 3006    if (IsShutdownInProgress())
 3007       return NetworkPathCheckResult();
 3008 
 3009    shared_ptr<Node> mgmtNode = static_pointer_cast<Node>(FindObjectById(g_dwMgmtNode, OBJECT_NODE));
 3010    if (mgmtNode == nullptr)
 3011    {
 3012       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%d]): cannot find management node"), m_name, m_id);
 3013       return NetworkPathCheckResult();
 3014    }
 3015 
 3016    shared_ptr<NetworkPath> trace = TraceRoute(mgmtNode, self());
 3017    if ((trace == nullptr) && (m_lastKnownNetworkPath == nullptr))
 3018    {
 3019       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%d]): trace not available"), m_name, m_id);
 3020       return NetworkPathCheckResult();
 3021    }
 3022    if ((trace == nullptr) || !trace->isComplete())
 3023    {
 3024       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%d]): current trace is %s, using cached trace"),
 3025                m_name, m_id, (trace == nullptr) ? _T("not available") : _T("incomplete"));
 3026       lockProperties();
 3027       if (m_lastKnownNetworkPath != nullptr)
 3028          trace = m_lastKnownNetworkPath;
 3029       unlockProperties();
 3030    }
 3031    nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%d]): trace available, %d hops, %s"),
 3032              m_name, m_id, trace->getHopCount(), trace->isComplete() ? _T("complete") : _T("incomplete"));
 3033 
 3034    // We will do path check in two passes
 3035    // If unreachable intermediate node will be found on first pass,
 3036    // then method will just return true. Otherwise, we will do
 3037    // second pass, this time forcing status poll on each node in the path.
 3038    sendPollerMsg(_T("Checking network path (%s pass)...\r\n"), secondPass ? _T("second") : _T("first"));
 3039    NetworkPathCheckResult result;
 3040    for(int i = 0; i < trace->getHopCount(); i++)
 3041    {
 3042       NetworkPathElement *hop = trace->getHopInfo(i);
 3043       if ((hop->object == nullptr) || (hop->object->getId() == m_id) || (hop->object->getObjectClass() != OBJECT_NODE))
 3044          continue;
 3045 
 3046       // Check for loops
 3047       if (i > 0)
 3048       {
 3049          for(int j = i - 1; j >= 0; j--)
 3050          {
 3051             NetworkPathElement *prevHop = trace->getHopInfo(j);
 3052             if (prevHop->object == hop->object)
 3053             {
 3054                prevHop = trace->getHopInfo(i - 1);
 3055                nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%d]): routing loop detected on upstream node %s [%d]"),
 3056                            m_name, m_id, prevHop->object->getName(), prevHop->object->getId());
 3057                sendPollerMsg(POLLER_WARNING _T("   Routing loop detected on upstream node %s\r\n"), prevHop->object->getName());
 3058 
 3059                static const TCHAR *names[] =
 3060                         { _T("protocol"), _T("destNodeId"), _T("destAddress"),
 3061                           _T("sourceNodeId"), _T("sourceAddress"), _T("prefix"),
 3062                           _T("prefixLength"), _T("nextHopNodeId"), _T("nextHopAddress")
 3063                         };
 3064                PostSystemEventWithNames(EVENT_ROUTING_LOOP_DETECTED, prevHop->object->getId(), "siAiAAdiA", names,
 3065                      (trace->getSourceAddress().getFamily() == AF_INET6) ? _T("IPv6") : _T("IPv4"),
 3066                      m_id, &m_ipAddress, g_dwMgmtNode, &(trace->getSourceAddress()),
 3067                      &prevHop->route, prevHop->route.getMaskBits(), hop->object->getId(), &prevHop->nextHop);
 3068 
 3069                result.rootCauseFound = true;
 3070                result.reason = NetworkPathFailureReason::ROUTING_LOOP;
 3071                result.rootCauseNodeId = hop->object->getId();
 3072                break;
 3073             }
 3074          }
 3075          if (result.rootCauseFound)
 3076             break;
 3077       }
 3078 
 3079       nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 6, _T("Node::checkNetworkPath(%s [%d]): checking upstream router %s [%d]"),
 3080                   m_name, m_id, hop->object->getName(), hop->object->getId());
 3081       result = checkNetworkPathElement(hop->object->getId(), _T("upstream router"), false, false, requestId, secondPass);
 3082       if (result.rootCauseFound)
 3083          break;
 3084 
 3085       if (hop->type == NetworkPathElementType::ROUTE)
 3086       {
 3087          shared_ptr<Interface> iface = static_cast<Node&>(*hop->object).findInterfaceByIndex(hop->ifIndex);
 3088          if ((iface != nullptr) && ((iface->getAdminState() == IF_ADMIN_STATE_DOWN) || (iface->getAdminState() == IF_ADMIN_STATE_TESTING)))
 3089          {
 3090             nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%d]): upstream interface %s [%d] on node %s [%d] is administratively down"),
 3091                         m_name, m_id, iface->getName(), iface->getId(), hop->object->getName(), hop->object->getId());
 3092             sendPollerMsg(POLLER_WARNING _T("   Upstream interface %s on node %s is administratively down\r\n"), iface->getName(), hop->object->getName());
 3093             result.rootCauseFound = true;
 3094             result.reason = NetworkPathFailureReason::INTERFACE_DISABLED;
 3095             result.rootCauseNodeId = hop->object->getId();
 3096             result.rootCauseInterfaceId = iface->getId();
 3097             break;
 3098          }
 3099       }
 3100       else if (hop->type == NetworkPathElementType::VPN)
 3101       {
 3102          // Next hop is behind VPN tunnel
 3103          shared_ptr<VPNConnector> vpnConn = static_pointer_cast<VPNConnector>(FindObjectById(hop->ifIndex, OBJECT_VPNCONNECTOR));
 3104          if ((vpnConn != nullptr) && (vpnConn->getStatus() == STATUS_CRITICAL))
 3105          {
 3106             result.rootCauseFound = true;
 3107             result.reason = NetworkPathFailureReason::VPN_TUNNEL_DOWN;
 3108             result.rootCauseNodeId = hop->object->getId();
 3109             result.rootCauseInterfaceId = vpnConn->getId();
 3110             break;
 3111          }
 3112       }
 3113    }
 3114 
 3115    lockProperties();
 3116    if ((trace != m_lastKnownNetworkPath) && trace->isComplete())
 3117    {
 3118       m_lastKnownNetworkPath = trace;
 3119    }
 3120    unlockProperties();
 3121    return result;
 3122 }
 3123 
 3124 /**
 3125  * Check network path between node and management server to detect possible intermediate node failure
 3126  *
 3127  * @return true if network path problems found
 3128  */
 3129 NetworkPathCheckResult Node::checkNetworkPath(uint32_t requestId)
 3130 {
 3131    NetworkPathCheckResult result = checkNetworkPathLayer2(requestId, false);
 3132    if (result.rootCauseFound)
 3133       return result;
 3134 
 3135    result = checkNetworkPathLayer3(requestId, false);
 3136    if (result.rootCauseFound)
 3137       return result;
 3138 
 3139    nxlog_debug_tag(DEBUG_TAG_STATUS_POLL, 5, _T("Node::checkNetworkPath(%s [%d]): will do second pass"), m_name, m_id);
 3140 
 3141    result = checkNetworkPathLayer2(requestId, true);
 3142    if (result.rootCauseFound)
 3143       return result;
 3144 
 3145    return checkNetworkPathLayer3(requestId, true);
 3146 }
 3147 
 3148 /**
 3149  * Check agent policy binding
 3150  * Intended to be called only from configuration poller
 3151  */
 3152 void Node::checkAgentPolicyBinding(const shared_ptr<AgentConnectionEx>& conn)
 3153 {
 3154    sendPollerMsg(_T("   Checking agent policy deployment\r\n"));
 3155 
 3156    AgentPolicyInfo *ap;
 3157    uint32_t rcc = conn->getPolicyInventory(&ap);
 3158    if (rcc == ERR_SUCCESS)
 3159    {
 3160       // Check for unbound but installed policies
 3161       for(int i = 0; i < ap->size(); i++)
 3162       {
 3163          uuid guid = ap->getGuid(i);
 3164          bool found = false;
 3165          readLockParentList();
 3166          for(int i = 0; i < getParentList().size(); i++)
 3167          {
 3168             if (getParentList().get(i)->getObjectClass() == OBJECT_TEMPLATE)
 3169             {
 3170                 if (static_cast<Template*>(getParentList().get(i))->hasPolicy(guid))
 3171                 {
 3172                    found = true;
 3173                    break;
 3174                 }
 3175             }
 3176          }
 3177 
 3178          if (!found)
 3179          {
 3180             sendPollerMsg(_T("      Removing policy %s from agent\r\n"), guid.toString().cstr());
 3181             auto data = make_shared<AgentPolicyRemovalData>(self(), guid, ap->getType(i), isNewPolicyTypeFormatSupported());
 3182             _sntprintf(data->debugId, 256, _T("%s [%u] from %s/%s"), getName(), getId(), _T("unknown"), guid.toString().cstr());
 3183             ThreadPoolExecute(g_agentConnectionThreadPool, RemoveAgentPolicy, data);
 3184          }
 3185 
 3186          unlockParentList();
 3187       }
 3188 
 3189       // Check for bound but not installed policies and schedule it's installation again
 3190       readLockParentList();
 3191       for(int i = 0; i < getParentList().size(); i++)
 3192       {
 3193          if (getParentList().get(i)->getObjectClass() == OBJECT_TEMPLATE)
 3194          {
 3195             static_cast<Template*>(getParentList().get(i))->checkPolicyDeployment(self(), ap);
 3196          }
 3197       }
 3198       unlockParentList();
 3199 
 3200       m_capabilities |= ap->isNewTypeFormat() ? NC_IS_NEW_POLICY_TYPES : 0;
 3201       delete ap;
 3202    }
 3203    else
 3204    {
 3205       sendPollerMsg(POLLER_WARNING _T("      Cannot get policy inventory form agent (%s)\r\n"), AgentErrorCodeToText(rcc));
 3206       nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): AgentConnection::getPolicyInventory() failed: rcc=%d"), m_name, rcc);
 3207    }
 3208 }
 3209 
 3210 /**
 3211  * Update primary IP address from primary name
 3212  */
 3213 void Node::updatePrimaryIpAddr()
 3214 {
 3215    if (m_primaryHostName.isNull() || m_primaryHostName.isEmpty() || IsShutdownInProgress())
 3216       return;
 3217 
 3218    InetAddress ipAddr = ResolveHostName(m_zoneUIN, m_primaryHostName);
 3219    if (!ipAddr.equals(getIpAddress()) && (ipAddr.isValidUnicast() || !_tcscmp(m_primaryHostName, _T("0.0.0.0")) || ((m_capabilities & NC_IS_LOCAL_MGMT) && ipAddr.isLoopback())))
 3220    {
 3221       TCHAR buffer1[64], buffer2[64];
 3222 
 3223       nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 4, _T("IP address for node %s [%d] changed from %s to %s"),
 3224          m_name, (int)m_id, m_ipAddress.toString(buffer1), ipAddr.toString(buffer2));
 3225       PostSystemEvent(EVENT_IP_ADDRESS_CHANGED, m_id, "AA", &ipAddr, &m_ipAddress);
 3226 
 3227       lockProperties();
 3228       setPrimaryIPAddress(ipAddr);
 3229       unlockProperties();
 3230 
 3231       agentLock();
 3232       deleteAgentConnection();
 3233       agentUnlock();
 3234    }
 3235 }
 3236 
 3237 /**
 3238  * Read baseboard information from agent
 3239  */
 3240 static int ReadBaseboardInformation(Node *node, ObjectArray<HardwareComponent> *components)
 3241 {
 3242    TCHAR buffer[256 * 4];
 3243    memset(buffer, 0, sizeof(buffer));
 3244 
 3245    static const TCHAR *metrics[4] =
 3246    {
 3247       _T("Hardware.Baseboard.Manufacturer"),
 3248       _T("Hardware.Baseboard.Product"),
 3249       _T("Hardware.Baseboard.SerialNumber"),
 3250       _T("Hardware.Baseboard.Type")
 3251    };
 3252 
 3253    int readCount = 0;
 3254    for(int i = 0; i < 4; i++)
 3255    {
 3256       if (node->getMetricFromAgent(metrics[i], &buffer[i * 256], 256) == ERR_SUCCESS)
 3257          readCount++;
 3258    }
 3259 
 3260    if (readCount > 0)
 3261    {
 3262       components->add(new HardwareComponent(HWC_BASEBOARD, 0, &buffer[256 * 3], buffer, &buffer[256], nullptr, &buffer[512]));
 3263    }
 3264    return readCount;
 3265 }
 3266 
 3267 /**
 3268  * Read hardware component information using given table
 3269  */
 3270 static int ReadHardwareInformation(Node *node, ObjectArray<HardwareComponent> *components, HardwareComponentCategory category, const TCHAR *tableName)
 3271 {
 3272    Table *table;
 3273    if (node->getTableFromAgent(tableName, &table) != DCE_SUCCESS)
 3274       return 0;
 3275 
 3276    bool resequence = false;
 3277    int base = components->size();
 3278    for(int i = 0; i < table->getNumRows(); i++)
 3279    {
 3280       HardwareComponent *c = new HardwareComponent(category, table, i);
 3281       if (!resequence)
 3282       {
 3283          // Check for duplicate indexes - older agent versions that use BIOS handles may return duplicate handles
 3284          for(int j = base; j < components->size(); j++)
 3285          {
 3286             if (components->get(j)->getIndex() == c->getIndex())
 3287             {
 3288                resequence = true;
 3289                break;
 3290             }
 3291          }
 3292       }
 3293       components->add(c);
 3294    }
 3295    delete table;
 3296 
 3297    if (resequence)
 3298    {
 3299       for(int i = base, index = 0; i < components->size(); i++, index++)
 3300          components->get(i)->setIndex(index);
 3301    }
 3302 
 3303    return 1;
 3304 }
 3305 
 3306 /**
 3307  * Update hardware property
 3308  */
 3309 bool Node::updateSystemHardwareProperty(SharedString &property, const TCHAR *value, const TCHAR *displayName, uint32_t requestId)
 3310 {
 3311    bool modified = false;
 3312    if (_tcscmp(property, value))
 3313    {
 3314       if (*value != 0)
 3315          sendPollerMsg(_T("   System hardware %s set to %s\r\n"), displayName, value);
 3316       property = value;
 3317       modified = true;
 3318    }
 3319    return modified;
 3320 }
 3321 
 3322 /**
 3323  * Update general system hardware information
 3324  */
 3325 bool Node::updateSystemHardwareInformation(PollerInfo *poller, uint32_t requestId)
 3326 {
 3327    // Skip for EtherNet/IP devices because hardware information is updated
 3328    // from identity message and if SNMP is present on device it may override
 3329    // it with incorrect values
 3330    if (m_capabilities & NC_IS_ETHERNET_IP)
 3331    {
 3332       nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): skipping Node::updateSystemHardwareInformation for EtherNet/IP device"), m_name);
 3333       return false;
 3334    }
 3335 
 3336    poller->setStatus(_T("hardware check"));
 3337    sendPollerMsg(_T("Updating general system hardware information\r\n"));
 3338 
 3339    DeviceHardwareInfo hwInfo;
 3340    memset(&hwInfo, 0, sizeof(hwInfo));
 3341 
 3342    bool success = false;
 3343    if (m_capabilities & NC_IS_NATIVE_AGENT)
 3344    {
 3345       if (getMetricFromAgent(_T("Hardware.System.Manufacturer"), hwInfo.vendor, sizeof(hwInfo.vendor) / sizeof(TCHAR)) == DCE_SUCCESS)
 3346          success = true;
 3347       if (getMetricFromAgent(_T("Hardware.System.Product"), hwInfo.productName, sizeof(hwInfo.productName) / sizeof(TCHAR)) == DCE_SUCCESS)
 3348          success = true;
 3349       if (getMetricFromAgent(_T("Hardware.System.ProductCode"), hwInfo.productCode, sizeof(hwInfo.productCode) / sizeof(TCHAR)) == DCE_SUCCESS)
 3350          success = true;
 3351       if (getMetricFromAgent(_T("Hardware.System.Version"), hwInfo.productVersion, sizeof(hwInfo.productVersion) / sizeof(TCHAR)) == DCE_SUCCESS)
 3352          success = true;
 3353       if (getMetricFromAgent(_T("Hardware.System.SerialNumber"), hwInfo.serialNumber, sizeof(hwInfo.serialNumber) / sizeof(TCHAR)) == DCE_SUCCESS)
 3354          success = true;
 3355    }
 3356 
 3357    if (!success && (m_capabilities & NC_IS_SNMP))
 3358    {
 3359       SNMP_Transport *snmp = createSnmpTransport();
 3360       if (snmp != nullptr)
 3361       {
 3362          success = m_driver->getHardwareInformation(snmp, this, m_driverData, &hwInfo);
 3363          delete snmp;
 3364       }
 3365       if (!success && (m_capabilities & NC_HAS_ENTITY_MIB))
 3366       {
 3367          // Try to get hardware information from ENTITY MIB
 3368          lockProperties();
 3369          if (m_components != nullptr)
 3370          {
 3371             const Component *root = m_components->getRoot();
 3372             if (root != nullptr)
 3373             {
 3374                // Device could be reported as but consist from single chassis only
 3375                if ((root->getClass() == 11) && (root->getChildren()->size() == 1) && (root->getChildren()->get(0)->getClass() == 3))
 3376                {
 3377                   root = root->getChildren()->get(0);
 3378                }
 3379                if ((root->getClass() == 3) || (root->getClass() == 11)) // Chassis or stack
 3380                {
 3381                   _tcslcpy(hwInfo.vendor, root->getVendor(), 128);
 3382                   _tcslcpy(hwInfo.productName, (*root->getModel() != 0) ? root->getModel() : root->getDescription(), 128);
 3383                   _tcslcpy(hwInfo.productVersion, root->getFirmware(), 16);
 3384                   _tcslcpy(hwInfo.serialNumber, root->getSerial(), 32);
 3385                }
 3386                success = (hwInfo.vendor[0] != 0) || (hwInfo.productName[0] != 0) || (hwInfo.productVersion[0] != 0) || (hwInfo.serialNumber[0] != 0);
 3387             }
 3388          }
 3389          unlockProperties();
 3390       }
 3391    }
 3392 
 3393    bool modified = false;
 3394    if (success)
 3395    {
 3396       nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): system hardware information: vendor=%s product=%s pcode=%s version=%s serial=%s"),
 3397                m_name, hwInfo.vendor, hwInfo.productName, hwInfo.productCode, hwInfo.productVersion, hwInfo.serialNumber);
 3398 
 3399       lockProperties();
 3400       if (updateSystemHardwareProperty(m_vendor, hwInfo.vendor, _T("vendor"), requestId))
 3401          modified = true;
 3402       if (updateSystemHardwareProperty(m_productName, hwInfo.productName, _T("product name"), requestId))
 3403          modified = true;
 3404       if (updateSystemHardwareProperty(m_productCode, hwInfo.productCode, _T("product code"), requestId))
 3405          modified = true;
 3406       if (updateSystemHardwareProperty(m_productVersion, hwInfo.productVersion, _T("product version"), requestId))
 3407          modified = true;
 3408       if (updateSystemHardwareProperty(m_serialNumber, hwInfo.serialNumber, _T("serial number"), requestId))
 3409          modified = true;
 3410       unlockProperties();
 3411 
 3412       if (modified)
 3413          markAsModified(MODIFY_NODE_PROPERTIES);
 3414    }
 3415 
 3416    return modified;
 3417 }
 3418 
 3419 /**
 3420  * Update list of hardware components for node
 3421  */
 3422 bool Node::updateHardwareComponents(PollerInfo *poller, uint32_t requestId)
 3423 {
 3424    if (!(m_capabilities & NC_IS_NATIVE_AGENT))
 3425       return false;
 3426 
 3427    poller->setStatus(_T("hardware check"));
 3428    sendPollerMsg(_T("Reading list of installed hardware components\r\n"));
 3429 
 3430    ObjectArray<HardwareComponent> *components = new ObjectArray<HardwareComponent>(16, 16, Ownership::True);
 3431    int readCount = ReadBaseboardInformation(this, components);
 3432    readCount += ReadHardwareInformation(this, components, HWC_PROCESSOR, _T("Hardware.Processors"));
 3433    readCount += ReadHardwareInformation(this, components, HWC_MEMORY, _T("Hardware.MemoryDevices"));
 3434    readCount += ReadHardwareInformation(this, components, HWC_STORAGE, _T("Hardware.StorageDevices"));
 3435    readCount += ReadHardwareInformation(this, components, HWC_BATTERY, _T("Hardware.Batteries"));
 3436    readCount += ReadHardwareInformation(this, components, HWC_NETWORK_ADAPTER, _T("Hardware.NetworkAdapters"));
 3437 
 3438    if (readCount == 0)
 3439    {
 3440       sendPollerMsg(POLLER_WARNING _T("Cannot read hardware component information\r\n"));
 3441       delete components;
 3442       return false;
 3443    }
 3444    sendPollerMsg(POLLER_INFO _T("Received information on %d hardware components\r\n"), components->size());
 3445 
 3446    static const TCHAR *eventParamNames[] =
 3447             { _T("category"), _T("type"), _T("vendor"), _T("model"),
 3448               _T("location"), _T("partNumber"), _T("serialNumber"),
 3449               _T("capacity"), _T("description") };
 3450    static const TCHAR *categoryNames[] = { _T("Other"), _T("Baseboard"), _T("Processor"), _T("Memory device"), _T("Storage device"), _T("Battery"), _T("Network adapter") };
 3451 
 3452    lockProperties();
 3453    if (m_hardwareComponents != nullptr)
 3454    {
 3455       ObjectArray<HardwareComponent> *changes = CalculateHardwareChanges(m_hardwareComponents, components);
 3456       for(int i = 0; i < changes->size(); i++)
 3457       {
 3458          HardwareComponent *c = changes->get(i);
 3459          switch(c->getChangeCode())
 3460          {
 3461             case CHANGE_NONE:
 3462             case CHANGE_UPDATED: // should not be set for hardware component
 3463                break;
 3464             case CHANGE_ADDED:
 3465                nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): new hardware component \"%s %s (%s)\" serial number %s added"),
 3466                         m_name, categoryNames[c->getCategory()], c->getModel(), c->getType(), c->getSerialNumber());
 3467                sendPollerMsg(_T("   %s %s (%s) added, serial number %s\r\n"),
 3468                         categoryNames[c->getCategory()], c->getModel(), c->getType(), c->getSerialNumber());
 3469                PostSystemEventWithNames(EVENT_HARDWARE_COMPONENT_ADDED, m_id, "sssssssDs", eventParamNames, categoryNames[c->getCategory()],
 3470                         c->getType(), c->getVendor(), c->getModel(), c->getLocation(), c->getPartNumber(),
 3471                         c->getSerialNumber(), c->getCapacity(), c->getDescription());
 3472                break;
 3473             case CHANGE_REMOVED:
 3474                nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): hardware component \"%s %s (%s)\" serial number %s removed"),
 3475                         m_name, categoryNames[c->getCategory()], c->getModel(), c->getType(), c->getSerialNumber());
 3476                sendPollerMsg(_T("   %s %s (%s) removed, serial number %s\r\n"),
 3477                         categoryNames[c->getCategory()], c->getModel(), c->getType(), c->getSerialNumber());
 3478                PostSystemEventWithNames(EVENT_HARDWARE_COMPONENT_REMOVED, m_id, "sssssssDs", eventParamNames, categoryNames[c->getCategory()],
 3479                         c->getType(), c->getVendor(), c->getModel(), c->getLocation(), c->getPartNumber(),
 3480                         c->getSerialNumber(), c->getCapacity(), c->getDescription());
 3481                break;
 3482          }
 3483       }
 3484       delete changes;
 3485       delete m_hardwareComponents;
 3486       setModified(MODIFY_HARDWARE_INVENTORY);
 3487    }
 3488    else if (components != nullptr)
 3489       setModified(MODIFY_HARDWARE_INVENTORY);
 3490 
 3491    m_hardwareComponents = components;
 3492    unlockProperties();
 3493    return true;
 3494 }
 3495 
 3496 /**
 3497  * Update list of software packages for node
 3498  */
 3499 bool Node::updateSoftwarePackages(PollerInfo *poller, uint32_t requestId)
 3500 {
 3501    static const TCHAR *eventParamNames[] = { _T("name"), _T("version"), _T("previousVersion") };
 3502 
 3503    if (!(m_capabilities & NC_IS_NATIVE_AGENT))
 3504       return false;
 3505 
 3506    poller->setStatus(_T("software check"));
 3507    sendPollerMsg(_T("Reading list of installed software packages\r\n"));
 3508 
 3509    Table *table;
 3510    if (getTableFromAgent(_T("System.InstalledProducts"), &table) != DCE_SUCCESS)
 3511    {
 3512       sendPollerMsg(POLLER_WARNING _T("Unable to get information about installed software packages\r\n"));
 3513       return false;
 3514    }
 3515 
 3516    ObjectArray<SoftwarePackage> *packages = new ObjectArray<SoftwarePackage>(table->getNumRows(), 16, Ownership::True);
 3517    for(int i = 0; i < table->getNumRows(); i++)
 3518    {
 3519       SoftwarePackage *pkg = SoftwarePackage::createFromTableRow(table, i);
 3520       if (pkg != nullptr)
 3521          packages->add(pkg);
 3522    }
 3523    packages->sort(PackageNameVersionComparator);
 3524    for(int i = 0; i < packages->size(); i++)
 3525    {
 3526       SoftwarePackage *curr = packages->get(i);
 3527       for(int j = i + 1; j < packages->size(); j++)
 3528       {
 3529          SoftwarePackage *next = packages->get(j);
 3530          if (_tcscmp(curr->getName(), next->getName()))
 3531             break;
 3532          if (!_tcscmp(curr->getVersion(), next->getVersion()))
 3533          {
 3534             nxlog_write_tag(NXLOG_WARNING, DEBUG_TAG_CONF_POLL, _T("Inconsistent software package data received from node %s [%u]: duplicate entry for package %s version %s"),
 3535                      m_name, m_id, curr->getName(), curr->getVersion());
 3536             packages->remove(j);
 3537             j--;
 3538          }
 3539       }
 3540    }
 3541    delete table;
 3542    sendPollerMsg(POLLER_INFO _T("Got information about %d installed software packages\r\n"), packages->size());
 3543 
 3544    lockProperties();
 3545    if (m_softwarePackages != nullptr)
 3546    {
 3547       ObjectArray<SoftwarePackage> *changes = CalculatePackageChanges(m_softwarePackages, packages);
 3548       for(int i = 0; i < changes->size(); i++)
 3549       {
 3550          SoftwarePackage *p = changes->get(i);
 3551          switch(p->getChangeCode())
 3552          {
 3553             case CHANGE_NONE:
 3554                break;
 3555             case CHANGE_ADDED:
 3556                nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): new package %s version %s"), m_name, p->getName(), p->getVersion());
 3557                sendPollerMsg(_T("   New package %s version %s\r\n"), p->getName(), p->getVersion());
 3558                PostSystemEventWithNames(EVENT_PACKAGE_INSTALLED, m_id, "ss", eventParamNames, p->getName(), p->getVersion());
 3559                break;
 3560             case CHANGE_REMOVED:
 3561                nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): package %s version %s removed"), m_name, p->getName(), p->getVersion());
 3562                sendPollerMsg(_T("   Package %s version %s removed\r\n"), p->getName(), p->getVersion());
 3563                PostSystemEventWithNames(EVENT_PACKAGE_REMOVED, m_id, "ss", eventParamNames, p->getName(), p->getVersion());
 3564                break;
 3565             case CHANGE_UPDATED:
 3566                SoftwarePackage *prev = changes->get(++i);   // next entry contains previous package version
 3567                nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): package %s updated (%s -> %s)"), m_name, p->getName(), prev->getVersion(), p->getVersion());
 3568                sendPollerMsg(_T("   Package %s updated (%s -> %s)\r\n"), p->getName(), prev->getVersion(), p->getVersion());
 3569                PostSystemEventWithNames(EVENT_PACKAGE_UPDATED, m_id, "sss", eventParamNames, p->getName(), p->getVersion(), prev->getVersion());
 3570                break;
 3571          }
 3572       }
 3573       delete changes;
 3574       delete m_softwarePackages;
 3575       setModified(MODIFY_SOFTWARE_INVENTORY);
 3576    }
 3577    else if (packages != nullptr)
 3578    {
 3579       setModified(MODIFY_SOFTWARE_INVENTORY);
 3580    }
 3581 
 3582    m_softwarePackages = packages;
 3583    unlockProperties();
 3584    return true;
 3585 }
 3586 
 3587 /**
 3588  * Data for DeleteDuplicateNode
 3589  */
 3590 struct DeleteDuplicateNodeData
 3591 {
 3592    shared_ptr<Node> originalNode;
 3593    shared_ptr<Node> duplicateNode;
 3594    TCHAR *reason;
 3595 
 3596    DeleteDuplicateNodeData(shared_ptr<Node> original, shared_ptr<Node> duplicate, const TCHAR *_reason) : originalNode(original), duplicateNode(duplicate)
 3597    {
 3598       reason = MemCopyString(_reason);
 3599    }
 3600 
 3601    ~DeleteDuplicateNodeData()
 3602    {
 3603       MemFree(reason);
 3604    }
 3605 };
 3606 
 3607 /**
 3608  * Background worker for duplicate node delete
 3609  */
 3610 static void DeleteDuplicateNode(DeleteDuplicateNodeData *data)
 3611 {
 3612    PostSystemEvent(EVENT_DUPLICATE_NODE_DELETED, g_dwMgmtNode, "dssdsss",
 3613             data->originalNode->getId(), data->originalNode->getName(), data->originalNode->getPrimaryHostName().cstr(),
 3614             data->duplicateNode->getId(), data->duplicateNode->getName(), data->duplicateNode->getPrimaryHostName().cstr(),
 3615             data->reason);
 3616    data->duplicateNode->deleteObject();
 3617    // Calling updateObjectIndexes will update all indexes that could be broken
 3618    // by deleting duplicate IP address entries
 3619    data->originalNode->updateObjectIndexes();
 3620    delete data;
 3621 }
 3622 
 3623 /**
 3624  * Perform configuration poll on node
 3625  */
 3626 void Node::configurationPoll(PollerInfo *poller, ClientSession *session, UINT32 rqId)
 3627 {
 3628    lockProperties();
 3629    if (m_isDeleteInitiated || IsShutdownInProgress())
 3630    {
 3631       m_configurationPollState.complete(0);
 3632       unlockProperties();
 3633       return;
 3634    }
 3635    unlockProperties();
 3636 
 3637    poller->setStatus(_T("wait for lock"));
 3638    pollerLock(configuration);
 3639 
 3640    POLL_CANCELLATION_CHECKPOINT();
 3641 
 3642    m_pollRequestor = session;
 3643    m_pollRequestId = rqId;
 3644 
 3645    if (m_status == STATUS_UNMANAGED)
 3646    {
 3647       sendPollerMsg(POLLER_WARNING _T("Node %s is unmanaged, configuration poll aborted\r\n"), m_name);
 3648       nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("Node %s [%u] is unmanaged, configuration poll aborted"), m_name, m_id);
 3649       pollerUnlock();
 3650       return;
 3651    }
 3652 
 3653    uint32_t oldCapabilities = m_capabilities;
 3654    uint32_t modified = 0;
 3655 
 3656    sendPollerMsg(_T("Starting configuration poll for node %s\r\n"), m_name);
 3657    nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 4, _T("Starting configuration poll for node %s (ID: %d)"), m_name, m_id);
 3658 
 3659    // Check for forced capabilities recheck
 3660    if (m_runtimeFlags & NDF_RECHECK_CAPABILITIES)
 3661    {
 3662       sendPollerMsg(POLLER_WARNING _T("Capability reset\r\n"));
 3663       lockProperties();
 3664       m_capabilities &= NC_IS_LOCAL_MGMT; // reset all except "local management" flag
 3665       m_runtimeFlags &= ~ODF_CONFIGURATION_POLL_PASSED;
 3666       MemFreeAndNull(m_snmpObjectId);
 3667       m_platformName[0] = 0;
 3668       m_agentVersion[0] = 0;
 3669       MemFreeAndNull(m_sysDescription);
 3670       MemFreeAndNull(m_sysName);
 3671       MemFreeAndNull(m_sysContact);
 3672       MemFreeAndNull(m_sysLocation);
 3673       MemFreeAndNull(m_lldpNodeId);
 3674       m_hypervisorType[0] = 0;
 3675       m_hypervisorInfo = nullptr;
 3676       m_vendor = nullptr;
 3677       m_productCode = nullptr;
 3678       m_productName = nullptr;
 3679       m_productVersion = nullptr;
 3680       m_serialNumber = nullptr;
 3681       unlockProperties();
 3682    }
 3683 
 3684    // Check if node is marked as unreachable
 3685    if ((m_state & DCSF_UNREACHABLE) && !(m_runtimeFlags & NDF_RECHECK_CAPABILITIES))
 3686    {
 3687       sendPollerMsg(POLLER_WARNING _T("Node is marked as unreachable, configuration poll aborted\r\n"));
 3688       nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 4, _T("Node is marked as unreachable, configuration poll aborted"));
 3689    }
 3690    else
 3691    {
 3692       updatePrimaryIpAddr();
 3693 
 3694       poller->setStatus(_T("capability check"));
 3695       sendPollerMsg(_T("Checking node's capabilities...\r\n"));
 3696 
 3697       if (confPollAgent(rqId))
 3698          modified |= MODIFY_NODE_PROPERTIES;
 3699 
 3700       if ((oldCapabilities & NC_IS_NATIVE_AGENT) && !(m_capabilities & NC_IS_NATIVE_AGENT))
 3701          m_lastAgentCommTime = TIMESTAMP_NEVER;
 3702 
 3703       POLL_CANCELLATION_CHECKPOINT();
 3704 
 3705       if (confPollSnmp(rqId))
 3706          modified |= MODIFY_NODE_PROPERTIES;
 3707 
 3708       POLL_CANCELLATION_CHECKPOINT();
 3709 
 3710       if (confPollEthernetIP(rqId))
 3711          modified |= MODIFY_NODE_PROPERTIES;
 3712 
 3713       POLL_CANCELLATION_CHECKPOINT();
 3714 
 3715       // Generate event if node flags has been changed
 3716       if (oldCapabilities != m_capabilities)
 3717       {
 3718          PostSystemEvent(EVENT_NODE_CAPABILITIES_CHANGED, m_id, "xx", oldCapabilities, m_capabilities);
 3719          modified |= MODIFY_NODE_PROPERTIES;
 3720       }
 3721 
 3722       // Update system description
 3723       TCHAR buffer[MAX_RESULT_LENGTH] = { 0 };
 3724       if ((m_capabilities & NC_IS_NATIVE_AGENT) && !(m_flags & NF_DISABLE_NXCP))
 3725       {
 3726          getMetricFromAgent(_T("System.Uname"), buffer, MAX_RESULT_LENGTH);
 3727       }
 3728       else if ((m_capabilities & NC_IS_SNMP) && !(m_flags & NF_DISABLE_SNMP))
 3729       {
 3730          getMetricFromSNMP(m_snmpPort, SNMP_VERSION_DEFAULT, _T(".1.3.6.1.2.1.1.1.0"), buffer, MAX_RESULT_LENGTH, SNMP_RAWTYPE_NONE);
 3731       }
 3732 
 3733       if (buffer[0] != _T('\0'))
 3734       {
 3735          TranslateStr(buffer, _T("\r\n"), _T(" "));
 3736          TranslateStr(buffer, _T("\n"), _T(" "));
 3737          TranslateStr(buffer, _T("\r"), _T(" "));
 3738 
 3739          lockProperties();
 3740 
 3741          if ((m_sysDescription == nullptr) || _tcscmp(m_sysDescription, buffer))
 3742          {
 3743             MemFree(m_sysDescription);
 3744             m_sysDescription = MemCopyString(buffer);
 3745             modified |= MODIFY_NODE_PROPERTIES;
 3746             sendPollerMsg(_T("   System description changed to %s\r\n"), m_sysDescription);
 3747          }
 3748 
 3749          unlockProperties();
 3750       }
 3751 
 3752       // Retrieve interface list
 3753       poller->setStatus(_T("interface check"));
 3754       sendPollerMsg(_T("Capability check finished\r\n"));
 3755 
 3756       if (updateInterfaceConfiguration(rqId, 0)) // maskBits
 3757          modified |= MODIFY_NODE_PROPERTIES;
 3758 
 3759       POLL_CANCELLATION_CHECKPOINT();
 3760 
 3761       if (g_flags & AF_MERGE_DUPLICATE_NODES)
 3762       {
 3763          shared_ptr<Node> duplicateNode;
 3764          TCHAR reason[1024];
 3765          DuplicateCheckResult dcr = checkForDuplicates(&duplicateNode, reason, 1024);
 3766          if (dcr == REMOVE_THIS)
 3767          {
 3768             nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 3, _T("Removing node %s [%u] as duplicate"), m_name, m_id);
 3769 
 3770             poller->setStatus(_T("cleanup"));
 3771             lockProperties();
 3772             m_runtimeFlags &= ~(ODF_CONFIGURATION_POLL_PENDING | NDF_RECHECK_CAPABILITIES);
 3773             unlockProperties();
 3774             pollerUnlock();
 3775 
 3776             duplicateNode->reconcileWithDuplicateNode(this);
 3777             nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 4, _T("Aborted configuration poll for node %s (ID: %d)"), m_name, m_id);
 3778             ThreadPoolExecute(g_pollerThreadPool, DeleteDuplicateNode, new DeleteDuplicateNodeData(duplicateNode, self(), reason));
 3779             return;
 3780          }
 3781          else if (dcr == REMOVE_OTHER)
 3782          {
 3783             nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 3, _T("Removing node %s [%u] as duplicate"), duplicateNode->getName(), duplicateNode->getId());
 3784             reconcileWithDuplicateNode(duplicateNode.get());
 3785             ThreadPoolExecute(g_pollerThreadPool, DeleteDuplicateNode, new DeleteDuplicateNodeData(self(), duplicateNode, reason));
 3786          }
 3787       }
 3788 
 3789       // Check node name
 3790       sendPollerMsg(_T("Checking node name\r\n"));
 3791       if (g_flags & AF_RESOLVE_NODE_NAMES)
 3792       {
 3793          InetAddress addr = InetAddress::parse(m_name);
 3794          if (addr.isValidUnicast() && isMyIP(addr))
 3795          {
 3796             nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 6, _T("ConfPoll(%s [%u]): node name is an IP address and need to be resolved"), m_name, m_id);
 3797             sendPollerMsg(_T("Node name is an IP address and need to be resolved\r\n"));
 3798             poller->setStatus(_T("resolving name"));
 3799             if (resolveName(false))
 3800             {
 3801                sendPollerMsg(POLLER_INFO _T("Node name resolved to %s\r\n"), m_name);
 3802                nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 4, _T("ConfPoll(%s [%u]): node name resolved"), m_name, m_id);
 3803                modified |= MODIFY_COMMON_PROPERTIES;
 3804             }
 3805             else
 3806             {
 3807                sendPollerMsg(POLLER_WARNING _T("Node name cannot be resolved\r\n"));
 3808                nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 4, _T("ConfPoll(%s [%u]): node name cannot be resolved"), m_name, m_id);
 3809             }
 3810          }
 3811          else
 3812          {
 3813             nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 6, _T("ConfPoll(%s [%u]): node name cannot be interpreted as valid IP address"), m_name, m_id);
 3814             sendPollerMsg(_T("Node name %s, no need to resolve to host name\r\n"), addr.isValidUnicast() ? _T("is a valid IP address but cannot be found in interface configuration") : _T("cannot be interpreted as valid IP address"));
 3815          }
 3816       }
 3817       else if (g_flags & AF_SYNC_NODE_NAMES_WITH_DNS)
 3818       {
 3819          sendPollerMsg(_T("Synchronizing node name with DNS\r\n"));
 3820          nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 6, _T("ConfPoll(%s [%u]): synchronizing node name with DNS"), m_name, m_id);
 3821          poller->setStatus(_T("resolving name"));
 3822          if (resolveName(TRUE))
 3823          {
 3824             sendPollerMsg(POLLER_INFO _T("Node name resolved to %s\r\n"), m_name);
 3825             nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 4, _T("ConfPoll(%s [%u]): node name resolved"), m_name, m_id);
 3826             modified |= MODIFY_COMMON_PROPERTIES;
 3827          }
 3828       }
 3829       else
 3830       {
 3831          sendPollerMsg(_T("Node name is OK\r\n"));
 3832       }
 3833 
 3834       POLL_CANCELLATION_CHECKPOINT();
 3835 
 3836       // Setup permanent connection to agent if not present (needed for proper configuration re-sync)
 3837       if (m_capabilities & NC_IS_NATIVE_AGENT)
 3838       {
 3839          agentLock();
 3840          connectToAgent();
 3841          agentUnlock();
 3842       }
 3843 
 3844       POLL_CANCELLATION_CHECKPOINT();
 3845 
 3846       updateSystemHardwareInformation(poller, rqId);
 3847       updateHardwareComponents(poller, rqId);
 3848       updateSoftwarePackages(poller, rqId);
 3849 
 3850       POLL_CANCELLATION_CHECKPOINT();
 3851 
 3852       // Update node type
 3853       TCHAR hypervisorType[MAX_HYPERVISOR_TYPE_LENGTH], hypervisorInfo[MAX_HYPERVISOR_INFO_LENGTH];
 3854       NodeType type = detectNodeType(hypervisorType, hypervisorInfo);
 3855       nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): detected node type: %d (%s)"), m_name, type, typeName(type));
 3856       if ((type == NODE_TYPE_VIRTUAL) || (type == NODE_TYPE_CONTAINER))
 3857       {
 3858          nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): hypervisor: %s (%s)"), m_name, hypervisorType, hypervisorInfo);
 3859       }
 3860       lockProperties();
 3861       if ((type != NODE_TYPE_UNKNOWN) &&
 3862                ((type != m_type) || _tcscmp(hypervisorType, m_hypervisorType) || _tcscmp(hypervisorInfo, CHECK_NULL_EX(m_hypervisorInfo))))
 3863       {
 3864          m_type = type;
 3865          modified |= MODIFY_NODE_PROPERTIES;
 3866          nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): node type set to %d (%s)"), m_name, type, typeName(type));
 3867          sendPollerMsg(_T("   Node type changed to %s\r\n"), typeName(type));
 3868 
 3869          if (*hypervisorType != 0)
 3870             sendPollerMsg(_T("   Hypervisor type set to %s\r\n"), hypervisorType);
 3871          _tcslcpy(m_hypervisorType, hypervisorType, MAX_HYPERVISOR_TYPE_LENGTH);
 3872 
 3873          if (*hypervisorInfo != 0)
 3874             sendPollerMsg(_T("   Hypervisor information set to %s\r\n"), hypervisorInfo);
 3875          m_hypervisorInfo = hypervisorInfo;
 3876       }
 3877       unlockProperties();
 3878 
 3879       POLL_CANCELLATION_CHECKPOINT();
 3880 
 3881       // Call hooks in loaded modules
 3882       ENUMERATE_MODULES(pfConfPollHook)
 3883       {
 3884          nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfigurationPoll(%s [%d]): calling hook in module %s"), m_name, m_id, CURRENT_MODULE.szName);
 3885          if (CURRENT_MODULE.pfConfPollHook(this, session, rqId, poller))
 3886             modified |= MODIFY_ALL;   // FIXME: change module call to get exact modifications
 3887       }
 3888 
 3889       POLL_CANCELLATION_CHECKPOINT();
 3890 
 3891       // Execute hook script
 3892       poller->setStatus(_T("hook"));
 3893       executeHookScript(_T("ConfigurationPoll"), rqId);
 3894 
 3895       POLL_CANCELLATION_CHECKPOINT();
 3896 
 3897       poller->setStatus(_T("autobind"));
 3898       applyTemplates();
 3899       updateContainerMembership();
 3900       updateClusterMembership();
 3901 
 3902       sendPollerMsg(_T("Finished configuration poll for node %s\r\n"), m_name);
 3903       sendPollerMsg(_T("Node configuration was%schanged after poll\r\n"), (modified != 0) ? _T(" ") : _T(" not "));
 3904 
 3905       m_runtimeFlags &= ~ODF_CONFIGURATION_POLL_PENDING;
 3906       m_runtimeFlags |= ODF_CONFIGURATION_POLL_PASSED;
 3907    }
 3908 
 3909    // Finish configuration poll
 3910    poller->setStatus(_T("cleanup"));
 3911    lockProperties();
 3912    m_runtimeFlags &= ~NDF_RECHECK_CAPABILITIES;
 3913    unlockProperties();
 3914    pollerUnlock();
 3915    nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 4, _T("Finished configuration poll for node %s (ID: %d)"), m_name, m_id);
 3916 
 3917    if (modified != 0)
 3918    {
 3919       markAsModified(modified);
 3920    }
 3921 }
 3922 
 3923 /**
 3924  * Check if agent on given node is restarting
 3925  */
 3926 static inline bool IsProxyAgentRestarting(uint32_t proxyId, time_t now)
 3927 {
 3928    if (proxyId == 0)
 3929       return false;
 3930    shared_ptr<NetObj> proxyNode = FindObjectById(proxyId, OBJECT_NODE);
 3931    return (proxyNode != nullptr) && (static_cast<Node&>(*proxyNode).getAgentRestartTime() + g_agentRestartWaitTime >= now);
 3932 }
 3933 
 3934 /**
 3935  * Check if any of the agent's proxies can still be in process of restarting
 3936  */
 3937 bool Node::isProxyAgentRestarting()
 3938 {
 3939    time_t now = time(nullptr);
 3940    return IsProxyAgentRestarting(getEffectiveAgentProxy(), now) ||
 3941           IsProxyAgentRestarting(getEffectiveSnmpProxy(), now) ||
 3942           IsProxyAgentRestarting(getEffectiveEtherNetIPProxy(), now) ||
 3943           IsProxyAgentRestarting(getEffectiveIcmpProxy(), now);
 3944 }
 3945 
 3946 /**
 3947  * Filter node index by zone UIN
 3948  */
 3949 static bool FilterByZone(NetObj *object, void *zoneUIN)
 3950 {
 3951    return static_cast<Node*>(object)->getZoneUIN() == CAST_FROM_POINTER(zoneUIN, int32_t);
 3952 }
 3953 
 3954 /**
 3955  * Check for duplicate nodes
 3956  */
 3957 DuplicateCheckResult Node::checkForDuplicates(shared_ptr<Node> *duplicate, TCHAR *reason, size_t size)
 3958 {
 3959    DuplicateCheckResult result = NO_DUPLICATES;
 3960    SharedObjectArray<NetObj> *nodes = g_idxNodeById.getObjects(FilterByZone, CAST_TO_POINTER(m_zoneUIN, void*));
 3961    int i;
 3962    for(i = 0; i < nodes->size(); i++)
 3963    {
 3964       Node *node = static_cast<Node*>(nodes->get(i));
 3965       if (node->m_id == m_id)
 3966          continue;
 3967 
 3968       if (isDuplicateOf(node, reason, 1024))
 3969       {
 3970          nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 3, _T("Node %s [%u] is a duplicate of node %s [%u]"),
 3971                   m_name, m_id, node->getName(), node->getId());
 3972 
 3973          if ((node->m_status == STATUS_UNMANAGED) || (node->m_state & DCSF_UNREACHABLE))
 3974          {
 3975             result = REMOVE_OTHER;
 3976          }
 3977          else if (node->isNativeAgent() && !isNativeAgent())
 3978          {
 3979             result = REMOVE_THIS;
 3980          }
 3981          else if (!node->isNativeAgent() && isNativeAgent())
 3982          {
 3983             result = REMOVE_OTHER;
 3984          }
 3985          else if (node->isSNMPSupported() && !isSNMPSupported())
 3986          {
 3987             result = REMOVE_THIS;
 3988          }
 3989          else if (!node->isSNMPSupported() && isSNMPSupported())
 3990          {
 3991             result = REMOVE_OTHER;
 3992          }
 3993          else if (node->m_id > m_id)
 3994          {
 3995             result = REMOVE_OTHER;
 3996          }
 3997          else
 3998          {
 3999             result = REMOVE_THIS;
 4000          }
 4001          *duplicate = node->self();
 4002          break;
 4003       }
 4004    }
 4005    delete nodes;
 4006    return result;
 4007 }
 4008 
 4009 /**
 4010  * Check if this node is a duplicate of given node
 4011  */
 4012 bool Node::isDuplicateOf(Node *node, TCHAR *reason, size_t size)
 4013 {
 4014    // Check if primary IP is on one of other node's interfaces
 4015    if (!(m_flags & NF_EXTERNAL_GATEWAY) && m_ipAddress.isValidUnicast() &&
 4016        !(node->getFlags() &  NF_EXTERNAL_GATEWAY) && node->getIpAddress().isValidUnicast())
 4017    {
 4018       shared_ptr<Interface> iface = node->findInterfaceByIP(m_ipAddress);
 4019       if (iface != nullptr)
 4020       {
 4021          _sntprintf(reason, size, _T("Primary IP address %s of node %s [%u] found on interface %s of node %s [%u]"),
 4022                   (const TCHAR *)m_ipAddress.toString(), m_name, m_id, iface->getName(), node->getName(), node->getId());
 4023          nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 3, _T("%s"), reason);
 4024          return true;
 4025       }
 4026    }
 4027 
 4028    // Check for exact match of interface list
 4029    //TODO
 4030 
 4031    return false;
 4032 }
 4033 
 4034 /**
 4035  * Reconcile with duplicate node.
 4036  *
 4037  * @param node Pointer to duplicate node about to be deleted.
 4038  */
 4039 void Node::reconcileWithDuplicateNode(Node *node)
 4040 {
 4041    // Copy all non-template DCIs
 4042    node->readLockDciAccess();
 4043    writeLockDciAccess();
 4044 
 4045    for(int i = 0; i < node->m_dcObjects->size(); i++)
 4046    {
 4047       DCObject *dci = node->m_dcObjects->get(i);
 4048       if (dci->getTemplateId() != 0)
 4049          continue;
 4050 
 4051       // Check if this node already have same DCI
 4052       bool found = false;
 4053       for(int j = 0; j < m_dcObjects->size(); j++)
 4054       {
 4055          DCObject *curr = m_dcObjects->get(j);
 4056          if ((curr->getDataSource() == dci->getDataSource()) &&
 4057              (curr->getSourceNode() == dci->getSourceNode()) &&
 4058              !_tcsicmp(curr->getName(), dci->getName()))
 4059          {
 4060             found = true;
 4061             break;
 4062          }
 4063       }
 4064 
 4065       if (!found)
 4066       {
 4067          nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("Creating copy of DCI \"%s\" [%u] from node %s [%u] on node %s [%u]"),
 4068                   dci->getName().cstr(), dci->getId(), node->m_name, node->m_id, m_name, m_id);
 4069 
 4070          DCObject *dciCopy = dci->clone();
 4071          dciCopy->changeBinding(CreateUniqueId(IDG_ITEM), self(), false);
 4072          addDCObject(dciCopy, true);
 4073       }
 4074    }
 4075 
 4076    unlockDciAccess();
 4077    node->unlockDciAccess();
 4078 
 4079    // Apply all manual templates from duplicate node
 4080    node->readLockParentList();
 4081    for(int i = 0; i < node->getParentList().size(); i++)
 4082    {
 4083       NetObj *object = node->getParentList().get(i);
 4084       if (object->getObjectClass() != OBJECT_TEMPLATE)
 4085          continue;
 4086 
 4087       if (static_cast<Template*>(object)->isAutoBindEnabled())
 4088          continue;
 4089 
 4090       if (!object->isDirectChild(m_id))
 4091       {
 4092          g_templateUpdateQueue.put(new TemplateUpdateTask(static_pointer_cast<DataCollectionOwner>(object->self()), m_id, APPLY_TEMPLATE, false));
 4093       }
 4094    }
 4095    node->unlockParentList();
 4096 }
 4097 
 4098 /**
 4099  * Detect node type
 4100  */
 4101 NodeType Node::detectNodeType(TCHAR *hypervisorType, TCHAR *hypervisorInfo)
 4102 {
 4103    NodeType type = NODE_TYPE_UNKNOWN;
 4104    *hypervisorType = 0;
 4105    *hypervisorInfo = 0;
 4106 
 4107    if (m_capabilities & NC_IS_SNMP)
 4108    {
 4109       nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 6, _T("Node::detectNodeType(%s [%d]): SNMP node, driver name is %s"),
 4110                m_name, m_id, (m_driver != nullptr) ? m_driver->getName() : _T("(not set)"));
 4111 
 4112       bool vtypeReportedByDevice = false;
 4113       if (m_driver != nullptr)
 4114       {
 4115          SNMP_Transport *snmp = createSnmpTransport();
 4116          if (snmp != nullptr)
 4117          {
 4118             VirtualizationType vt;
 4119             vtypeReportedByDevice = m_driver->getVirtualizationType(snmp, this, m_driverData, &vt);
 4120             delete snmp;
 4121             if (vtypeReportedByDevice)
 4122             {
 4123                if (vt != VTYPE_NONE)
 4124                   type = (vt == VTYPE_FULL) ? NODE_TYPE_VIRTUAL : NODE_TYPE_CONTAINER;
 4125                else
 4126                  type = NODE_TYPE_PHYSICAL;
 4127             }
 4128          }
 4129       }
 4130 
 4131       if (!vtypeReportedByDevice)
 4132       {
 4133          // Assume physical device if it supports SNMP and driver is not "GENERIC" nor "NET-SNMP"
 4134          if ((m_driver != nullptr) && _tcscmp(m_driver->getName(), _T("GENERIC")) && _tcscmp(m_driver->getName(), _T("NET-SNMP")))
 4135          {
 4136             type = NODE_TYPE_PHYSICAL;
 4137          }
 4138          else
 4139          {
 4140             if (m_capabilities & NC_IS_PRINTER)
 4141             {
 4142                // Assume that printers are physical devices
 4143                type = NODE_TYPE_PHYSICAL;
 4144             }
 4145          }
 4146       }
 4147    }
 4148    if (m_capabilities & NC_IS_NATIVE_AGENT)
 4149    {
 4150       nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 6, _T("Node::detectNodeType(%s [%d]): NetXMS agent node"), m_name, m_id);
 4151 
 4152       shared_ptr<AgentConnectionEx> conn = getAgentConnection();
 4153       if (conn != nullptr)
 4154       {
 4155          TCHAR buffer[MAX_RESULT_LENGTH];
 4156          if (conn->getParameter(_T("System.IsVirtual"), buffer, MAX_RESULT_LENGTH) == ERR_SUCCESS)
 4157          {
 4158             VirtualizationType vt = static_cast<VirtualizationType>(_tcstol(buffer, nullptr, 10));
 4159             if (vt != VTYPE_NONE)
 4160             {
 4161                type = (vt == VTYPE_FULL) ? NODE_TYPE_VIRTUAL : NODE_TYPE_CONTAINER;
 4162                if (conn->getParameter(_T("Hypervisor.Type"), buffer, MAX_RESULT_LENGTH) == ERR_SUCCESS)
 4163                {
 4164                   _tcslcpy(hypervisorType, buffer, MAX_HYPERVISOR_TYPE_LENGTH);
 4165                   if (conn->getParameter(_T("Hypervisor.Version"), buffer, MAX_RESULT_LENGTH) == ERR_SUCCESS)
 4166                   {
 4167                      _tcslcpy(hypervisorInfo, buffer, MAX_HYPERVISOR_INFO_LENGTH);
 4168                   }
 4169                }
 4170             }
 4171             else
 4172             {
 4173                type = NODE_TYPE_PHYSICAL;
 4174             }
 4175          }
 4176       }
 4177    }
 4178    return type;
 4179 }
 4180 
 4181 /**
 4182  * Configuration poll: check for NetXMS agent
 4183  */
 4184 bool Node::confPollAgent(UINT32 rqId)
 4185 {
 4186    nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): checking for NetXMS agent Flags={%08X} StateFlags={%08X} RuntimeFlags={%08X}"), m_name, m_flags, m_state, m_runtimeFlags);
 4187    if (((m_capabilities & NC_IS_NATIVE_AGENT) && (m_state & NSF_AGENT_UNREACHABLE)) || (m_flags & NF_DISABLE_NXCP))
 4188    {
 4189       sendPollerMsg(_T("   NetXMS agent polling is %s\r\n"), (m_flags & NF_DISABLE_NXCP) ? _T("disabled") : _T("not possible"));
 4190       return false;
 4191    }
 4192 
 4193    bool hasChanges = false;
 4194 
 4195    sendPollerMsg(_T("   Checking NetXMS agent...\r\n"));
 4196    shared_ptr<AgentConnectionEx> pAgentConn;
 4197    shared_ptr<AgentTunnel> tunnel = GetTunnelForNode(m_id);
 4198    if (tunnel != nullptr)
 4199    {
 4200       pAgentConn = AgentConnectionEx::create(m_id, tunnel, m_agentSecret, isAgentCompressionAllowed());
 4201    }
 4202    else
 4203    {
 4204       if (m_flags & NF_AGENT_OVER_TUNNEL_ONLY)
 4205       {
 4206          sendPollerMsg(POLLER_ERROR _T("   Direct agent connection is disabled and there are no active tunnels\r\n"));
 4207          nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): direct agent connection is disabled and there are no active tunnels"), m_name);
 4208          return false;
 4209       }
 4210       if (!m_ipAddress.isValidUnicast() && !((m_capabilities & NC_IS_LOCAL_MGMT) && m_ipAddress.isLoopback()))
 4211       {
 4212          sendPollerMsg(POLLER_ERROR _T("   Node primary IP is invalid and there are no active tunnels\r\n"));
 4213          nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): node primary IP is invalid and there are no active tunnels"), m_name);
 4214          return false;
 4215       }
 4216       pAgentConn = AgentConnectionEx::create(m_id, m_ipAddress, m_agentPort, m_agentSecret, isAgentCompressionAllowed());
 4217       setAgentProxy(pAgentConn.get());
 4218    }
 4219    pAgentConn->setCommandTimeout(g_agentCommandTimeout);
 4220    nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): checking for NetXMS agent - connecting"), m_name);
 4221 
 4222    // Try to connect to agent
 4223    uint32_t rcc;
 4224    if (!pAgentConn->connect(g_pServerKey, &rcc))
 4225    {
 4226       // If there are authentication problem, try default shared secret
 4227       if ((rcc == ERR_AUTH_REQUIRED) || (rcc == ERR_AUTH_FAILED))
 4228       {
 4229          StringList secrets;
 4230          DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
 4231          DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT secret FROM shared_secrets WHERE zone=? OR zone=-1 ORDER BY zone DESC, id ASC"));
 4232          if (hStmt != NULL)
 4233          {
 4234             DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_zoneUIN);
 4235 
 4236             DB_RESULT hResult = DBSelectPrepared(hStmt);
 4237             if (hResult != NULL)
 4238             {
 4239                int count = DBGetNumRows(hResult);
 4240                for(int i = 0; i < count; i++)
 4241                   secrets.addPreallocated(DBGetField(hResult, i, 0, NULL, 0));
 4242                DBFreeResult(hResult);
 4243             }
 4244             DBFreeStatement(hStmt);
 4245          }
 4246          DBConnectionPoolReleaseConnection(hdb);
 4247 
 4248          for(int i = 0; (i < secrets.size()) && !IsShutdownInProgress(); i++)
 4249          {
 4250             pAgentConn->setSharedSecret(secrets.get(i));
 4251             if (pAgentConn->connect(g_pServerKey, &rcc))
 4252             {
 4253                _tcslcpy(m_agentSecret, secrets.get(i), MAX_SECRET_LENGTH);
 4254                nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): checking for NetXMS agent - shared secret changed to system default"), m_name);
 4255                break;
 4256             }
 4257 
 4258             if (((rcc != ERR_AUTH_REQUIRED) || (rcc != ERR_AUTH_FAILED)))
 4259             {
 4260                break;
 4261             }
 4262          }
 4263       }
 4264    }
 4265 
 4266    if (rcc == ERR_SUCCESS)
 4267    {
 4268       nxlog_debug_tag(DEBUG_TAG_CONF_POLL, 5, _T("ConfPoll(%s): checking for NetXMS agent - connected"), m_name);
 4269       lockProperties();
 4270       m_capabilities |= NC_IS_NATIVE_AGENT;
 4271       if (m_state & NSF_AGENT_UNREACHABLE)
 4272       {
 4273          m_state &= ~NSF_AGENT_UNREACHABLE;
 4274          PostSystemEvent(EVENT_AGENT_OK, m_id, nullptr);
 4275          sendPollerMsg(POLLER_INFO _T("   Connectivity with NetXMS agent restored\r\n"));
 4276       }
 4277       else
 4278       {
 4279          sendPollerMsg(POLLER_INFO _T("   NetXMS agent is active\r\n"));
 4280       }
 4281       unlockProperties();
 4282 
 4283       TCHAR buffer[MAX_RESULT_LENGTH];
 4284       if (pAgentConn->getParameter(_T("Agent.Version"), buffer, MAX_AGENT_VERSION_LEN) == ERR_SUCCESS)
 4285       {
 4286          lockProperties();
 4287          if (_tcscmp(m_agentVersion, buffer))
 4288          {
 4289             _tcscpy(m_agentVersion, buffer);
 4290             hasChanges = true;
 4291             sendPollerMsg(_T("   NetXMS agent version changed to %s\r\n"), m_agentVersion);
 4292          }
 4293          unlockProperties();
 4294       }
 4295 
 4296       if (pAgentConn->getParameter(_T("Agent.ID"), buffer, MAX_RESULT_LENGTH) == ERR_SUCCESS)
 4297       {
 4298          uuid agentId = uuid::parse(buffer);
 4299          lockProperties();
 4300          if (!m_agentId.equals(agentId))
 4301          {
 4302             PostSystemEvent(EVENT_AGENT_ID_CHANGED, m_id, "GG", &m_agentId, &agentId);
 4303             m_agentId = agentId;
 4304             hasChanges = true;
 4305             sendPollerMsg(_T("   NetXMS agent ID changed to %s\r\n"), m_agentId.toString(buffer));
 4306          }
 4307          unlockProperties();
 4308       }
 4309 
 4310       if (pAgentConn->getParameter(_T("System.PlatformName"), buffer, MAX_PLATFORM_NAME_LEN) == ERR_SUCCESS)
 4311       {
 4312          lockProperties();
 4313          if (_tcscmp(m_platformName, buffer))
 4314          {
 4315             _tcscpy(m_platformName, buffer);
 4316             hasChanges = true;
 4317             sendPollerMsg(_T("   Platform name changed to %s\r\n"), m_platformName);
 4318          }
 4319          unlockProperties();
 4320       }
 4321 
 4322       if (pAgentConn->getParameter(_T("System.HardwareId"), buffer, MAX_RESULT_LENGTH) == ERR_SUCCESS)
 4323       {
 4324          BYTE hardwareId[HARDWARE_ID_LENGTH];
 4325          StrToBin(buffer, hardwareId, sizeof(hardwareId));
 4326          lockProperties();
 4327          if (!m_hardwareId.equals(hardwareId))
 4328          {
 4329             m_hardwareId = NodeHardwareId(hardwareId);
 4330             hasChanges = true;
 4331             sendPollerMsg(_T("   Hardware ID changed to %s\r\n"), buffer);
 4332          }
 4333          unlockProperties();
 4334       }
 4335 
 4336       // Check IP forwarding status
 4337       if (pAgentConn->getParameter(_T("Net.IP.Forwarding"), buffer, 16) == ERR_SUCCESS)
 4338       {
 4339          if (_tcstoul(buffer, nullptr, 10) != 0)
 4340             m_capabilities |= NC_IS_ROUTER;
 4341          else
 4342             m_capabilities &= ~NC_IS_ROUTER;
 4343       }
 4344 
 4345       // Check for 64 bit counter support.
 4346       // if Net.Interface.64BitCounters not supported by agent then use
 4347       // only presence of 64 bit parameters as indicator
 4348       bool netIf64bitCounters = true;
 4349       if (pAgentConn->getParameter(_T("Net.Interface.64BitCounters"), buffer, MAX_DB_STRING) == ERR_SUCCESS)
 4350       {
 4351          netIf64bitCounters = _tcstol(buffer, nullptr, 10) ? true : false;
 4352       }
 4353 
 4354       ObjectArray<AgentParameterDefinition> *plist;
 4355       ObjectArray<AgentTableDefinition> *tlist;
 4356       uint32_t rcc = pAgentConn->getSupportedParameters(&plist, &tlist);
 4357       if (rcc == ERR_SUCCESS)
 4358       {
 4359          lockProperties();
 4360          delete m_agentParameters;
 4361          delete m_agentTables;
 4362          m_agentParameters = plist;
 4363          m_agentTables = tlist;
 4364 
 4365          // Check for 64-bit interface counters
 4366          m_capabilities &= ~NC_HAS_AGENT_IFXCOUNTERS;
 4367          if (netIf64bitCounters)
 4368          {
 4369             for(int i = 0; i < plist->size(); i++)
 4370             {
 4371                if (!_tcsicmp(plist->get