"Fossies" - the Fresh Open Source Software Archive

Member "netxms-3.8.166/src/server/core/main.cpp" (23 Feb 2021, 50925 Bytes) of package /linux/misc/netxms-3.8.166.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 "main.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.8.120_vs_3.8.166.

    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: main.cpp
   20 **
   21 **/
   22 
   23 #include "nxcore.h"
   24 #include <netxmsdb.h>
   25 #include <netxms_mt.h>
   26 #include <hdlink.h>
   27 #include <agent_tunnel.h>
   28 #include <nxcore_websvc.h>
   29 #include <nxcore_logs.h>
   30 #include <nxcore_ps.h>
   31 #include <netxms-version.h>
   32 
   33 #if !defined(_WIN32) && HAVE_READLINE_READLINE_H && HAVE_READLINE && !defined(UNICODE)
   34 #include <readline/readline.h>
   35 #include <readline/history.h>
   36 #define USE_READLINE 1
   37 #endif
   38 
   39 #ifdef _WIN32
   40 #include <errno.h>
   41 #include <psapi.h>
   42 #include <conio.h>
   43 #else
   44 #include <signal.h>
   45 #include <sys/wait.h>
   46 #endif
   47 
   48 #if WITH_ZMQ
   49 #include "zeromq.h"
   50 #endif
   51 
   52 #ifdef CUSTOM_INIT_CODE
   53 #include <server_custom_init.cpp>
   54 #endif
   55 
   56 /**
   57  * Externals
   58  */
   59 extern ThreadPool *g_clientThreadPool;
   60 extern ThreadPool *g_syncerThreadPool;
   61 extern ThreadPool *g_discoveryThreadPool;
   62 
   63 void InitClientListeners();
   64 void InitMobileDeviceListeners();
   65 void InitCertificates();
   66 bool LoadServerCertificate(RSA **serverKey);
   67 void InitUsers();
   68 void CleanupUsers();
   69 void LoadPerfDataStorageDrivers();
   70 void ShutdownPerfDataStorageDrivers();
   71 void ImportLocalConfiguration(bool overwrite);
   72 void RegisterPredictionEngines();
   73 void ShutdownPredictionEngines();
   74 void ExecuteStartupScripts();
   75 void CloseAgentTunnels();
   76 void StopDataCollection();
   77 void StopObjectMaintenanceThreads();
   78 bool LoadPhysicalLinks();
   79 THREAD StartEventProcessor();
   80 
   81 void ExecuteScheduledAction(const shared_ptr<ScheduledTaskParameters>& parameters);
   82 void ExecuteScheduledScript(const shared_ptr<ScheduledTaskParameters>& parameters);
   83 void MaintenanceModeEnter(const shared_ptr<ScheduledTaskParameters>& parameters);
   84 void MaintenanceModeLeave(const shared_ptr<ScheduledTaskParameters>& parameters);
   85 void ProcessUnboundTunnels(const shared_ptr<ScheduledTaskParameters>& parameters);
   86 void RenewAgentCertificates(const shared_ptr<ScheduledTaskParameters>& parameters);
   87 void ReloadCRLs(const shared_ptr<ScheduledTaskParameters>& parameters);
   88 void ExecuteReport(const shared_ptr<ScheduledTaskParameters>& parameters);
   89 
   90 void InitCountryList();
   91 void InitCurrencyList();
   92 
   93 #if XMPP_SUPPORTED
   94 void StartXMPPConnector();
   95 void StopXMPPConnector();
   96 #endif
   97 
   98 /**
   99  * Syslog server control
  100  */
  101 void StartSyslogServer();
  102 void StopSyslogServer();
  103 
  104 /**
  105  * Windows event log server control
  106  */
  107 void StartWindowsEventProcessing();
  108 void StopWindowsEventProcessing();
  109 
  110 /**
  111  * Thread functions
  112  */
  113 void Syncer();
  114 void NodePoller();
  115 void PollManager(CONDITION startCondition);
  116 void ClientListenerThread();
  117 void MobileDeviceListenerThread();
  118 void ISCListener();
  119 void LocalAdminListener();
  120 void SNMPTrapReceiver();
  121 void BeaconPoller();
  122 void JobManagerThread();
  123 void UptimeCalculator();
  124 void ReportingServerConnector();
  125 void ServerStatCollector();
  126 void TunnelListenerThread();
  127 void LDAPSyncThread();
  128 
  129 /**
  130  * Global variables
  131  */
  132 NXCORE_EXPORTABLE_VAR(TCHAR g_szConfigFile[MAX_PATH]) = _T("{search}");
  133 NXCORE_EXPORTABLE_VAR(TCHAR g_szLogFile[MAX_PATH]) = DEFAULT_LOG_FILE;
  134 uint32_t g_logRotationMode = NXLOG_ROTATION_BY_SIZE;
  135 uint64_t g_maxLogSize = 16384 * 1024;
  136 uint32_t g_logHistorySize = 4;
  137 TCHAR g_szDailyLogFileSuffix[64] = _T("");
  138 NXCORE_EXPORTABLE_VAR(TCHAR g_szDumpDir[MAX_PATH]) = DEFAULT_DUMP_DIR;
  139 char g_szCodePage[256] = ICONV_DEFAULT_CODEPAGE;
  140 NXCORE_EXPORTABLE_VAR(TCHAR g_szListenAddress[MAX_PATH]) = _T("*");
  141 #ifndef _WIN32
  142 NXCORE_EXPORTABLE_VAR(TCHAR g_szPIDFile[MAX_PATH]) = _T("/var/run/netxmsd.pid");
  143 #endif
  144 uint32_t g_discoveryPollingInterval;
  145 uint32_t g_statusPollingInterval;
  146 uint32_t g_configurationPollingInterval;
  147 uint32_t g_routingTableUpdateInterval;
  148 uint32_t g_topologyPollingInterval;
  149 uint32_t g_conditionPollingInterval;
  150 uint32_t g_instancePollingInterval;
  151 uint32_t g_icmpPollingInterval;
  152 uint32_t g_icmpPingSize;
  153 uint32_t g_icmpPingTimeout = 1500;    // ICMP ping timeout (milliseconds)
  154 uint32_t g_auditFlags;
  155 uint32_t g_slmPollingInterval;
  156 uint32_t g_offlineDataRelevanceTime = 86400;
  157 NXCORE_EXPORTABLE_VAR(TCHAR g_netxmsdDataDir[MAX_PATH]) = _T("");
  158 NXCORE_EXPORTABLE_VAR(TCHAR g_netxmsdLibDir[MAX_PATH]) = _T("");
  159 NXCORE_EXPORTABLE_VAR(int g_dbSyntax) = DB_SYNTAX_UNKNOWN;
  160 NXCORE_EXPORTABLE_VAR(UINT32 g_processAffinityMask) = DEFAULT_AFFINITY_MASK;
  161 uint64_t g_serverId = 0;
  162 RSA *g_pServerKey = nullptr;
  163 time_t g_serverStartTime = 0;
  164 uint32_t g_agentCommandTimeout = 4000;  // Default timeout for requests to agent
  165 uint32_t g_thresholdRepeatInterval = 0; // Disabled by default
  166 uint32_t g_requiredPolls = 1;
  167 int32_t g_instanceRetentionTime = 7; // Default instance retention time (in days)
  168 uint32_t g_snmpTrapStormCountThreshold = 0;
  169 uint32_t g_snmpTrapStormDurationThreshold = 15;
  170 DB_DRIVER g_dbDriver = nullptr;
  171 NXCORE_EXPORTABLE_VAR(ThreadPool *g_mainThreadPool) = nullptr;
  172 int16_t g_defaultAgentCacheMode = AGENT_CACHE_OFF;
  173 InetAddressList g_peerNodeAddrList;
  174 Condition g_dbPasswordReady(true);
  175 
  176 /**
  177  * Static data
  178  */
  179 static THREAD s_pollManagerThread = INVALID_THREAD_HANDLE;
  180 static THREAD s_syncerThread = INVALID_THREAD_HANDLE;
  181 static THREAD s_clientListenerThread = INVALID_THREAD_HANDLE;
  182 static THREAD s_mobileDeviceListenerThread = INVALID_THREAD_HANDLE;
  183 static THREAD s_tunnelListenerThread = INVALID_THREAD_HANDLE;
  184 static THREAD s_eventProcessorThread = INVALID_THREAD_HANDLE;
  185 static THREAD s_statCollectorThread = INVALID_THREAD_HANDLE;
  186 static ShutdownReason s_shutdownReason = ShutdownReason::OTHER;
  187 static StringSet s_components;
  188 static ObjectArray<LicenseProblem> s_licenseProblems(0, 16, Ownership::True);
  189 static Mutex s_licenseProblemsLock;
  190 static uint32_t s_licenseProblemId = 1;
  191 
  192 #ifndef _WIN32
  193 static pthread_t m_signalHandlerThread;
  194 #endif
  195 
  196 /**
  197  * Register component
  198  */
  199 void NXCORE_EXPORTABLE RegisterComponent(const TCHAR *id)
  200 {
  201    s_components.add(id);
  202 }
  203 
  204 /**
  205  * Check if component with given ID is registered
  206  */
  207 bool NXCORE_EXPORTABLE IsComponentRegistered(const TCHAR *id)
  208 {
  209    return s_components.contains(id);
  210 }
  211 
  212 /**
  213  * Fill NXCP message with components data
  214  */
  215 void FillComponentsMessage(NXCPMessage *msg)
  216 {
  217    s_components.fillMessage(msg, VID_COMPONENT_LIST_BASE, VID_NUM_COMPONENTS);
  218 }
  219 
  220 /**
  221  * Register license problem. Returns assigned problem ID.
  222  */
  223 uint32_t NXCORE_EXPORTABLE RegisterLicenseProblem(const TCHAR *component, const TCHAR *type, const TCHAR *description)
  224 {
  225    s_licenseProblemsLock.lock();
  226    auto p = new LicenseProblem(s_licenseProblemId++, component, type, description);
  227    s_licenseProblems.add(p);
  228    s_licenseProblemsLock.unlock();
  229    PostSystemEvent(EVENT_LICENSE_PROBLEM, g_dwMgmtNode, "s", description);
  230    return p->id;
  231 }
  232 
  233 /**
  234  * Unregister license problem using problem ID.
  235  */
  236 void NXCORE_EXPORTABLE UnregisterLicenseProblem(uint32_t id)
  237 {
  238    s_licenseProblemsLock.lock();
  239    for(int i = 0; i < s_licenseProblems.size(); i++)
  240       if (s_licenseProblems.get(i)->id == id)
  241       {
  242          s_licenseProblems.remove(i);
  243          break;
  244       }
  245    s_licenseProblemsLock.unlock();
  246 }
  247 
  248 /**
  249  * Unregister license problem using component name and problem type.
  250  */
  251 void NXCORE_EXPORTABLE UnregisterLicenseProblem(const TCHAR *component, const TCHAR *type)
  252 {
  253    s_licenseProblemsLock.lock();
  254    for(int i = 0; i < s_licenseProblems.size(); i++)
  255    {
  256       LicenseProblem *p = s_licenseProblems.get(i);
  257       if (!_tcsicmp(p->component, component) && !_tcsicmp(p->type, type))
  258       {
  259          s_licenseProblems.remove(i);
  260          break;
  261       }
  262    }
  263    s_licenseProblemsLock.unlock();
  264 }
  265 
  266 /**
  267  * Fill NXCP message with list of license problems
  268  */
  269 void NXCORE_EXPORTABLE FillLicenseProblemsMessage(NXCPMessage *msg)
  270 {
  271    uint32_t fieldId = VID_LICENSE_PROBLEM_BASE;
  272    s_licenseProblemsLock.lock();
  273    for(int i = 0; i < s_licenseProblems.size(); i++)
  274    {
  275       LicenseProblem *p = s_licenseProblems.get(i);
  276       msg->setField(fieldId++, p->id);
  277       msg->setFieldFromTime(fieldId++, p->timestamp);
  278       msg->setField(fieldId++, p->component);
  279       msg->setField(fieldId++, p->type);
  280       msg->setField(fieldId++, p->description);
  281       fieldId += 5;
  282    }
  283    msg->setField(VID_LICENSE_PROBLEM_COUNT, s_licenseProblems.size());
  284    s_licenseProblemsLock.unlock();
  285 }
  286 
  287 /**
  288  * Disconnect from database (exportable function for startup module)
  289  */
  290 void NXCORE_EXPORTABLE ShutdownDatabase()
  291 {
  292    DBConnectionPoolShutdown();
  293    DBUnloadDriver(g_dbDriver);
  294 }
  295 
  296 /**
  297  * Check data directory for existence
  298  */
  299 static BOOL CheckDataDir()
  300 {
  301     TCHAR szBuffer[MAX_PATH];
  302 
  303     if (_tchdir(g_netxmsdDataDir) == -1)
  304     {
  305         nxlog_write(NXLOG_ERROR, _T("Data directory \"%s\" does not exist or is inaccessible"), g_netxmsdDataDir);
  306         return FALSE;
  307     }
  308 
  309 #ifdef _WIN32
  310 #define MKDIR(name) _tmkdir(name)
  311 #else
  312 #define MKDIR(name) _tmkdir(name, 0700)
  313 #endif
  314 
  315     // Create directory for package files if it doesn't exist
  316     _tcscpy(szBuffer, g_netxmsdDataDir);
  317     _tcscat(szBuffer, DDIR_PACKAGES);
  318     if (MKDIR(szBuffer) == -1)
  319         if (errno != EEXIST)
  320         {
  321          nxlog_write(NXLOG_ERROR, _T("Error creating data directory \"%s\" (%s)"), szBuffer, _tcserror(errno));
  322             return FALSE;
  323         }
  324 
  325     // Create directory for map background images if it doesn't exist
  326     _tcscpy(szBuffer, g_netxmsdDataDir);
  327     _tcscat(szBuffer, DDIR_BACKGROUNDS);
  328     if (MKDIR(szBuffer) == -1)
  329         if (errno != EEXIST)
  330         {
  331          nxlog_write(NXLOG_ERROR, _T("Error creating data directory \"%s\" (%s)"), szBuffer, _tcserror(errno));
  332             return FALSE;
  333         }
  334 
  335     // Create directory for image library is if does't exists
  336     _tcscpy(szBuffer, g_netxmsdDataDir);
  337     _tcscat(szBuffer, DDIR_IMAGES);
  338     if (MKDIR(szBuffer) == -1)
  339     {
  340         if (errno != EEXIST)
  341         {
  342          nxlog_write(NXLOG_ERROR, _T("Error creating data directory \"%s\" (%s)"), szBuffer, _tcserror(errno));
  343             return FALSE;
  344         }
  345     }
  346 
  347     // Create directory for file store if does't exists
  348     _tcscpy(szBuffer, g_netxmsdDataDir);
  349     _tcscat(szBuffer, DDIR_FILES);
  350     if (MKDIR(szBuffer) == -1)
  351     {
  352         if (errno != EEXIST)
  353         {
  354             nxlog_write(NXLOG_ERROR, _T("Error creating data directory \"%s\" (%s)"), szBuffer, _tcserror(errno));
  355             return FALSE;
  356         }
  357     }
  358 
  359    // Create directory for CRL if does't exists
  360    _tcscpy(szBuffer, g_netxmsdDataDir);
  361    _tcscat(szBuffer, DDIR_CRL);
  362    if (MKDIR(szBuffer) == -1)
  363    {
  364       if (errno != EEXIST)
  365       {
  366          nxlog_write(NXLOG_ERROR, _T("Error creating data directory \"%s\" (%s)"), szBuffer, _tcserror(errno));
  367          return FALSE;
  368       }
  369    }
  370 
  371 #undef MKDIR
  372 
  373     return TRUE;
  374 }
  375 
  376 /**
  377  * Load global configuration parameters
  378  */
  379 static void LoadGlobalConfig()
  380 {
  381    if (MetaDataReadInt32(_T("SingeTablePerfData"), 0))
  382    {
  383       nxlog_debug_tag(_T("dc"), 1, _T("Using single table for performance data storage"));
  384       g_flags |= AF_SINGLE_TABLE_PERF_DATA;
  385    }
  386 
  387    g_conditionPollingInterval = ConfigReadInt(_T("ConditionPollingInterval"), 60);
  388    g_configurationPollingInterval = ConfigReadInt(_T("ConfigurationPollingInterval"), 3600);
  389    g_discoveryPollingInterval = ConfigReadInt(_T("NetworkDiscovery.PassiveDiscovery.Interval"), 900);
  390    g_icmpPollingInterval = ConfigReadInt(_T("ICMP.PollingInterval"), 60);
  391    g_instancePollingInterval = ConfigReadInt(_T("InstancePollingInterval"), 600);
  392    g_routingTableUpdateInterval = ConfigReadInt(_T("RoutingTableUpdateInterval"), 300);
  393    g_slmPollingInterval = ConfigReadInt(_T("SlmPollingInterval"), 60);
  394    g_statusPollingInterval = ConfigReadInt(_T("StatusPollingInterval"), 60);
  395    g_topologyPollingInterval = ConfigReadInt(_T("Topology.PollingInterval"), 1800);
  396    DCObject::m_defaultPollingInterval = ConfigReadInt(_T("DefaultDCIPollingInterval"), 60);
  397    DCObject::m_defaultRetentionTime = ConfigReadInt(_T("DefaultDCIRetentionTime"), 30);
  398    g_defaultAgentCacheMode = (INT16)ConfigReadInt(_T("DefaultAgentCacheMode"), AGENT_CACHE_OFF);
  399    if ((g_defaultAgentCacheMode != AGENT_CACHE_ON) && (g_defaultAgentCacheMode != AGENT_CACHE_OFF))
  400    {
  401       nxlog_debug_tag(_T("dc"), 1, _T("Invalid value %d of DefaultAgentCacheMode: reset to %d (OFF)"), g_defaultAgentCacheMode, AGENT_CACHE_OFF);
  402       ConfigWriteInt(_T("DefaultAgentCacheMode"), AGENT_CACHE_OFF, true, true, true);
  403       g_defaultAgentCacheMode = AGENT_CACHE_OFF;
  404    }
  405    if (ConfigReadBoolean(_T("DeleteEmptySubnets"), true))
  406       g_flags |= AF_DELETE_EMPTY_SUBNETS;
  407    if (ConfigReadBoolean(_T("SNMP.Traps.Enable"), true))
  408       g_flags |= AF_ENABLE_SNMP_TRAPD;
  409    if (ConfigReadBoolean(_T("SNMP.Traps.ProcessUnmanagedNodes"), false))
  410       g_flags |= AF_TRAPS_FROM_UNMANAGED_NODES;
  411    if (ConfigReadBoolean(_T("SNMP.Traps.LogAll"), false))
  412       g_flags |= AF_LOG_ALL_SNMP_TRAPS;
  413    if (ConfigReadBoolean(_T("SNMP.Traps.AllowVarbindsConversion"), true))
  414       g_flags |= AF_ALLOW_TRAP_VARBIND_CONVERSION;
  415    if (ConfigReadBoolean(_T("EnableZoning"), false))
  416       g_flags |= AF_ENABLE_ZONING;
  417    if (ConfigReadBoolean(_T("EnableObjectTransactions"), false))
  418       g_flags |= AF_ENABLE_OBJECT_TRANSACTIONS;
  419    if (ConfigReadBoolean(_T("UseSNMPTrapsForDiscovery"), false))
  420       g_flags |= AF_SNMP_TRAP_DISCOVERY;
  421    if (ConfigReadBoolean(_T("UseSyslogForDiscovery"), false))
  422       g_flags |= AF_SYSLOG_DISCOVERY;
  423    if (ConfigReadBoolean(_T("Objects.Interfaces.Enable8021xStatusPoll"), true))
  424       g_flags |= AF_ENABLE_8021X_STATUS_POLL;
  425    if (ConfigReadBoolean(_T("Objects.Nodes.ResolveNames"), true))
  426       g_flags |= AF_RESOLVE_NODE_NAMES;
  427    if (ConfigReadBoolean(_T("Objects.Nodes.SyncNamesWithDNS"), false))
  428       g_flags |= AF_SYNC_NODE_NAMES_WITH_DNS;
  429    if (ConfigReadBoolean(_T("CheckTrustedNodes"), true))
  430       g_flags |= AF_CHECK_TRUSTED_NODES;
  431    if (ConfigReadBoolean(_T("NetworkDiscovery.EnableParallelProcessing"), false))
  432       g_flags |= AF_PARALLEL_NETWORK_DISCOVERY;
  433    if (ConfigReadBoolean(_T("NetworkDiscovery.MergeDuplicateNodes"), false))
  434       g_flags |= AF_MERGE_DUPLICATE_NODES;
  435    if (ConfigReadBoolean(_T("NXSL.EnableContainerFunctions"), true))
  436    {
  437       g_flags |= AF_ENABLE_NXSL_CONTAINER_FUNCTIONS;
  438       nxlog_debug_tag(_T("nxsl"), 3, _T("NXSL container management functions enabled"));
  439    }
  440    if (ConfigReadBoolean(_T("NXSL.EnableFileIOFunctions"), false))
  441    {
  442       g_flags |= AF_ENABLE_NXSL_FILE_IO_FUNCTIONS;
  443       nxlog_debug_tag(_T("nxsl"), 3, _T("NXSL file I/O functions enabled"));
  444    }
  445    if (ConfigReadBoolean(_T("UseFQDNForNodeNames"), true))
  446       g_flags |= AF_USE_FQDN_FOR_NODE_NAMES;
  447    if (ConfigReadBoolean(_T("ApplyDCIFromTemplateToDisabledDCI"), true))
  448       g_flags |= AF_APPLY_TO_DISABLED_DCI_FROM_TEMPLATE;
  449    if (ConfigReadBoolean(_T("Objects.Nodes.ResolveDNSToIPOnStatusPoll"), false))
  450       g_flags |= AF_RESOLVE_IP_FOR_EACH_STATUS_POLL;
  451    if (ConfigReadBoolean(_T("CaseInsensitiveLoginNames"), false))
  452       g_flags |= AF_CASE_INSENSITIVE_LOGINS;
  453    if (ConfigReadBoolean(_T("TrapSourcesInAllZones"), false))
  454       g_flags |= AF_TRAP_SOURCES_IN_ALL_ZONES;
  455    if (ConfigReadBoolean(_T("ICMP.CollectPollStatistics"), true))
  456       g_flags |= AF_COLLECT_ICMP_STATISTICS;
  457 
  458    switch(ConfigReadInt(_T("NetworkDiscovery.Type"), 0))
  459    {
  460       case 1:  // Passive only
  461          g_flags |= AF_PASSIVE_NETWORK_DISCOVERY;
  462          break;
  463       case 2:  // Active only
  464          g_flags |= AF_ACTIVE_NETWORK_DISCOVERY;
  465          break;
  466       case 3:  // Active and passive
  467          g_flags |= AF_PASSIVE_NETWORK_DISCOVERY | AF_ACTIVE_NETWORK_DISCOVERY;
  468          break;
  469       default:
  470          break;
  471    }
  472 
  473    switch(ConfigReadInt(_T("DBWriter.HouseKeeperInterlock"), 0))
  474    {
  475       case 0:  // Auto
  476          if (g_dbSyntax == DB_SYNTAX_MSSQL)
  477             g_flags |= AF_DBWRITER_HK_INTERLOCK;
  478          else
  479             g_flags &= ~AF_DBWRITER_HK_INTERLOCK;
  480          break;
  481       case 1:  // Off
  482          g_flags &= ~AF_DBWRITER_HK_INTERLOCK;
  483          break;
  484       case 2:  // On
  485          g_flags |= AF_DBWRITER_HK_INTERLOCK;
  486          break;
  487       default:
  488          break;
  489    }
  490    nxlog_write_tag(NXLOG_INFO, _T("db.writer"), _T("DBWriter/Housekeeper interlock is %s"), (g_flags & AF_DBWRITER_HK_INTERLOCK) ? _T("ON") : _T("OFF"));
  491 
  492    if (g_netxmsdDataDir[0] == 0)
  493    {
  494       GetNetXMSDirectory(nxDirData, g_netxmsdDataDir);
  495       nxlog_debug_tag(_T("core"), 1, _T("Data directory set to %s"), g_netxmsdDataDir);
  496    }
  497    else
  498    {
  499       nxlog_debug_tag(_T("core"), 1, _T("Using data directory %s"), g_netxmsdDataDir);
  500       SetNetXMSDataDirectory(g_netxmsdDataDir);
  501    }
  502 
  503    g_icmpPingTimeout = ConfigReadInt(_T("IcmpPingTimeout"), 1500);
  504    g_icmpPingSize = ConfigReadInt(_T("IcmpPingSize"), 46);
  505    g_agentCommandTimeout = ConfigReadInt(_T("AgentCommandTimeout"), 4000);
  506    g_thresholdRepeatInterval = ConfigReadInt(_T("ThresholdRepeatInterval"), 0);
  507    g_requiredPolls = ConfigReadInt(_T("PollCountForStatusChange"), 1);
  508    g_offlineDataRelevanceTime = ConfigReadInt(_T("OfflineDataRelevanceTime"), 86400);
  509    g_instanceRetentionTime = ConfigReadInt(_T("DataCollection.InstanceRetentionTime"), 7); // Config values are in days
  510    g_snmpTrapStormCountThreshold = ConfigReadInt(_T("SNMP.Traps.RateLimit.Threshold"), 0);
  511    g_snmpTrapStormDurationThreshold = ConfigReadInt(_T("SNMP.Traps.RateLimit.Duration"), 15);
  512 
  513    SnmpSetDefaultTimeout(ConfigReadInt(_T("SNMPRequestTimeout"), 1500));
  514 }
  515 
  516 /**
  517  * Initialize cryptografic functions
  518  */
  519 static bool InitCryptography()
  520 {
  521 #ifdef _WITH_ENCRYPTION
  522    if (!InitCryptoLib(ConfigReadULong(_T("AllowedCiphers"), 0x7F)))
  523       return false;
  524    nxlog_debug_tag(_T("crypto"), 4, _T("Supported ciphers: %s"), NXCPGetSupportedCiphersAsText().cstr());
  525 
  526 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
  527    OPENSSL_init_ssl(0, nullptr);
  528 #else
  529    SSL_library_init();
  530    SSL_load_error_strings();
  531 #endif
  532 
  533    bool success = false;
  534    if (LoadServerCertificate(&g_pServerKey))
  535    {
  536       nxlog_debug_tag(_T("crypto"), 1, _T("Server certificate loaded"));
  537    }
  538    if (g_pServerKey != nullptr)
  539    {
  540       nxlog_debug_tag(_T("crypto"), 1, _T("Using server certificate key"));
  541       success = true;
  542    }
  543    else
  544    {
  545       TCHAR szKeyFile[MAX_PATH];
  546       _tcscpy(szKeyFile, g_netxmsdDataDir);
  547       _tcscat(szKeyFile, DFILE_KEYS);
  548       g_pServerKey = LoadRSAKeys(szKeyFile);
  549       if (g_pServerKey == NULL)
  550       {
  551          nxlog_debug_tag(_T("crypto"), 1, _T("Generating RSA key pair..."));
  552          g_pServerKey = RSAGenerateKey(NETXMS_RSA_KEYLEN);
  553          if (g_pServerKey != NULL)
  554          {
  555             int fd = _topen(szKeyFile, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600);
  556             if (fd != -1)
  557             {
  558                UINT32 dwLen = i2d_RSAPublicKey(g_pServerKey, NULL);
  559                dwLen += i2d_RSAPrivateKey(g_pServerKey, NULL);
  560                BYTE *pKeyBuffer = (BYTE *)malloc(dwLen);
  561 
  562                BYTE *pBufPos = pKeyBuffer;
  563                i2d_RSAPublicKey(g_pServerKey, &pBufPos);
  564                i2d_RSAPrivateKey(g_pServerKey, &pBufPos);
  565                _write(fd, &dwLen, sizeof(UINT32));
  566                _write(fd, pKeyBuffer, dwLen);
  567 
  568                BYTE hash[SHA1_DIGEST_SIZE];
  569                CalculateSHA1Hash(pKeyBuffer, dwLen, hash);
  570                _write(fd, hash, SHA1_DIGEST_SIZE);
  571 
  572                _close(fd);
  573                free(pKeyBuffer);
  574                success = true;
  575             }
  576             else
  577             {
  578                nxlog_write_tag(NXLOG_ERROR, _T("crypto"), _T("Failed to open key file %s for writing"), szKeyFile);
  579             }
  580          }
  581          else
  582          {
  583             nxlog_write_tag(NXLOG_ERROR, _T("crypto"), _T("Failed to generate RSA key"));
  584          }
  585       }
  586       else
  587       {
  588          success = true;
  589       }
  590    }
  591 
  592    int iPolicy = ConfigReadInt(_T("DefaultEncryptionPolicy"), 1);
  593    if ((iPolicy < 0) || (iPolicy > 3))
  594       iPolicy = 1;
  595    SetAgentDEP(iPolicy);
  596 
  597    return success;
  598 #else
  599    return true;
  600 #endif
  601 }
  602 
  603 /**
  604  * Check if process with given PID exists and is a NetXMS server process
  605  */
  606 static bool IsNetxmsdProcess(UINT32 pid)
  607 {
  608 #ifdef _WIN32
  609    bool result = false;
  610    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
  611    if (hProcess != NULL)
  612    {
  613       TCHAR szExtModule[MAX_PATH], szIntModule[MAX_PATH];
  614       if ((GetModuleBaseName(hProcess, NULL, szExtModule, MAX_PATH) > 0) &&
  615           (GetModuleBaseName(GetCurrentProcess(), NULL, szIntModule, MAX_PATH) > 0))
  616       {
  617          result = (_tcsicmp(szExtModule, szIntModule) == 0);
  618       }
  619       else
  620       {
  621          // Cannot read process name, for safety assume that it's a server process
  622          result = true;
  623       }
  624       CloseHandle(hProcess);
  625    }
  626    return result;
  627 #else
  628    return kill((pid_t)pid, 0) != -1;
  629 #endif
  630 }
  631 
  632 /**
  633  * Check if remote netxmsd is running
  634  */
  635 static bool PeerNodeIsRunning(const InetAddress& addr)
  636 {
  637    bool result = false;
  638 
  639    TCHAR keyFile[MAX_PATH];
  640    _tcscpy(keyFile, g_netxmsdDataDir);
  641    _tcscat(keyFile, DFILE_KEYS);
  642    RSA *key = LoadRSAKeys(keyFile);
  643 
  644    shared_ptr<AgentConnection> ac = AgentConnection::create(addr);
  645    if (ac->connect(key))
  646    {
  647       TCHAR result[MAX_RESULT_LENGTH];
  648 #ifdef _WIN32
  649       UINT32 rcc = ac->getParameter(_T("Process.Count(netxmsd.exe)"), result, MAX_RESULT_LENGTH);
  650 #else
  651       UINT32 rcc = ac->getParameter(_T("Process.Count(netxmsd)"), result, MAX_RESULT_LENGTH);
  652 #endif
  653       if (key != nullptr)
  654          RSA_free(key);
  655       if (rcc == ERR_SUCCESS)
  656       {
  657          return _tcstol(result, nullptr, 10) > 0;
  658       }
  659    }
  660    else
  661    {
  662       if (key != nullptr)
  663          RSA_free(key);
  664    }
  665 
  666    UINT16 port = (UINT16)ConfigReadInt(_T("ClientListenerPort"), SERVER_LISTEN_PORT_FOR_CLIENTS);
  667    SOCKET s = ConnectToHost(addr, port, 5000);
  668    if (s != INVALID_SOCKET)
  669    {
  670       shutdown(s, SHUT_RDWR);
  671       closesocket(s);
  672       result = true;
  673    }
  674    return result;
  675 }
  676 
  677 /**
  678  * Database event handler
  679  */
  680 static void DBEventHandler(uint32_t event, const WCHAR *arg1, const WCHAR *arg2, bool connLost, void *context)
  681 {
  682     if (!(g_flags & AF_SERVER_INITIALIZED))
  683         return;     // Don't try to do anything if server is not ready yet
  684 
  685     switch(event)
  686     {
  687         case DBEVENT_CONNECTION_LOST:
  688             PostSystemEvent(EVENT_DB_CONNECTION_LOST, g_dwMgmtNode, NULL);
  689             g_flags |= AF_DB_CONNECTION_LOST;
  690             NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, FALSE);
  691             break;
  692         case DBEVENT_CONNECTION_RESTORED:
  693             PostSystemEvent(EVENT_DB_CONNECTION_RESTORED, g_dwMgmtNode, NULL);
  694             g_flags &= ~AF_DB_CONNECTION_LOST;
  695             NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, TRUE);
  696             break;
  697         case DBEVENT_QUERY_FAILED:
  698             PostSystemEvent(EVENT_DB_QUERY_FAILED, g_dwMgmtNode, "uud", arg1, arg2, connLost ? 1 : 0);
  699             break;
  700         default:
  701             break;
  702     }
  703 }
  704 
  705 /**
  706  * Send console message to session with open console
  707  */
  708 static void SendConsoleMessage(ClientSession *session, TCHAR *message)
  709 {
  710     if (session->isConsoleOpen())
  711     {
  712         NXCPMessage msg;
  713         msg.setCode(CMD_ADM_MESSAGE);
  714         msg.setField(VID_MESSAGE, message);
  715         session->postMessage(&msg);
  716     }
  717 }
  718 
  719 /**
  720  * Console writer
  721  */
  722 static void LogConsoleWriter(const TCHAR *format, ...)
  723 {
  724     TCHAR buffer[8192];
  725     va_list args;
  726 
  727     va_start(args, format);
  728     _vsntprintf(buffer, 8192, format, args);
  729     buffer[8191] = 0;
  730     va_end(args);
  731 
  732     WriteToTerminal(buffer);
  733 
  734     EnumerateClientSessions(SendConsoleMessage, buffer);
  735 }
  736 
  737 /**
  738  * Oracle session init callback
  739  */
  740 static void OracleSessionInitCallback(DB_HANDLE hdb)
  741 {
  742    if (!strcmp(DBGetDriverName(DBGetDriver(hdb)), "ORACLE"))
  743       DBQuery(hdb, _T("ALTER SESSION SET DDL_LOCK_TIMEOUT = 60"));
  744 }
  745 
  746 /**
  747  * Get database password
  748  */
  749 static void GetDatabasePassword()
  750 {
  751    if (_tcscmp(g_szDbPassword, _T("?")))
  752       return;
  753 
  754    nxlog_write(NXLOG_INFO, _T("Server is waiting for database password to be supplied"));
  755    g_dbPasswordReady.wait(INFINITE);
  756    nxlog_debug(1, _T("Database password received"));
  757 }
  758 
  759 /**
  760  * Get log destination flags
  761  */
  762 static inline UINT32 GetLogDestinationFlag()
  763 {
  764    if (g_flags & AF_USE_SYSLOG)
  765       return NXLOG_USE_SYSLOG;
  766    if (g_flags & AF_USE_SYSTEMD_JOURNAL)
  767       return NXLOG_USE_SYSTEMD;
  768    if (g_flags & AF_LOG_TO_STDOUT)
  769       return NXLOG_USE_STDOUT;
  770    return 0;
  771 }
  772 
  773 /**
  774  * Scheduled task handler - enter maintenance mode
  775  */
  776 void DummyScheduledTaskExecutor(const shared_ptr<ScheduledTaskParameters>& parameters)
  777 {
  778    //Do nothing
  779 }
  780 
  781 /**
  782  * Server initialization
  783  */
  784 BOOL NXCORE_EXPORTABLE Initialize()
  785 {
  786     s_components.add(_T("CORE"));
  787 
  788     INT64 initStartTime = GetCurrentTimeMs();
  789     g_serverStartTime = static_cast<time_t>(initStartTime / 1000);
  790 
  791     if (!(g_flags & AF_USE_SYSLOG))
  792     {
  793         if (!nxlog_set_rotation_policy((int)g_logRotationMode, g_maxLogSize, (int)g_logHistorySize, g_szDailyLogFileSuffix))
  794             if (!(g_flags & AF_DAEMON))
  795                 _tprintf(_T("WARNING: cannot set log rotation policy; using default values\n"));
  796     }
  797    if (!nxlog_open((g_flags & AF_USE_SYSLOG) ? NETXMSD_SYSLOG_NAME : g_szLogFile,
  798                    GetLogDestinationFlag() |
  799                     ((g_flags & AF_BACKGROUND_LOG_WRITER) ? NXLOG_BACKGROUND_WRITER : 0) |
  800                    ((g_flags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT) |
  801                    ((g_flags & AF_LOG_IN_JSON_FORMAT) ? NXLOG_JSON_FORMAT : 0)))
  802    {
  803         _ftprintf(stderr, _T("FATAL ERROR: Cannot open log file\n"));
  804       return FALSE;
  805    }
  806     nxlog_set_console_writer(LogConsoleWriter);
  807 
  808    if (g_netxmsdLibDir[0] == 0)
  809    {
  810       GetNetXMSDirectory(nxDirLib, g_netxmsdLibDir);
  811       nxlog_debug(1, _T("LIB directory set to %s"), g_netxmsdLibDir);
  812    }
  813 
  814     // Set code page
  815 #ifndef _WIN32
  816     if (SetDefaultCodepage(g_szCodePage))
  817     {
  818         nxlog_write(NXLOG_INFO, _T("Code page set to %hs"), g_szCodePage);
  819     }
  820     else
  821     {
  822         nxlog_write(NXLOG_WARNING, _T("Unable to set codepage to %hs"), g_szCodePage);
  823     }
  824 #endif
  825 
  826     // Set process affinity mask
  827     if (g_processAffinityMask != DEFAULT_AFFINITY_MASK)
  828     {
  829 #ifdef _WIN32
  830         if (SetProcessAffinityMask(GetCurrentProcess(), g_processAffinityMask))
  831            nxlog_write(NXLOG_INFO, _T("Process affinity mask set to 0x%08X"), g_processAffinityMask);
  832 #else
  833         nxlog_write(NXLOG_WARNING, _T("Setting process CPU affinity is not supported on this operating system"));
  834 #endif
  835     }
  836 
  837 #ifdef _WIN32
  838     WSADATA wsaData;
  839     int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
  840     if (wrc != 0)
  841     {
  842       TCHAR buffer[1024];
  843       nxlog_write(NXLOG_ERROR, _T("Call to WSAStartup() failed (%s)"), GetSystemErrorText(wrc, buffer, 1024));
  844         return FALSE;
  845     }
  846 #endif
  847 
  848     InitLocalNetInfo();
  849 
  850     // Initialize database driver and connect to database
  851     if (!DBInit())
  852         return FALSE;
  853     g_dbDriver = DBLoadDriver(g_szDbDriver, g_szDbDrvParams, DBEventHandler, nullptr);
  854     if (g_dbDriver == nullptr)
  855         return FALSE;
  856 
  857    // Start local administrative interface listener if required
  858    if (g_flags & AF_ENABLE_LOCAL_CONSOLE)
  859       ThreadCreate(LocalAdminListener);
  860 
  861     // Wait for database password if needed
  862     GetDatabasePassword();
  863 
  864     // Connect to database
  865     DB_HANDLE hdbBootstrap = nullptr;
  866     TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
  867     for(int i = 0; ; i++)
  868     {
  869        hdbBootstrap = DBConnect(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, errorText);
  870         if ((hdbBootstrap != nullptr) || (i == 5))
  871             break;
  872         ThreadSleep(5);
  873     }
  874     if (hdbBootstrap == nullptr)
  875     {
  876         nxlog_write(NXLOG_ERROR, _T("Unable to establish connection with database (%s)"), errorText);
  877         return FALSE;
  878     }
  879     nxlog_debug(1, _T("Successfully connected to database %s@%s"), g_szDbName, g_szDbServer);
  880 
  881     // Check database schema version
  882     INT32 schemaVersionMajor, schemaVersionMinor;
  883     if (!DBGetSchemaVersion(hdbBootstrap, &schemaVersionMajor, &schemaVersionMinor))
  884     {
  885        nxlog_write(NXLOG_ERROR, _T("Unable to get database schema version"));
  886       DBDisconnect(hdbBootstrap);
  887       return FALSE;
  888     }
  889 
  890     if ((schemaVersionMajor != DB_SCHEMA_VERSION_MAJOR) || (schemaVersionMinor != DB_SCHEMA_VERSION_MINOR))
  891     {
  892         nxlog_write(NXLOG_ERROR, _T("Your database has format version %d.%d, but server is compiled for version %d.%d"), schemaVersionMajor, schemaVersionMinor, DB_SCHEMA_VERSION_MAJOR, DB_SCHEMA_VERSION_MINOR);
  893         DBDisconnect(hdbBootstrap);
  894         return FALSE;
  895     }
  896 
  897     // Read database syntax
  898     g_dbSyntax = DBGetSyntax(hdbBootstrap);
  899    if (g_dbSyntax == DB_SYNTAX_ORACLE)
  900    {
  901       DBSetSessionInitCallback(OracleSessionInitCallback);
  902    }
  903    else if (g_dbSyntax == DB_SYNTAX_PGSQL)
  904    {
  905       DB_RESULT hResult = DBSelect(hdbBootstrap, _T("SELECT version()"));
  906       if (hResult != NULL)
  907       {
  908          if (DBGetNumRows(hResult) > 0)
  909          {
  910             char version[256];
  911             DBGetFieldA(hResult, 0, 0, version, 256);
  912             int major, minor;
  913             if (sscanf(version, "PostgreSQL %d.%d.", &major, &minor) == 2)
  914             {
  915                nxlog_debug(1, _T("Detected PostgreSQL version %d.%d"), major, minor);
  916                if ((major >= 10) || ((major == 9) && (minor >= 5)))
  917                {
  918                   g_flags |= AF_DB_SUPPORTS_MERGE;
  919                }
  920             }
  921          }
  922          DBFreeResult(hResult);
  923       }
  924    }
  925    else if (g_dbSyntax == DB_SYNTAX_TSDB)
  926    {
  927       // TimeScaleDB requires PostgreSQL 9.6.3+ so merge is always supported
  928       g_flags |= AF_DB_SUPPORTS_MERGE;
  929    }
  930     else if (g_dbSyntax == DB_SYNTAX_SQLITE)
  931     {
  932       // Disable startup database cache for SQLite
  933       g_flags &= ~AF_CACHE_DB_ON_STARTUP;
  934     }
  935 
  936     int baseSize = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPool.BaseSize"), 10);
  937     int maxSize = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPool.MaxSize"), 30);
  938     int cooldownTime = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPool.CooldownTime"), 300);
  939     int ttl = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPool.MaxLifetime"), 14400);
  940 
  941    DBDisconnect(hdbBootstrap);
  942 
  943     if (!DBConnectionPoolStartup(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, baseSize, maxSize, cooldownTime, ttl))
  944     {
  945       nxlog_write(NXLOG_ERROR, _T("Failed to initialize database connection pool"));
  946        return FALSE;
  947     }
  948 
  949    UINT32 lrt = ConfigReadULong(_T("LongRunningQueryThreshold"), 0);
  950    if (lrt != 0)
  951    {
  952       DBSetLongRunningThreshold(lrt);
  953       nxlog_write(NXLOG_INFO, _T("Long running query threshold set at %d milliseconds"), lrt);
  954    }
  955 
  956    MetaDataPreLoad();
  957 
  958    // Read server ID
  959    TCHAR buffer[256];
  960    MetaDataReadStr(_T("ServerID"), buffer, 256, _T(""));
  961    StrStrip(buffer);
  962    if (buffer[0] != 0)
  963    {
  964       g_serverId = _tcstoull(buffer, NULL, 16);
  965    }
  966    else
  967    {
  968       // Generate new ID
  969       g_serverId = ((UINT64)time(NULL) << 31) | (UINT64)((UINT32)rand() & 0x7FFFFFFF);
  970       _sntprintf(buffer, 256, UINT64X_FMT(_T("016")), g_serverId);
  971       MetaDataWriteStr(_T("ServerID"), buffer);
  972    }
  973    nxlog_write(NXLOG_INFO, _T("Server ID ") UINT64X_FMT(_T("016")), g_serverId);
  974 
  975    // Initialize locks
  976 retry_db_lock:
  977    InetAddress addr;
  978    if (!LockDatabase(&addr, buffer))
  979    {
  980       if (addr.isValidUnicast())     // Database already locked by another server instance
  981       {
  982          // Check for lock from crashed/terminated local process
  983          if (GetLocalIpAddr().equals(addr))
  984          {
  985             uint32_t pid = ConfigReadULong(_T("DBLockPID"), 0);
  986                 if (!IsNetxmsdProcess(pid) || (pid == GetCurrentProcessId()))
  987                 {
  988                     UnlockDatabase();
  989                nxlog_write_tag(NXLOG_INFO, _T("db.lock"), _T("Stalled database lock removed"));
  990                     goto retry_db_lock;
  991                 }
  992             }
  993             else if (g_peerNodeAddrList.hasAddress(addr))
  994             {
  995                if (!PeerNodeIsRunning(addr))
  996                {
  997                UnlockDatabase();
  998                nxlog_write_tag(NXLOG_INFO, _T("db.lock"), _T("Stalled database lock removed"));
  999                goto retry_db_lock;
 1000                }
 1001             }
 1002             nxlog_write_tag(NXLOG_ERROR, _T("db.lock"), _T("Database is already locked by another NetXMS server instance (IP address: %s, machine info: %s)"),
 1003                      (const TCHAR *)addr.toString(), buffer);
 1004         }
 1005       else if (!_tcsncmp(buffer, _T("NXDBMGR"), 7))
 1006       {
 1007          nxlog_write_tag(NXLOG_ERROR, _T("db.lock"), _T("Database is already locked by database manager"));
 1008       }
 1009         else
 1010       {
 1011          nxlog_write_tag(NXLOG_ERROR, _T("db.lock"), _T("Cannot lock database"));
 1012       }
 1013         return FALSE;
 1014     }
 1015     g_flags |= AF_DB_LOCKED;
 1016    nxlog_debug_tag(_T("db.lock"), 1, _T("Database lock set"));
 1017 
 1018    // Load global configuration parameters
 1019    ConfigPreLoad();
 1020    LoadGlobalConfig();
 1021    CASReadSettings();
 1022    nxlog_debug(1, _T("Global configuration loaded"));
 1023 
 1024    // Setup thread pool resize parameters
 1025    ThreadPoolSetResizeParameters(
 1026             ConfigReadInt(_T("ThreadPool.Global.Responsiveness"), 12),
 1027             ConfigReadInt(_T("ThreadPool.Global.WaitTimeHighWatermark"), 200),
 1028             ConfigReadInt(_T("ThreadPool.Global.WaitTimeLowWatermark"), 100));
 1029 
 1030    // Check data directory
 1031    if (!CheckDataDir())
 1032       return FALSE;
 1033 
 1034    // Initialize cryptography
 1035    if (!InitCryptography())
 1036    {
 1037       nxlog_write(NXLOG_ERROR, _T("Failed to initialize cryptography module"));
 1038       return FALSE;
 1039    }
 1040 
 1041    // Initialize certificate store and CA
 1042    InitCertificates();
 1043 
 1044    // Call custom initialization code
 1045 #ifdef CUSTOM_INIT_CODE
 1046    if (!ServerCustomInit())
 1047       return FALSE;
 1048 #endif
 1049 
 1050    // Create thread pools
 1051    nxlog_debug(2, _T("Creating thread pools"));
 1052    g_mainThreadPool = ThreadPoolCreate(_T("MAIN"),
 1053          ConfigReadInt(_T("ThreadPool.Main.BaseSize"), 8),
 1054          ConfigReadInt(_T("ThreadPool.Main.MaxSize"), 256));
 1055    g_agentConnectionThreadPool = ThreadPoolCreate(_T("AGENT"),
 1056          ConfigReadInt(_T("ThreadPool.Agent.BaseSize"), 4),
 1057          ConfigReadInt(_T("ThreadPool.Agent.MaxSize"), 256));
 1058 
 1059    // Setup unique identifiers table
 1060    if (!InitIdTable())
 1061       return FALSE;
 1062    nxlog_debug(2, _T("ID table created"));
 1063 
 1064    InitCountryList();
 1065    InitCurrencyList();
 1066 
 1067    // Load and compile scripts
 1068    LoadScripts();
 1069 
 1070    // Initialize persistent storage
 1071    PersistentStorageInit();
 1072 
 1073    // Initialize watchdog
 1074    WatchdogInit();
 1075 
 1076    // Start database _T("lazy") write thread
 1077    StartDBWriter();
 1078 
 1079    // Load modules
 1080    if (!LoadNetXMSModules())
 1081       return FALSE;   // Mandatory module not loaded
 1082    RegisterPredictionEngines();
 1083 
 1084    // Initialize mailer
 1085    InitMailer();
 1086 
 1087    // Load users from database
 1088    InitUsers();
 1089    if (!LoadUsers())
 1090    {
 1091       nxlog_write(NXLOG_ERROR, _T("Unable to load users and user groups from database (probably database is corrupted)"));
 1092       return FALSE;
 1093    }
 1094    nxlog_debug(2, _T("User accounts loaded"));
 1095 
 1096    // Initialize audit
 1097    InitAuditLog();
 1098 
 1099    // Initialize event handling subsystem
 1100    if (!InitEventSubsystem())
 1101       return FALSE;
 1102 
 1103    // Initialize alarms
 1104    LoadAlarmCategories();
 1105    if (!InitAlarmManager())
 1106       return FALSE;
 1107 
 1108    // Initialize notification channels
 1109    LoadNotificationChannelDrivers();
 1110    LoadNotificationChannels();
 1111 
 1112    // Initialize objects infrastructure and load objects from database
 1113    LoadGeoAreas();
 1114    LoadNetworkDeviceDrivers();
 1115    ObjectsInit();
 1116    LoadObjectCategories();
 1117    LoadSshKeys();
 1118    if (!LoadObjects())
 1119       return FALSE;
 1120    nxlog_debug(1, _T("Objects loaded and initialized"));
 1121 
 1122    // Initialize and load event actions
 1123    if (!LoadActions())
 1124    {
 1125       nxlog_write(NXLOG_ERROR, _T("Unable to initialize actions"));
 1126       return FALSE;
 1127    }
 1128 
 1129    // Initialize helpdesk link
 1130    SetHDLinkEntryPoints(ResolveAlarmByHDRef, TerminateAlarmByHDRef);
 1131    LoadHelpDeskLink();
 1132 
 1133    InitLogAccess();
 1134    FileUploadJob::init();
 1135    InitMappingTables();
 1136 
 1137    InitUserAgentNotifications();
 1138 
 1139    if (!LoadPhysicalLinks())
 1140    {
 1141       nxlog_write(NXLOG_ERROR, _T("Unable to load physical links"));
 1142       return FALSE;
 1143    }
 1144 
 1145    // Initialize data collection subsystem
 1146    LoadPerfDataStorageDrivers();
 1147    LoadWebServiceDefinitions();
 1148    InitDataCollector();
 1149 
 1150    int importMode = ConfigReadInt(_T("ImportConfigurationOnStartup"), 1);
 1151    if (importMode > 0)
 1152       ImportLocalConfiguration(importMode == 2);
 1153 
 1154    InitClientListeners();
 1155 
 1156    // Check if management node object presented in database
 1157    CheckForMgmtNode();
 1158    if (g_dwMgmtNode == 0)
 1159    {
 1160       nxlog_write(NXLOG_ERROR, _T("NetXMS server cannot create node object for itself - probably because platform subagent cannot be loaded (check above error messages, if any)"));
 1161       return FALSE;
 1162    }
 1163 
 1164    // Create syncer thread pool
 1165    maxSize = ConfigReadInt(_T("ThreadPool.Syncer.MaxSize"), 1);
 1166    if (maxSize > 1)
 1167    {
 1168       g_syncerThreadPool = ThreadPoolCreate(_T("SYNCER"), ConfigReadInt(_T("ThreadPool.Syncer.BaseSize"), 1), maxSize);
 1169    }
 1170 
 1171    // Create network discovery thread pool
 1172    maxSize = ConfigReadInt(_T("ThreadPool.Discovery.MaxSize"), 16);
 1173    if (maxSize > 1)
 1174    {
 1175       g_discoveryThreadPool = ThreadPoolCreate(_T("DISCOVERY"), ConfigReadInt(_T("ThreadPool.Discovery.BaseSize"), 1), maxSize);
 1176    }
 1177 
 1178    // Start threads
 1179    ThreadCreate(NodePoller);
 1180    ThreadCreate(JobManagerThread);
 1181    s_syncerThread = ThreadCreateEx(Syncer);
 1182 
 1183    CONDITION pollManagerInitialized = ConditionCreate(true);
 1184    s_pollManagerThread = ThreadCreateEx(PollManager, pollManagerInitialized);
 1185 
 1186    StartHouseKeeper();
 1187 
 1188    // Start event processor
 1189    s_eventProcessorThread = StartEventProcessor();
 1190 
 1191    // Start SNMP trapper
 1192    InitTraps();
 1193    if (ConfigReadBoolean(_T("SNMP.Traps.Enable"), true))
 1194       ThreadCreate(SNMPTrapReceiver);
 1195 
 1196    StartSyslogServer();
 1197    StartWindowsEventProcessing();
 1198 
 1199    // Start beacon host poller
 1200    ThreadCreate(BeaconPoller);
 1201 
 1202    // Start inter-server communication listener
 1203    if (ConfigReadBoolean(_T("EnableISCListener"), false))
 1204       ThreadCreate(ISCListener);
 1205 
 1206    // Start reporting server connector
 1207    if (ConfigReadBoolean(_T("EnableReportingServer"), false))
 1208       ThreadCreate(ReportingServerConnector);
 1209 
 1210    // Start LDAP synchronization
 1211    if (ConfigReadInt(_T("LDAP.SyncInterval"), 0))
 1212       ThreadCreate(LDAPSyncThread);
 1213 
 1214    // Wait for initialization of critical threads
 1215    ConditionWait(pollManagerInitialized, INFINITE);
 1216    ConditionDestroy(pollManagerInitialized);
 1217    nxlog_debug(2, _T("Poll manager initialized"));
 1218 
 1219    RegisterSchedulerTaskHandler(_T("Execute.Action"), ExecuteScheduledAction, SYSTEM_ACCESS_SCHEDULE_SCRIPT);
 1220    RegisterSchedulerTaskHandler(_T("Execute.Script"), ExecuteScheduledScript, SYSTEM_ACCESS_SCHEDULE_SCRIPT);
 1221    RegisterSchedulerTaskHandler(_T("Maintenance.Enter"), MaintenanceModeEnter, SYSTEM_ACCESS_SCHEDULE_MAINTENANCE);
 1222    RegisterSchedulerTaskHandler(_T("Maintenance.Leave"), MaintenanceModeLeave, SYSTEM_ACCESS_SCHEDULE_MAINTENANCE);
 1223    RegisterSchedulerTaskHandler(_T("Dummy"), DummyScheduledTaskExecutor, SYSTEM_ACCESS_USER_SCHEDULED_TASKS);
 1224    RegisterSchedulerTaskHandler(ALARM_SUMMARY_EMAIL_TASK_ID, SendAlarmSummaryEmail, 0); //No access right because it will be used only by server
 1225    RegisterSchedulerTaskHandler(UNBOUND_TUNNEL_PROCESSOR_TASK_ID, ProcessUnboundTunnels, 0); //No access right because it will be used only by server
 1226    RegisterSchedulerTaskHandler(RENEW_AGENT_CERTIFICATES_TASK_ID, RenewAgentCertificates, 0); //No access right because it will be used only by server
 1227    RegisterSchedulerTaskHandler(RELOAD_CRLS_TASK_ID, ReloadCRLs, 0); //No access right because it will be used only by server
 1228    RegisterSchedulerTaskHandler(DCT_RESET_POLL_TIMERS_TASK_ID, ResetObjectPollTimers, 0); //No access right because it will be used only by server
 1229    RegisterSchedulerTaskHandler(EXECUTE_REPORT_TASK_ID, ExecuteReport, SYSTEM_ACCESS_REPORTING_SERVER);
 1230    InitializeTaskScheduler();
 1231 
 1232    // Schedule unbound agent tunnel processing and automatic agent certificate renewal
 1233    AddUniqueRecurrentScheduledTask(UNBOUND_TUNNEL_PROCESSOR_TASK_ID, _T("*/5 * * * *"), _T(""), nullptr, 0, 0, SYSTEM_ACCESS_FULL, _T(""), nullptr, true);
 1234    AddUniqueRecurrentScheduledTask(RENEW_AGENT_CERTIFICATES_TASK_ID, _T("0 12 * * *"), _T(""), nullptr, 0, 0, SYSTEM_ACCESS_FULL, _T(""), nullptr, true);
 1235 
 1236    // Send summary emails
 1237    if (ConfigReadBoolean(_T("EnableAlarmSummaryEmails"), false))
 1238       EnableAlarmSummaryEmails();
 1239    else
 1240       DeleteScheduledTaskByHandlerId(ALARM_SUMMARY_EMAIL_TASK_ID);
 1241 
 1242    // Schedule automatic CRL reload
 1243    AddUniqueRecurrentScheduledTask(RELOAD_CRLS_TASK_ID, _T("0 */4 * * *"), _T(""), nullptr, 0, 0, SYSTEM_ACCESS_FULL, _T(""), nullptr, true);
 1244 
 1245    // Schedule poll timers reset
 1246    AddUniqueRecurrentScheduledTask(DCT_RESET_POLL_TIMERS_TASK_ID, _T("0 0 1 * *"), _T(""), nullptr, 0, 0, SYSTEM_ACCESS_FULL, _T(""), nullptr, true);
 1247 
 1248    // Start listeners
 1249    s_tunnelListenerThread = ThreadCreateEx(TunnelListenerThread);
 1250    s_clientListenerThread = ThreadCreateEx(ClientListenerThread);
 1251    InitMobileDeviceListeners();
 1252    s_mobileDeviceListenerThread = ThreadCreateEx(MobileDeviceListenerThread);
 1253 
 1254    // Start uptime calculator for SLM
 1255    ThreadCreate(UptimeCalculator);
 1256 
 1257    nxlog_debug(2, _T("LIBDIR: %s"), g_netxmsdLibDir);
 1258 
 1259    // Call startup functions for the modules
 1260    CALL_ALL_MODULES(pfServerStarted, ());
 1261 
 1262 #if XMPP_SUPPORTED
 1263    if (ConfigReadBoolean(_T("EnableXMPPConnector"), true))
 1264    {
 1265       StartXMPPConnector();
 1266    }
 1267 #endif
 1268 
 1269 #if WITH_ZMQ
 1270    StartZMQConnector();
 1271 #endif
 1272 
 1273    ExecuteStartupScripts();
 1274 
 1275    // Internal stat collector should be started last when all queues
 1276    // and thread pools already created
 1277    s_statCollectorThread = ThreadCreateEx(ServerStatCollector);
 1278 
 1279    g_flags |= AF_SERVER_INITIALIZED;
 1280    PostSystemEvent(EVENT_SERVER_STARTED, g_dwMgmtNode, NULL);
 1281    nxlog_debug(1, _T("Server initialization completed in %d milliseconds"), static_cast<int>(GetCurrentTimeMs() - initStartTime));
 1282    return TRUE;
 1283 }
 1284 
 1285 /**
 1286  * Shutdown reason texts
 1287  */
 1288 static const TCHAR *s_shutdownReasonText[] = { _T("due to unknown reason"), _T("from local console"), _T("from remote console"), _T("by signal"), _T("by service manager") };
 1289 
 1290 /**
 1291  * Server shutdown
 1292  */
 1293 void NXCORE_EXPORTABLE Shutdown()
 1294 {
 1295    // Notify clients
 1296    NotifyClientSessions(NX_NOTIFY_SHUTDOWN, 0);
 1297 
 1298    nxlog_write(NXLOG_INFO, _T("NetXMS Server stopped %s"), s_shutdownReasonText[static_cast<int>(s_shutdownReason)]);
 1299    g_flags |= AF_SHUTDOWN;     // Set shutdown flag
 1300    InitiateProcessShutdown();
 1301 
 1302    // Call shutdown functions for the modules
 1303    // CALL_ALL_MODULES cannot be used here because it checks for shutdown flag
 1304    for(int i = 0; i < g_moduleList.size(); i++)
 1305    {
 1306       if (g_moduleList.get(i)->pfShutdown != nullptr)
 1307          g_moduleList.get(i)->pfShutdown();
 1308    }
 1309 
 1310    ThreadJoin(s_statCollectorThread);
 1311 
 1312    StopHouseKeeper();
 1313    ShutdownTaskScheduler();
 1314 
 1315    // Stop DCI cache loading thread
 1316    g_dciCacheLoaderQueue.setShutdownMode();
 1317 
 1318     ShutdownPredictionEngines();
 1319    StopObjectMaintenanceThreads();
 1320    StopDataCollection();
 1321 
 1322    // Wait for critical threads
 1323    ThreadJoin(s_pollManagerThread);
 1324    ThreadJoin(s_syncerThread);
 1325 
 1326    nxlog_debug(2, _T("Waiting for listener threads to stop"));
 1327    ThreadJoin(s_tunnelListenerThread);
 1328    ThreadJoin(s_clientListenerThread);
 1329    ThreadJoin(s_mobileDeviceListenerThread);
 1330 
 1331    CloseAgentTunnels();
 1332    StopSyslogServer();
 1333    StopWindowsEventProcessing();
 1334 
 1335    nxlog_debug(2, _T("Waiting for event processor to stop"));
 1336     g_eventQueue.put(INVALID_POINTER_VALUE);
 1337     ThreadJoin(s_eventProcessorThread);
 1338 
 1339     ShutdownMailer();
 1340 
 1341 #if XMPP_SUPPORTED
 1342    StopXMPPConnector();
 1343 #endif
 1344 
 1345 #if WITH_ZMQ
 1346    StopZMQConnector();
 1347 #endif
 1348 
 1349     ThreadSleep(1);     // Give other threads a chance to terminate in a safe way
 1350     nxlog_debug(2, _T("All threads were notified, continue with shutdown"));
 1351 
 1352    DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
 1353     SaveObjects(hdb, INVALID_INDEX, true);
 1354     nxlog_debug(2, _T("All objects saved to database"));
 1355     SaveUsers(hdb, INVALID_INDEX);
 1356     nxlog_debug(2, _T("All users saved to database"));
 1357    UpdatePStorageDatabase(hdb, INVALID_INDEX);
 1358     nxlog_debug(2, _T("All persistent storage values saved"));
 1359     DBConnectionPoolReleaseConnection(hdb);
 1360 
 1361     if (g_syncerThreadPool != NULL)
 1362        ThreadPoolDestroy(g_syncerThreadPool);
 1363 
 1364    if (g_discoveryThreadPool != NULL)
 1365       ThreadPoolDestroy(g_discoveryThreadPool);
 1366 
 1367     StopDBWriter();
 1368     nxlog_debug(1, _T("Database writer stopped"));
 1369 
 1370     CleanupUsers();
 1371     PersistentStorageDestroy();
 1372 
 1373    ShutdownPerfDataStorageDrivers();
 1374 
 1375    CleanupActions();
 1376    ShutdownEventSubsystem();
 1377    ShutdownAlarmManager();
 1378    ShutdownNotificationChannels();
 1379    nxlog_debug(1, _T("Event processing stopped"));
 1380 
 1381    DisableAgentConnections();
 1382 
 1383    ThreadPoolDestroy(g_clientThreadPool);
 1384    ThreadPoolDestroy(g_agentConnectionThreadPool);
 1385    ThreadPoolDestroy(g_mainThreadPool);
 1386    WatchdogShutdown();
 1387 
 1388    SaveCurrentFreeId();
 1389 
 1390     // Remove database lock
 1391     UnlockDatabase();
 1392 
 1393     DBConnectionPoolShutdown();
 1394     DBUnloadDriver(g_dbDriver);
 1395     nxlog_debug(1, _T("Database driver unloaded"));
 1396 
 1397     nxlog_debug(1, _T("Server shutdown complete"));
 1398     nxlog_close();
 1399 
 1400     // Remove PID file
 1401 #ifndef _WIN32
 1402     _tremove(g_szPIDFile);
 1403 #endif
 1404 
 1405     // Terminate process
 1406 #ifdef _WIN32
 1407     if (!(g_flags & AF_DAEMON))
 1408         ExitProcess(0);
 1409 #else
 1410     exit(0);
 1411 #endif
 1412 }
 1413 
 1414 /**
 1415  * Fast server shutdown - normally called only by Windows service on system shutdown
 1416  */
 1417 void NXCORE_EXPORTABLE FastShutdown(ShutdownReason reason)
 1418 {
 1419    nxlog_write(NXLOG_INFO, _T("NetXMS Server stopped %s using fast shutdown procedure"), s_shutdownReasonText[static_cast<int>(s_shutdownReason)]);
 1420 
 1421     g_flags |= AF_SHUTDOWN;     // Set shutdown flag
 1422     InitiateProcessShutdown();
 1423 
 1424     DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
 1425     SaveObjects(hdb, INVALID_INDEX, true);
 1426     DbgPrintf(2, _T("All objects saved to database"));
 1427     SaveUsers(hdb, INVALID_INDEX);
 1428     DbgPrintf(2, _T("All users saved to database"));
 1429    UpdatePStorageDatabase(hdb, INVALID_INDEX);
 1430     DbgPrintf(2, _T("All persistent storage values saved"));
 1431     DBConnectionPoolReleaseConnection(hdb);
 1432 
 1433     // Remove database lock first, because we have a chance to lose DB connection
 1434     UnlockDatabase();
 1435 
 1436     // Stop database writers
 1437     StopDBWriter();
 1438     DbgPrintf(1, _T("Database writer stopped"));
 1439 
 1440    DbgPrintf(1, _T("Server shutdown complete"));
 1441     nxlog_close();
 1442 }
 1443 
 1444 /**
 1445  * Signal handler for UNIX platforms
 1446  */
 1447 #ifndef _WIN32
 1448 
 1449 void SignalHandlerStub(int nSignal)
 1450 {
 1451     // should be unused, but JIC...
 1452     if (nSignal == SIGCHLD)
 1453     {
 1454         while (waitpid(-1, NULL, WNOHANG) > 0)
 1455             ;
 1456     }
 1457 }
 1458 
 1459 THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL SignalHandler(void *pArg)
 1460 {
 1461     sigset_t signals;
 1462     int nSignal;
 1463 
 1464     m_signalHandlerThread = pthread_self();
 1465 
 1466     // default for SIGCHLD: ignore
 1467     signal(SIGCHLD, &SignalHandlerStub);
 1468 
 1469     sigemptyset(&signals);
 1470     sigaddset(&signals, SIGTERM);
 1471     sigaddset(&signals, SIGINT);
 1472     sigaddset(&signals, SIGSEGV);
 1473     sigaddset(&signals, SIGCHLD);
 1474     sigaddset(&signals, SIGHUP);
 1475     sigaddset(&signals, SIGUSR1);
 1476     sigaddset(&signals, SIGUSR2);
 1477 #if !defined(__sun) && !defined(_AIX) && !defined(__hpux)
 1478     sigaddset(&signals, SIGPIPE);
 1479 #endif
 1480 
 1481     sigprocmask(SIG_BLOCK, &signals, NULL);
 1482 
 1483     while(1)
 1484     {
 1485         if (sigwait(&signals, &nSignal) == 0)
 1486         {
 1487             switch(nSignal)
 1488             {
 1489                 case SIGTERM:
 1490                 case SIGINT:
 1491                    // avoid repeat Shutdown() call
 1492                    if (!(g_flags & AF_SHUTDOWN))
 1493                    {
 1494                       if (s_shutdownReason == ShutdownReason::OTHER)
 1495                          s_shutdownReason = ShutdownReason::BY_SIGNAL;
 1496                   if (IsStandalone())
 1497                   {
 1498                      Shutdown(); // will never return
 1499                   }
 1500                   else
 1501                   {
 1502                      InitiateProcessShutdown();
 1503                   }
 1504                    }
 1505                    break;
 1506                 case SIGSEGV:
 1507                     abort();
 1508                     break;
 1509                 case SIGCHLD:
 1510                     while (waitpid(-1, NULL, WNOHANG) > 0)
 1511                         ;
 1512                     break;
 1513                 case SIGUSR1:
 1514                     if (g_flags & AF_SHUTDOWN)
 1515                         goto stop_handler;
 1516                     break;
 1517                 default:
 1518                     break;
 1519             }
 1520         }
 1521         else
 1522         {
 1523             ThreadSleepMs(100);
 1524         }
 1525     }
 1526 
 1527 stop_handler:
 1528     sigprocmask(SIG_UNBLOCK, &signals, NULL);
 1529     return THREAD_OK;
 1530 }
 1531 
 1532 #endif
 1533 
 1534 /**
 1535  * Common main()
 1536  */
 1537 THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL Main(void *pArg)
 1538 {
 1539     nxlog_write(NXLOG_INFO, _T("NetXMS Server started"));
 1540 
 1541     if (IsStandalone())
 1542    {
 1543       if (!(g_flags & AF_DEBUG_CONSOLE_DISABLED))
 1544        {
 1545            char *ptr, szCommand[256];
 1546            LocalTerminalConsole ctx;
 1547 #ifdef UNICODE
 1548         WCHAR wcCommand[256];
 1549 #endif
 1550 
 1551            WriteToTerminal(_T("\nNetXMS Server V") NETXMS_VERSION_STRING _T(" Build ") NETXMS_BUILD_TAG IS_UNICODE_BUILD_STRING _T(" Ready\n")
 1552                              _T("Enter \"\x1b[1mhelp\x1b[0m\" for command list or \"\x1b[1mdown\x1b[0m\" for server shutdown\n")
 1553                              _T("System Console\n\n"));
 1554 
 1555 #if USE_READLINE
 1556            // Initialize readline library if we use it
 1557            rl_bind_key('\t', RL_INSERT_CAST rl_insert);
 1558 #endif
 1559 
 1560            while(1)
 1561            {
 1562 #if USE_READLINE
 1563             ptr = readline("\x1b[33mnetxmsd:\x1b[0m ");
 1564 #else
 1565                WriteToTerminal(_T("\x1b[33mnetxmsd:\x1b[0m "));
 1566                fflush(stdout);
 1567                if (fgets(szCommand, 255, stdin) == NULL)
 1568                    break;   // Error reading stdin
 1569                ptr = strchr(szCommand, '\n');
 1570                if (ptr != NULL)
 1571                    *ptr = 0;
 1572                ptr = szCommand;
 1573 #endif
 1574 
 1575                if (ptr != NULL)
 1576                {
 1577 #ifdef UNICODE
 1578 #if HAVE_MBSTOWCS
 1579                   mbstowcs(wcCommand, ptr, 255);
 1580 #else
 1581                    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ptr, -1, wcCommand, 256);
 1582 #endif
 1583                    wcCommand[255] = 0;
 1584                    StrStrip(wcCommand);
 1585                    if (wcCommand[0] != 0)
 1586                    {
 1587                        if (ProcessConsoleCommand(wcCommand, &ctx) == CMD_EXIT_SHUTDOWN)
 1588 #else
 1589                    StrStrip(ptr);
 1590                    if (*ptr != 0)
 1591                    {
 1592                        if (ProcessConsoleCommand(ptr, &ctx) == CMD_EXIT_SHUTDOWN)
 1593 #endif
 1594                            break;
 1595 #if USE_READLINE
 1596                        add_history(ptr);
 1597 #endif
 1598                    }
 1599 #if USE_READLINE
 1600                    free(ptr);
 1601 #endif
 1602                }
 1603                else
 1604                {
 1605                    _tprintf(_T("\n"));
 1606                }
 1607            }
 1608 
 1609 #if USE_READLINE
 1610         free(ptr);
 1611 #endif
 1612         if (!(g_flags & AF_SHUTDOWN))
 1613         {
 1614            s_shutdownReason = ShutdownReason::FROM_LOCAL_CONSOLE;
 1615             Shutdown();
 1616         }
 1617       }
 1618       else
 1619       {
 1620          // standalone with debug console disabled
 1621 #ifdef _WIN32
 1622          _tprintf(_T("Server running. Press ESC to shutdown.\n"));
 1623          while(1)
 1624          {
 1625             if (_getch() == 27)
 1626                break;
 1627          }
 1628          _tprintf(_T("Server shutting down...\n"));
 1629          Shutdown();
 1630 #else
 1631          _tprintf(_T("Server running. Press Ctrl+C to shutdown.\n"));
 1632          // Shutdown will be called from signal handler
 1633          SleepAndCheckForShutdown(INFINITE);
 1634 #endif
 1635       }
 1636     }
 1637     else
 1638     {
 1639       SleepAndCheckForShutdown(INFINITE);
 1640         // On Win32, Shutdown() will be called by service control handler
 1641 #ifndef _WIN32
 1642         Shutdown();
 1643 #endif
 1644     }
 1645     return THREAD_OK;
 1646 }
 1647 
 1648 /**
 1649  * Initiate server shutdown
 1650  */
 1651 void NXCORE_EXPORTABLE InitiateShutdown(ShutdownReason reason)
 1652 {
 1653    s_shutdownReason = reason;
 1654 #ifdef _WIN32
 1655     Shutdown();
 1656 #else
 1657     if (IsStandalone())
 1658     {
 1659         Shutdown();
 1660     }
 1661     else
 1662     {
 1663         pthread_kill(m_signalHandlerThread, SIGTERM);
 1664     }
 1665 #endif
 1666 }
 1667 
 1668 /**
 1669  *DLL Entry point
 1670  */
 1671 #ifdef _WIN32
 1672 
 1673 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
 1674 {
 1675     if (dwReason == DLL_PROCESS_ATTACH)
 1676         DisableThreadLibraryCalls(hInstance);
 1677     return TRUE;
 1678 }
 1679 
 1680 #endif