calls to functions deprecated in OpenSSL 1.1 replaced with compatible ones
[public/netxms.git] / src / server / core / main.cpp
index 207983a..9378d9b 100644 (file)
@@ -1,6 +1,6 @@
 /*
 ** NetXMS - Network Management System
-** Copyright (C) 2003-2016 Raden Solutions
+** Copyright (C) 2003-2017 Raden Solutions
 **
 ** This program is free software; you can redistribute it and/or modify
 ** it under the terms of the GNU General Public License as published by
@@ -35,9 +35,6 @@
 #include <errno.h>
 #include <psapi.h>
 #include <conio.h>
-#define open   _open
-#define write  _write
-#define close  _close
 #else
 #include <signal.h>
 #include <sys/wait.h>
@@ -70,10 +67,16 @@ extern Queue g_dciCacheLoaderQueue;
 void InitClientListeners();
 void InitMobileDeviceListeners();
 void InitCertificates();
+bool LoadServerCertificate(RSA **serverKey);
 void InitUsers();
 void CleanupUsers();
 void LoadPerfDataStorageDrivers();
 void ImportLocalConfiguration();
+void RegisterPredictionEngines();
+void ExecuteStartupScripts();
+void CloseAgentTunnels();
+void StopDataCollection();
+void StopObjectMaintenanceThreads();
 
 void ExecuteScheduledScript(const ScheduledTaskParameters *param);
 void MaintenanceModeEnter(const ScheduledTaskParameters *params);
@@ -103,10 +106,8 @@ THREAD_RESULT THREAD_CALL NodePoller(void *);
 THREAD_RESULT THREAD_CALL PollManager(void *);
 THREAD_RESULT THREAD_CALL EventProcessor(void *);
 THREAD_RESULT THREAD_CALL WatchdogThread(void *);
-THREAD_RESULT THREAD_CALL ClientListener(void *);
-THREAD_RESULT THREAD_CALL ClientListenerIPv6(void *);
-THREAD_RESULT THREAD_CALL MobileDeviceListener(void *);
-THREAD_RESULT THREAD_CALL MobileDeviceListenerIPv6(void *);
+THREAD_RESULT THREAD_CALL ClientListenerThread(void *);
+THREAD_RESULT THREAD_CALL MobileDeviceListenerThread(void *);
 THREAD_RESULT THREAD_CALL ISCListener(void *);
 THREAD_RESULT THREAD_CALL LocalAdminListener(void *);
 THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *);
@@ -114,15 +115,16 @@ THREAD_RESULT THREAD_CALL BeaconPoller(void *);
 THREAD_RESULT THREAD_CALL JobManagerThread(void *);
 THREAD_RESULT THREAD_CALL UptimeCalculator(void *);
 THREAD_RESULT THREAD_CALL ReportingServerConnector(void *);
+THREAD_RESULT THREAD_CALL TunnelListenerThread(void *arg);
 
 /**
  * Global variables
  */
 TCHAR NXCORE_EXPORTABLE g_szConfigFile[MAX_PATH] = _T("{search}");
 TCHAR NXCORE_EXPORTABLE g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
-UINT32 g_dwLogRotationMode = NXLOG_ROTATION_BY_SIZE;
-UINT32 g_dwMaxLogSize = 16384 * 1024;
-UINT32 g_dwLogHistorySize = 4;
+UINT32 g_logRotationMode = NXLOG_ROTATION_BY_SIZE;
+UINT64 g_maxLogSize = 16384 * 1024;
+UINT32 g_logHistorySize = 4;
 TCHAR g_szDailyLogFileSuffix[64] = _T("");
 TCHAR NXCORE_EXPORTABLE g_szDumpDir[MAX_PATH] = DEFAULT_DUMP_DIR;
 char g_szCodePage[256] = ICONV_DEFAULT_CODEPAGE;
@@ -156,6 +158,8 @@ int g_requiredPolls = 1;
 DB_DRIVER g_dbDriver = NULL;
 ThreadPool NXCORE_EXPORTABLE *g_mainThreadPool = NULL;
 INT16 g_defaultAgentCacheMode = AGENT_CACHE_OFF;
+InetAddressList g_peerNodeAddrList;
+Condition g_dbPasswordReady(true);
 
 /**
  * Static data
@@ -163,13 +167,46 @@ INT16 g_defaultAgentCacheMode = AGENT_CACHE_OFF;
 static CONDITION m_condShutdown = INVALID_CONDITION_HANDLE;
 static THREAD m_thPollManager = INVALID_THREAD_HANDLE;
 static THREAD m_thSyncer = INVALID_THREAD_HANDLE;
+static THREAD s_tunnelListenerThread = INVALID_THREAD_HANDLE;
 static int m_nShutdownReason = SHUTDOWN_DEFAULT;
+static StringSet s_components;
 
 #ifndef _WIN32
 static pthread_t m_signalHandlerThread;
 #endif
 
 /**
+ * Register component
+ */
+void NXCORE_EXPORTABLE RegisterComponent(const TCHAR *id)
+{
+   s_components.add(id);
+}
+
+/**
+ * Check if component with given ID is registered
+ */
+bool NXCORE_EXPORTABLE IsComponentRegistered(const TCHAR *id)
+{
+   return s_components.contains(id);
+}
+
+/**
+ * Fill NXCP message with components data
+ */
+void FillComponentsMessage(NXCPMessage *msg)
+{
+   msg->setField(VID_NUM_COMPONENTS, (INT32)s_components.size());
+   UINT32 fieldId = VID_COMPONENT_LIST_BASE;
+   Iterator<const TCHAR> *it = s_components.iterator();
+   while(it->hasNext())
+   {
+      msg->setField(fieldId++, it->next());
+   }
+   delete it;
+}
+
+/**
  * Sleep for specified number of seconds or until system shutdown arrives
  * Function will return TRUE if shutdown event occurs
  *
@@ -296,6 +333,8 @@ static void LoadGlobalConfig()
                g_flags |= AF_ACTIVE_NETWORK_DISCOVERY;
    if (ConfigReadInt(_T("UseSNMPTrapsForDiscovery"), 0))
       g_flags |= AF_SNMP_TRAP_DISCOVERY;
+   if (ConfigReadInt(_T("UseSyslogForDiscovery"), 0))
+      g_flags |= AF_SYSLOG_DISCOVERY;
        if (ConfigReadInt(_T("ResolveNodeNames"), 1))
                g_flags |= AF_RESOLVE_NODE_NAMES;
        if (ConfigReadInt(_T("SyncNodeNamesWithDNS"), 0))
@@ -340,70 +379,86 @@ static void LoadGlobalConfig()
 /**
  * Initialize cryptografic functions
  */
-static BOOL InitCryptografy()
+static bool InitCryptografy()
 {
 #ifdef _WITH_ENCRYPTION
-       BOOL bResult = FALSE;
+       bool success = false;
 
    if (!InitCryptoLib(ConfigReadULong(_T("AllowedCiphers"), 0x7F)))
                return FALSE;
    nxlog_debug(4, _T("Supported ciphers: %s"), (const TCHAR *)NXCPGetSupportedCiphersAsText());
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+   OPENSSL_init_ssl(0, NULL);
+#else
    SSL_library_init();
    SSL_load_error_strings();
+#endif
 
-   TCHAR szKeyFile[MAX_PATH];
-       _tcscpy(szKeyFile, g_netxmsdDataDir);
-       _tcscat(szKeyFile, DFILE_KEYS);
-       g_pServerKey = LoadRSAKeys(szKeyFile);
-       if (g_pServerKey == NULL)
-       {
-          nxlog_debug(1, _T("Generating RSA key pair..."));
-               g_pServerKey = RSA_generate_key(NETXMS_RSA_KEYLEN, 17, NULL, 0);
-               if (g_pServerKey != NULL)
-               {
-                       int fd = _topen(szKeyFile, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600);
-                       if (fd != -1)
-                       {
-                               UINT32 dwLen = i2d_RSAPublicKey(g_pServerKey, NULL);
-                               dwLen += i2d_RSAPrivateKey(g_pServerKey, NULL);
-                               BYTE *pKeyBuffer = (BYTE *)malloc(dwLen);
-
-                               BYTE *pBufPos = pKeyBuffer;
-                               i2d_RSAPublicKey(g_pServerKey, &pBufPos);
-                               i2d_RSAPrivateKey(g_pServerKey, &pBufPos);
-                               write(fd, &dwLen, sizeof(UINT32));
-                               write(fd, pKeyBuffer, dwLen);
-
-                          BYTE hash[SHA1_DIGEST_SIZE];
-                               CalculateSHA1Hash(pKeyBuffer, dwLen, hash);
-                               write(fd, hash, SHA1_DIGEST_SIZE);
-
-                               close(fd);
-                               free(pKeyBuffer);
-                               bResult = TRUE;
-                       }
+   if (LoadServerCertificate(&g_pServerKey))
+   {
+      nxlog_debug(1, _T("Server certificate loaded"));
+   }
+   if (g_pServerKey != NULL)
+   {
+      nxlog_debug(1, _T("Using server certificate key"));
+      success = true;
+   }
+   else
+   {
+      TCHAR szKeyFile[MAX_PATH];
+      _tcscpy(szKeyFile, g_netxmsdDataDir);
+      _tcscat(szKeyFile, DFILE_KEYS);
+      g_pServerKey = LoadRSAKeys(szKeyFile);
+      if (g_pServerKey == NULL)
+      {
+         nxlog_debug(1, _T("Generating RSA key pair..."));
+         g_pServerKey = RSAGenerateKey(NETXMS_RSA_KEYLEN);
+         if (g_pServerKey != NULL)
+         {
+            int fd = _topen(szKeyFile, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600);
+            if (fd != -1)
+            {
+               UINT32 dwLen = i2d_RSAPublicKey(g_pServerKey, NULL);
+               dwLen += i2d_RSAPrivateKey(g_pServerKey, NULL);
+               BYTE *pKeyBuffer = (BYTE *)malloc(dwLen);
+
+               BYTE *pBufPos = pKeyBuffer;
+               i2d_RSAPublicKey(g_pServerKey, &pBufPos);
+               i2d_RSAPrivateKey(g_pServerKey, &pBufPos);
+               _write(fd, &dwLen, sizeof(UINT32));
+               _write(fd, pKeyBuffer, dwLen);
+
+               BYTE hash[SHA1_DIGEST_SIZE];
+               CalculateSHA1Hash(pKeyBuffer, dwLen, hash);
+               _write(fd, hash, SHA1_DIGEST_SIZE);
+
+               _close(fd);
+               free(pKeyBuffer);
+               success = true;
+            }
+            else
+            {
+               nxlog_debug(0, _T("Failed to open %s for writing"), szKeyFile);
+            }
+         }
          else
          {
-            nxlog_debug(0, _T("Failed to open %s for writing"), szKeyFile);
+            nxlog_debug(0, _T("Failed to generate RSA key"));
          }
-               }
+      }
       else
       {
-         nxlog_debug(0, _T("Failed to generate RSA key"));
+         success = true;
       }
-       }
-       else
-       {
-               bResult = TRUE;
-       }
+   }
 
        int iPolicy = ConfigReadInt(_T("DefaultEncryptionPolicy"), 1);
        if ((iPolicy < 0) || (iPolicy > 3))
                iPolicy = 1;
        SetAgentDEP(iPolicy);
 
-       return bResult;
+       return success;
 #else
        return TRUE;
 #endif
@@ -412,32 +467,77 @@ static BOOL InitCryptografy()
 /**
  * Check if process with given PID exists and is a NetXMS server process
  */
-static BOOL IsNetxmsdProcess(UINT32 dwPID)
+static bool IsNetxmsdProcess(UINT32 pid)
 {
 #ifdef _WIN32
-       HANDLE hProcess;
-       TCHAR szExtModule[MAX_PATH], szIntModule[MAX_PATH];
-       BOOL bRet = FALSE;
-
-       hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
+       bool result = false;
+       HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
        if (hProcess != NULL)
        {
+          TCHAR szExtModule[MAX_PATH], szIntModule[MAX_PATH];
                if ((GetModuleBaseName(hProcess, NULL, szExtModule, MAX_PATH) > 0) &&
-                               (GetModuleBaseName(GetCurrentProcess(), NULL, szIntModule, MAX_PATH) > 0))
+                        (GetModuleBaseName(GetCurrentProcess(), NULL, szIntModule, MAX_PATH) > 0))
                {
-                       bRet = !_tcsicmp(szExtModule, szIntModule);
+                       result = (_tcsicmp(szExtModule, szIntModule) == 0);
                }
                else
                {
                        // Cannot read process name, for safety assume that it's a server process
-                       bRet = TRUE;
+                       result = true;
                }
                CloseHandle(hProcess);
        }
-       return bRet;
+       return result;
+#else
+       return kill((pid_t)pid, 0) != -1;
+#endif
+}
+
+/**
+ * Check if remote netxmsd is running
+ */
+static bool PeerNodeIsRunning(const InetAddress& addr)
+{
+   bool result = false;
+
+   TCHAR keyFile[MAX_PATH];
+   _tcscpy(keyFile, g_netxmsdDataDir);
+   _tcscat(keyFile, DFILE_KEYS);
+   RSA *key = LoadRSAKeys(keyFile);
+
+   AgentConnection *ac = new AgentConnection(addr);
+   if (ac->connect(key))
+   {
+      TCHAR result[MAX_RESULT_LENGTH];
+#ifdef _WIN32
+      UINT32 rcc = ac->getParameter(_T("Process.Count(netxmsd.exe)"), MAX_RESULT_LENGTH, result);
 #else
-       return (kill((pid_t)dwPID, 0) != -1);
+      UINT32 rcc = ac->getParameter(_T("Process.Count(netxmsd)"), MAX_RESULT_LENGTH, result);
 #endif
+      ac->decRefCount();
+      if (key != NULL)
+         RSA_free(key);
+      if (rcc == ERR_SUCCESS)
+      {
+         return _tcstol(result, NULL, 10) > 0;
+      }
+   }
+   else
+   {
+      ac->decRefCount();
+      if (key != NULL)
+         RSA_free(key);
+   }
+
+   UINT16 port = (UINT16)ConfigReadInt(_T("ClientListenerPort"), SERVER_LISTEN_PORT_FOR_CLIENTS);
+   SOCKET s = ConnectToHost(addr, port, 5000);
+   if (s != INVALID_SOCKET)
+   {
+      shutdown(s, SHUT_RDWR);
+      closesocket(s);
+      result = true;
+   }
+   return result;
 }
 
 /**
@@ -510,19 +610,31 @@ static void OracleSessionInitCallback(DB_HANDLE hdb)
 }
 
 /**
+ * Get database password
+ */
+static void GetDatabasePassword()
+{
+   if (_tcscmp(g_szDbPassword, _T("?")))
+      return;
+
+   nxlog_write(MSG_WAITING_FOR_DB_PASSWORD, NXLOG_INFO, NULL);
+   g_dbPasswordReady.wait(INFINITE);
+   nxlog_debug(1, _T("Database password received"));
+}
+
+/**
  * Server initialization
  */
 BOOL NXCORE_EXPORTABLE Initialize()
 {
-       int i, iDBVersion;
-       TCHAR szInfo[256];
+       s_components.add(_T("CORE"));
 
        g_serverStartTime = time(NULL);
        srand((unsigned int)g_serverStartTime);
 
        if (!(g_flags & AF_USE_SYSLOG))
        {
-               if (!nxlog_set_rotation_policy((int)g_dwLogRotationMode, (int)g_dwMaxLogSize, (int)g_dwLogHistorySize, g_szDailyLogFileSuffix))
+               if (!nxlog_set_rotation_policy((int)g_logRotationMode, g_maxLogSize, (int)g_logHistorySize, g_szDailyLogFileSuffix))
                        if (!(g_flags & AF_DAEMON))
                                _tprintf(_T("WARNING: cannot set log rotation policy; using default values\n"));
        }
@@ -532,9 +644,9 @@ BOOL NXCORE_EXPORTABLE Initialize()
                    ((g_flags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
                    _T("LIBNXSRV.DLL"),
 #ifdef _WIN32
-                                      0, NULL, MSG_DEBUG))
+                                      0, NULL, MSG_DEBUG, MSG_DEBUG_TAG, MSG_OTHER))
 #else
-                                      g_dwNumMessages, g_szMessages, MSG_DEBUG))
+                                      g_dwNumMessages, g_szMessages, MSG_DEBUG, MSG_DEBUG_TAG, MSG_OTHER))
 #endif
    {
                _ftprintf(stderr, _T("FATAL ERROR: Cannot open log file\n"));
@@ -596,10 +708,17 @@ BOOL NXCORE_EXPORTABLE Initialize()
        if (g_dbDriver == NULL)
                return FALSE;
 
+   // Start local administrative interface listener if required
+   if (g_flags & AF_ENABLE_LOCAL_CONSOLE)
+      ThreadCreate(LocalAdminListener, 0, NULL);
+
+       // Wait for database password if needed
+       GetDatabasePassword();
+
        // Connect to database
        DB_HANDLE hdbBootstrap = NULL;
        TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
-       for(i = 0; ; i++)
+       for(int i = 0; ; i++)
        {
           hdbBootstrap = DBConnect(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, errorText);
                if ((hdbBootstrap != NULL) || (i == 5))
@@ -613,11 +732,18 @@ BOOL NXCORE_EXPORTABLE Initialize()
        }
        nxlog_debug(1, _T("Successfully connected to database %s@%s"), g_szDbName, g_szDbServer);
 
-       // Check database version
-       iDBVersion = DBGetSchemaVersion(hdbBootstrap);
-       if (iDBVersion != DB_FORMAT_VERSION)
+       // Check database schema version
+       INT32 schemaVersionMajor, schemaVersionMinor;
+       if (!DBGetSchemaVersion(hdbBootstrap, &schemaVersionMajor, &schemaVersionMinor))
        {
-               nxlog_write(MSG_WRONG_DB_VERSION, EVENTLOG_ERROR_TYPE, "dd", iDBVersion, DB_FORMAT_VERSION);
+          nxlog_write(MSG_UNABLE_TO_GET_DB_SCHEMA_VERSION, NXLOG_ERROR, NULL);
+      DBDisconnect(hdbBootstrap);
+      return FALSE;
+       }
+
+       if ((schemaVersionMajor != DB_SCHEMA_VERSION_MAJOR) || (schemaVersionMinor != DB_SCHEMA_VERSION_MINOR))
+       {
+               nxlog_write(MSG_WRONG_DB_SCHEMA_VERSION, NXLOG_ERROR, "dddd", schemaVersionMajor, schemaVersionMinor, DB_SCHEMA_VERSION_MAJOR, DB_SCHEMA_VERSION_MINOR);
                DBDisconnect(hdbBootstrap);
                return FALSE;
        }
@@ -644,52 +770,63 @@ BOOL NXCORE_EXPORTABLE Initialize()
 
    UINT32 lrt = ConfigReadULong(_T("LongRunningQueryThreshold"), 0);
    if (lrt != 0)
+   {
       DBSetLongRunningThreshold(lrt);
+      nxlog_write_generic(NXLOG_INFO, _T("Long running query threshold set at %d milliseconds"), lrt);
+   }
 
    MetaDataPreLoad();
 
        // Read server ID
-       MetaDataReadStr(_T("ServerID"), szInfo, 256, _T(""));
-       StrStrip(szInfo);
-       if (szInfo[0] != 0)
+   TCHAR buffer[256];
+       MetaDataReadStr(_T("ServerID"), buffer, 256, _T(""));
+       StrStrip(buffer);
+       if (buffer[0] != 0)
        {
-      g_serverId = _tcstoull(szInfo, NULL, 16);
+      g_serverId = _tcstoull(buffer, NULL, 16);
        }
        else
        {
                // Generate new ID
                g_serverId = ((UINT64)time(NULL) << 31) | (UINT64)((UINT32)rand() & 0x7FFFFFFF);
-      _sntprintf(szInfo, 256, UINT64X_FMT(_T("016")), g_serverId);
-               MetaDataWriteStr(_T("ServerID"), szInfo);
+      _sntprintf(buffer, 256, UINT64X_FMT(_T("016")), g_serverId);
+               MetaDataWriteStr(_T("ServerID"), buffer);
        }
-       nxlog_debug(1, _T("Server ID ") UINT64X_FMT(_T("016")), g_serverId);
+       nxlog_write_generic(NXLOG_INFO, _T("Server ID ") UINT64X_FMT(_T("016")), g_serverId);
 
        // Initialize locks
 retry_db_lock:
    InetAddress addr;
-       if (!InitLocks(&addr, szInfo))
+       if (!InitLocks(&addr, buffer))
        {
-      if (!addr.isValid())    // Some SQL problems
-               {
-                       nxlog_write(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
-               }
-               else     // Database already locked by another server instance
+               if (addr.isValidUnicast())     // Database already locked by another server instance
                {
                        // Check for lock from crashed/terminated local process
                        if (GetLocalIpAddr().equals(addr))
                        {
-                               UINT32 dwPID;
-
-                               dwPID = ConfigReadULong(_T("DBLockPID"), 0);
-                               if (!IsNetxmsdProcess(dwPID) || (dwPID == GetCurrentProcessId()))
+                               UINT32 pid = ConfigReadULong(_T("DBLockPID"), 0);
+                               if (!IsNetxmsdProcess(pid) || (pid == GetCurrentProcessId()))
                                {
                                        UnlockDB();
                                        nxlog_write(MSG_DB_LOCK_REMOVED, EVENTLOG_INFORMATION_TYPE, NULL);
                                        goto retry_db_lock;
                                }
                        }
-                       nxlog_write(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "As", &addr, szInfo);
+                       else if (g_peerNodeAddrList.hasAddress(addr))
+                       {
+                          if (!PeerNodeIsRunning(addr))
+                          {
+               UnlockDB();
+               nxlog_write(MSG_DB_LOCK_REMOVED, EVENTLOG_INFORMATION_TYPE, NULL);
+               goto retry_db_lock;
+                          }
+                       }
+                       nxlog_write(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "Is", &addr, buffer);
                }
+               else
+      {
+         nxlog_write(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
+      }
                return FALSE;
        }
        g_flags |= AF_DB_LOCKED;
@@ -737,12 +874,16 @@ retry_db_lock:
        // Load and compile scripts
        LoadScripts();
 
+       // Initialize persistent storage
+       PersistentStorageInit();
+
        // Initialize watchdog
        WatchdogInit();
 
        // Load modules
        if (!LoadNetXMSModules())
                return FALSE;   // Mandatory module not loaded
+       RegisterPredictionEngines();
 
        // Initialize mailer and SMS sender
        InitMailer();
@@ -765,6 +906,7 @@ retry_db_lock:
                return FALSE;
 
        // Initialize alarms
+   LoadAlarmCategories();
        if (!InitAlarmManager())
                return FALSE;
 
@@ -775,11 +917,6 @@ retry_db_lock:
                return FALSE;
        nxlog_debug(1, _T("Objects loaded and initialized"));
 
-       // Initialize situations
-       if (!SituationsInit())
-               return FALSE;
-       nxlog_debug(1, _T("Situations loaded and initialized"));
-
        // Initialize and load event actions
        if (!InitActions())
        {
@@ -793,8 +930,7 @@ retry_db_lock:
 
        // Initialize data collection subsystem
    LoadPerfDataStorageDrivers();
-       if (!InitDataCollector())
-               return FALSE;
+       InitDataCollector();
 
        InitLogAccess();
        FileUploadJob::init();
@@ -830,16 +966,11 @@ retry_db_lock:
                ThreadCreate(SNMPTrapReceiver, 0, NULL);
 
        // Start built-in syslog daemon
-       if (ConfigReadInt(_T("EnableSyslogDaemon"), 0))
-          StartSyslogServer();
+   StartSyslogServer();
 
        // Start database _T("lazy") write thread
        StartDBWriter();
 
-       // Start local administartive interface listener if required
-       if (ConfigReadInt(_T("EnableAdminInterface"), 1))
-               ThreadCreate(LocalAdminListener, 0, NULL);
-
        // Start beacon host poller
        ThreadCreate(BeaconPoller, 0, NULL);
 
@@ -860,20 +991,24 @@ retry_db_lock:
    RegisterSchedulerTaskHandler(_T("Maintenance.Leave"), MaintenanceModeLeave, SYSTEM_ACCESS_SCHEDULE_MAINTENANCE);
        RegisterSchedulerTaskHandler(_T("Policy.Deploy"), ScheduleDeployPolicy, 0); //No access right beacause it will be used only by server
        RegisterSchedulerTaskHandler(_T("Policy.Uninstall"), ScheduleUninstallPolicy, 0); //No access right beacause it will be used only by server
+   RegisterSchedulerTaskHandler(ALARM_SUMMARY_EMAIL_TASK_ID, SendAlarmSummaryEmail, 0); //No access right beacause it will be used only by server
    InitializeTaskScheduler();
 
+   // Send summary emails
+   if (ConfigReadInt(_T("EnableAlarmSummaryEmails"), 0))
+      EnableAlarmSummaryEmails();
+   else
+      RemoveScheduledTaskByHandlerId(ALARM_SUMMARY_EMAIL_TASK_ID);
+
        // Allow clients to connect
-       ThreadCreate(ClientListener, 0, NULL);
-#ifdef WITH_IPV6
-       ThreadCreate(ClientListenerIPv6, 0, NULL);
-#endif
+       ThreadCreate(ClientListenerThread, 0, NULL);
 
        // Allow mobile devices to connect
        InitMobileDeviceListeners();
-       ThreadCreate(MobileDeviceListener, 0, NULL);
-#ifdef WITH_IPV6
-       ThreadCreate(MobileDeviceListenerIPv6, 0, NULL);
-#endif
+       ThreadCreate(MobileDeviceListenerThread, 0, NULL);
+
+       // Agent tunnels
+   s_tunnelListenerThread = ThreadCreateEx(TunnelListenerThread, 0, NULL);
 
        // Start uptime calculator for SLM
        ThreadCreate(UptimeCalculator, 0, NULL);
@@ -894,6 +1029,8 @@ retry_db_lock:
    StartZMQConnector();
 #endif
 
+   ExecuteStartupScripts();
+
        g_flags |= AF_SERVER_INITIALIZED;
        nxlog_debug(1, _T("Server initialization completed"));
        return TRUE;
@@ -939,6 +1076,9 @@ void NXCORE_EXPORTABLE Shutdown()
        // Wait for critical threads
        ThreadJoin(m_thPollManager);
        ThreadJoin(m_thSyncer);
+       ThreadJoin(s_tunnelListenerThread);
+
+       CloseAgentTunnels();
 
        // Call shutdown functions for the modules
    // CALL_ALL_MODULES cannot be used here because it checks for shutdown flag
@@ -948,17 +1088,23 @@ void NXCORE_EXPORTABLE Shutdown()
                        g_pModuleList[i].pfShutdown();
        }
 
+   StopDataCollection();
+   StopObjectMaintenanceThreads();
+
    DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
-       SaveObjects(hdb);
+       SaveObjects(hdb, INVALID_INDEX, true);
        nxlog_debug(2, _T("All objects saved to database"));
-       SaveUsers(hdb);
+       SaveUsers(hdb, INVALID_INDEX);
        nxlog_debug(2, _T("All users saved to database"));
+   UpdatePStorageDatabase(hdb, INVALID_INDEX);
+       nxlog_debug(2, _T("All persistent storage values saved"));
        DBConnectionPoolReleaseConnection(hdb);
 
        StopDBWriter();
        nxlog_debug(1, _T("Database writer stopped"));
 
        CleanupUsers();
+       PersistentStorageDestroy();
 
        // Remove database lock
        UnlockDB();
@@ -975,8 +1121,7 @@ void NXCORE_EXPORTABLE Shutdown()
        ThreadPoolDestroy(g_agentConnectionThreadPool);
    ThreadPoolDestroy(g_mainThreadPool);
    MsgWaitQueue::shutdown();
-
-       delete g_pScriptLibrary;
+   WatchdogShutdown();
 
        nxlog_debug(1, _T("Server shutdown complete"));
        nxlog_close();
@@ -1006,10 +1151,12 @@ void NXCORE_EXPORTABLE FastShutdown()
        ConditionSet(m_condShutdown);
 
        DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
-       SaveObjects(hdb);
+       SaveObjects(hdb, INVALID_INDEX, true);
        DbgPrintf(2, _T("All objects saved to database"));
-       SaveUsers(hdb);
+       SaveUsers(hdb, INVALID_INDEX);
        DbgPrintf(2, _T("All users saved to database"));
+   UpdatePStorageDatabase(hdb, INVALID_INDEX);
+       DbgPrintf(2, _T("All persistent storage values saved"));
        DBConnectionPoolReleaseConnection(hdb);
 
        // Remove database lock first, because we have a chance to lose DB connection