ICMP ping timeout made configurable; code refactoring
[public/netxms.git] / src / server / core / main.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2013 NetXMS Team
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
27 #if !defined(_WIN32) && HAVE_READLINE_READLINE_H && HAVE_READLINE && !defined(UNICODE)
28 #include <readline/readline.h>
29 #include <readline/history.h>
30 #define USE_READLINE 1
31 #endif
32
33 #ifdef _WIN32
34 #include <errno.h>
35 #include <psapi.h>
36 #define open _open
37 #define write _write
38 #define close _close
39 #else
40 #include <signal.h>
41 #include <sys/wait.h>
42 #endif
43
44 /**
45 * Messages generated by mc.pl (for UNIX version only)
46 */
47 #ifndef _WIN32
48 extern unsigned int g_dwNumMessages;
49 extern const TCHAR *g_szMessages[];
50 #endif
51
52 /**
53 * Shutdown reasons
54 */
55 #define SHUTDOWN_DEFAULT 0
56 #define SHUTDOWN_FROM_CONSOLE 1
57 #define SHUTDOWN_BY_SIGNAL 2
58
59 /**
60 * Externals
61 */
62 extern Queue g_statusPollQueue;
63 extern Queue g_configPollQueue;
64 extern Queue g_topologyPollQueue;
65 extern Queue g_routePollQueue;
66 extern Queue g_discoveryPollQueue;
67 extern Queue g_nodePollerQueue;
68 extern Queue g_conditionPollerQueue;
69 extern Queue *g_pItemQueue;
70
71 void InitClientListeners();
72 void InitMobileDeviceListeners();
73 void InitCertificates();
74 void InitUsers();
75 void CleanupUsers();
76
77 /**
78 * Thread functions
79 */
80 THREAD_RESULT THREAD_CALL HouseKeeper(void *);
81 THREAD_RESULT THREAD_CALL Syncer(void *);
82 THREAD_RESULT THREAD_CALL NodePoller(void *);
83 THREAD_RESULT THREAD_CALL PollManager(void *);
84 THREAD_RESULT THREAD_CALL EventProcessor(void *);
85 THREAD_RESULT THREAD_CALL WatchdogThread(void *);
86 THREAD_RESULT THREAD_CALL ClientListener(void *);
87 THREAD_RESULT THREAD_CALL ClientListenerIPv6(void *);
88 THREAD_RESULT THREAD_CALL MobileDeviceListener(void *);
89 THREAD_RESULT THREAD_CALL MobileDeviceListenerIPv6(void *);
90 THREAD_RESULT THREAD_CALL ISCListener(void *);
91 THREAD_RESULT THREAD_CALL LocalAdminListener(void *);
92 THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *);
93 THREAD_RESULT THREAD_CALL SyslogDaemon(void *);
94 THREAD_RESULT THREAD_CALL BeaconPoller(void *);
95 THREAD_RESULT THREAD_CALL JobManagerThread(void *);
96 THREAD_RESULT THREAD_CALL UptimeCalculator(void *);
97 THREAD_RESULT THREAD_CALL ReportingServerConnector(void *);
98
99 /**
100 * Global variables
101 */
102 TCHAR NXCORE_EXPORTABLE g_szConfigFile[MAX_PATH] = DEFAULT_CONFIG_FILE;
103 TCHAR NXCORE_EXPORTABLE g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
104 UINT32 g_dwLogRotationMode = NXLOG_ROTATION_BY_SIZE;
105 UINT32 g_dwMaxLogSize = 16384 * 1024;
106 UINT32 g_dwLogHistorySize = 4;
107 TCHAR g_szDailyLogFileSuffix[64] = _T("");
108 TCHAR NXCORE_EXPORTABLE g_szDumpDir[MAX_PATH] = DEFAULT_DUMP_DIR;
109 char g_szCodePage[256] = ICONV_DEFAULT_CODEPAGE_A;
110 TCHAR NXCORE_EXPORTABLE g_szListenAddress[MAX_PATH] = _T("0.0.0.0");
111 #ifndef _WIN32
112 TCHAR NXCORE_EXPORTABLE g_szPIDFile[MAX_PATH] = _T("/var/run/netxmsd.pid");
113 #endif
114 DB_HANDLE g_hCoreDB = 0;
115 UINT32 g_dwDiscoveryPollingInterval;
116 UINT32 g_dwStatusPollingInterval;
117 UINT32 g_dwConfigurationPollingInterval;
118 UINT32 g_dwRoutingTableUpdateInterval;
119 UINT32 g_dwTopologyPollingInterval;
120 UINT32 g_dwConditionPollingInterval;
121 UINT32 g_icmpPingSize;
122 UINT32 g_icmpPingTimeout = 1500; // ICMP ping timeout (milliseconds)
123 UINT32 g_auditFlags;
124 UINT32 g_slmPollingInterval;
125 TCHAR g_szDataDir[MAX_PATH] = _T("");
126 TCHAR g_szLibDir[MAX_PATH] = DEFAULT_LIBDIR;
127 TCHAR g_szJavaLibDir[MAX_PATH] = DEFAULT_JAVA_LIBDIR;
128 TCHAR NXCORE_EXPORTABLE g_szJavaPath[MAX_DB_NAME] = _T("java");
129 int g_nDBSyntax = DB_SYNTAX_UNKNOWN;
130 UINT32 NXCORE_EXPORTABLE g_processAffinityMask = DEFAULT_AFFINITY_MASK;
131 QWORD g_qwServerId;
132 RSA *g_pServerKey = NULL;
133 time_t g_serverStartTime = 0;
134 UINT32 g_lockTimeout = 60000; // Default timeout for acquiring mutex
135 UINT32 g_agentCommandTimeout = 4000; // Default timeout for requests to agent
136 UINT32 g_thresholdRepeatInterval = 0; // Disabled by default
137 int g_requiredPolls = 1;
138 DB_DRIVER g_dbDriver = NULL;
139
140 /**
141 * Static data
142 */
143 static CONDITION m_condShutdown = INVALID_CONDITION_HANDLE;
144 static THREAD m_thPollManager = INVALID_THREAD_HANDLE;
145 static THREAD m_thHouseKeeper = INVALID_THREAD_HANDLE;
146 static THREAD m_thSyncer = INVALID_THREAD_HANDLE;
147 static THREAD m_thSyslogDaemon = INVALID_THREAD_HANDLE;
148 static int m_nShutdownReason = SHUTDOWN_DEFAULT;
149
150 #ifndef _WIN32
151 static pthread_t m_signalHandlerThread;
152 #endif
153
154 /**
155 * Sleep for specified number of seconds or until system shutdown arrives
156 * Function will return TRUE if shutdown event occurs
157 *
158 * @param seconds seconds to sleep
159 * @return true if server is shutting down
160 */
161 BOOL NXCORE_EXPORTABLE SleepAndCheckForShutdown(int seconds)
162 {
163 return ConditionWait(m_condShutdown, seconds * 1000);
164 }
165
166 /**
167 * Disconnect from database (exportable function for startup module)
168 */
169 void NXCORE_EXPORTABLE ShutdownDB()
170 {
171 if (g_hCoreDB != NULL)
172 DBDisconnect(g_hCoreDB);
173 DBUnloadDriver(g_dbDriver);
174 }
175
176 /**
177 * Check data directory for existence
178 */
179 static BOOL CheckDataDir()
180 {
181 TCHAR szBuffer[MAX_PATH];
182
183 if (_tchdir(g_szDataDir) == -1)
184 {
185 nxlog_write(MSG_INVALID_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", g_szDataDir);
186 return FALSE;
187 }
188
189 #ifdef _WIN32
190 #define MKDIR(name) _tmkdir(name)
191 #else
192 #define MKDIR(name) _tmkdir(name, 0700)
193 #endif
194
195 // Create directory for mib files if it doesn't exist
196 _tcscpy(szBuffer, g_szDataDir);
197 _tcscat(szBuffer, DDIR_MIBS);
198 if (MKDIR(szBuffer) == -1)
199 if (errno != EEXIST)
200 {
201 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
202 return FALSE;
203 }
204
205 // Create directory for package files if it doesn't exist
206 _tcscpy(szBuffer, g_szDataDir);
207 _tcscat(szBuffer, DDIR_PACKAGES);
208 if (MKDIR(szBuffer) == -1)
209 if (errno != EEXIST)
210 {
211 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
212 return FALSE;
213 }
214
215 // Create directory for map background images if it doesn't exist
216 _tcscpy(szBuffer, g_szDataDir);
217 _tcscat(szBuffer, DDIR_BACKGROUNDS);
218 if (MKDIR(szBuffer) == -1)
219 if (errno != EEXIST)
220 {
221 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
222 return FALSE;
223 }
224
225 // Create directory for image library is if does't exists
226 _tcscpy(szBuffer, g_szDataDir);
227 _tcscat(szBuffer, DDIR_IMAGES);
228 if (MKDIR(szBuffer) == -1)
229 {
230 if (errno != EEXIST)
231 {
232 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
233 return FALSE;
234 }
235 }
236
237 // Create directory for shared file store if does't exists
238 _tcscpy(szBuffer, g_szDataDir);
239 _tcscat(szBuffer, DDIR_SHARED_FILES);
240 if (MKDIR(szBuffer) == -1)
241 {
242 if (errno != EEXIST)
243 {
244 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
245 return FALSE;
246 }
247 }
248
249 // Create directory for file store if does't exists
250 _tcscpy(szBuffer, g_szDataDir);
251 _tcscat(szBuffer, DDIR_FILES);
252 if (MKDIR(szBuffer) == -1)
253 {
254 if (errno != EEXIST)
255 {
256 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
257 return FALSE;
258 }
259 }
260
261 // Create directory for reports if does't exists
262 _tcscpy(szBuffer, g_szDataDir);
263 _tcscat(szBuffer, DDIR_REPORTS);
264 if (MKDIR(szBuffer) == -1)
265 {
266 if (errno != EEXIST)
267 {
268 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
269 return FALSE;
270 }
271 }
272
273 #undef MKDIR
274
275 return TRUE;
276 }
277
278 /**
279 * Load global configuration parameters
280 */
281 static void LoadGlobalConfig()
282 {
283 g_dwDiscoveryPollingInterval = ConfigReadInt(_T("DiscoveryPollingInterval"), 900);
284 g_dwStatusPollingInterval = ConfigReadInt(_T("StatusPollingInterval"), 60);
285 g_dwConfigurationPollingInterval = ConfigReadInt(_T("ConfigurationPollingInterval"), 3600);
286 g_dwRoutingTableUpdateInterval = ConfigReadInt(_T("RoutingTableUpdateInterval"), 300);
287 g_dwTopologyPollingInterval = ConfigReadInt(_T("TopologyPollingInterval"), 1800);
288 g_dwConditionPollingInterval = ConfigReadInt(_T("ConditionPollingInterval"), 60);
289 g_slmPollingInterval = ConfigReadInt(_T("SlmPollingInterval"), 60);
290 if (ConfigReadInt(_T("DeleteEmptySubnets"), 1))
291 g_dwFlags |= AF_DELETE_EMPTY_SUBNETS;
292 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
293 g_dwFlags |= AF_ENABLE_SNMP_TRAPD;
294 if (ConfigReadInt(_T("EnableZoning"), 0))
295 g_dwFlags |= AF_ENABLE_ZONING;
296 if (ConfigReadInt(_T("EnableObjectTransactions"), 0))
297 g_dwFlags |= AF_ENABLE_OBJECT_TRANSACTIONS;
298 if (ConfigReadInt(_T("EnableMultipleDBConnections"), 1))
299 {
300 // SQLite has troubles with multiple connections to the same database
301 // from different threads, and it does not speed up database access
302 // anyway, so we will not enable multiple connections for SQLite
303 if (g_nDBSyntax != DB_SYNTAX_SQLITE)
304 {
305 g_dwFlags |= AF_ENABLE_MULTIPLE_DB_CONN;
306 }
307 else
308 {
309 DbgPrintf(1, _T("Configuration parameter EnableMultipleDBConnections ignored because database engine is SQLite"));
310 }
311 }
312 if (ConfigReadInt(_T("RunNetworkDiscovery"), 0))
313 g_dwFlags |= AF_ENABLE_NETWORK_DISCOVERY;
314 if (ConfigReadInt(_T("ActiveNetworkDiscovery"), 0))
315 g_dwFlags |= AF_ACTIVE_NETWORK_DISCOVERY;
316 if (ConfigReadInt(_T("ResolveNodeNames"), 1))
317 g_dwFlags |= AF_RESOLVE_NODE_NAMES;
318 if (ConfigReadInt(_T("SyncNodeNamesWithDNS"), 0))
319 g_dwFlags |= AF_SYNC_NODE_NAMES_WITH_DNS;
320 if (ConfigReadInt(_T("InternalCA"), 0))
321 g_dwFlags |= AF_INTERNAL_CA;
322 if (ConfigReadInt(_T("CheckTrustedNodes"), 1))
323 g_dwFlags |= AF_CHECK_TRUSTED_NODES;
324 if (ConfigReadInt(_T("EnableNXSLContainerFunctions"), 1))
325 g_dwFlags |= AF_ENABLE_NXSL_CONTAINER_FUNCS;
326 if (ConfigReadInt(_T("UseFQDNForNodeNames"), 1))
327 g_dwFlags |= AF_USE_FQDN_FOR_NODE_NAMES;
328 if (ConfigReadInt(_T("ApplyDCIFromTemplateToDisabledDCI"), 0))
329 g_dwFlags |= AF_APPLY_TO_DISABLED_DCI_FROM_TEMPLATE;
330
331 if (g_szDataDir[0] == 0)
332 {
333 ConfigReadStr(_T("DataDirectory"), g_szDataDir, MAX_PATH, DEFAULT_DATA_DIR);
334 DbgPrintf(1, _T("Data directory set to %s from server configuration variable"), g_szDataDir);
335 }
336 else
337 {
338 DbgPrintf(1, _T("Using data directory %s"), g_szDataDir);
339 }
340
341 g_icmpPingTimeout = ConfigReadInt(_T("IcmpPingTimeout"), 1500);
342 g_icmpPingSize = ConfigReadInt(_T("IcmpPingSize"), 46);
343 g_lockTimeout = ConfigReadInt(_T("LockTimeout"), 60000);
344 g_dwSNMPTimeout = ConfigReadInt(_T("SNMPRequestTimeout"), 2000);
345 g_agentCommandTimeout = ConfigReadInt(_T("AgentCommandTimeout"), 4000);
346 g_thresholdRepeatInterval = ConfigReadInt(_T("ThresholdRepeatInterval"), 0);
347 g_requiredPolls = ConfigReadInt(_T("PollCountForStatusChange"), 1);
348 }
349
350 /**
351 * Initialize cryptografic functions
352 */
353 static BOOL InitCryptografy()
354 {
355 #ifdef _WITH_ENCRYPTION
356 TCHAR szKeyFile[MAX_PATH];
357 BOOL bResult = FALSE;
358 int fd, iPolicy;
359 UINT32 dwLen;
360 BYTE *pBufPos, *pKeyBuffer, hash[SHA1_DIGEST_SIZE];
361
362 if (!InitCryptoLib(ConfigReadULong(_T("AllowedCiphers"), 0x1F)))
363 return FALSE;
364
365 SSL_library_init();
366 SSL_load_error_strings();
367
368 _tcscpy(szKeyFile, g_szDataDir);
369 _tcscat(szKeyFile, DFILE_KEYS);
370 fd = _topen(szKeyFile, O_RDONLY | O_BINARY);
371 g_pServerKey = LoadRSAKeys(szKeyFile);
372 if (g_pServerKey == NULL)
373 {
374 DbgPrintf(1, _T("Generating RSA key pair..."));
375 g_pServerKey = RSA_generate_key(NETXMS_RSA_KEYLEN, 17, NULL, 0);
376 if (g_pServerKey != NULL)
377 {
378 fd = _topen(szKeyFile, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600);
379 if (fd != -1)
380 {
381 dwLen = i2d_RSAPublicKey(g_pServerKey, NULL);
382 dwLen += i2d_RSAPrivateKey(g_pServerKey, NULL);
383 pKeyBuffer = (BYTE *)malloc(dwLen);
384
385 pBufPos = pKeyBuffer;
386 i2d_RSAPublicKey(g_pServerKey, &pBufPos);
387 i2d_RSAPrivateKey(g_pServerKey, &pBufPos);
388 write(fd, &dwLen, sizeof(UINT32));
389 write(fd, pKeyBuffer, dwLen);
390
391 CalculateSHA1Hash(pKeyBuffer, dwLen, hash);
392 write(fd, hash, SHA1_DIGEST_SIZE);
393
394 close(fd);
395 free(pKeyBuffer);
396 bResult = TRUE;
397 }
398 else
399 {
400 DbgPrintf(0, _T("Failed to open %s for writing"), szKeyFile);
401 }
402 }
403 else
404 {
405 DbgPrintf(0, _T("Failed to generate RSA key"));
406 }
407 }
408 else
409 {
410 bResult = TRUE;
411 }
412
413 iPolicy = ConfigReadInt(_T("DefaultEncryptionPolicy"), 1);
414 if ((iPolicy < 0) || (iPolicy > 3))
415 iPolicy = 1;
416 SetAgentDEP(iPolicy);
417
418 return bResult;
419 #else
420 return TRUE;
421 #endif
422 }
423
424 /**
425 * Check if process with given PID exists and is a NetXMS server process
426 */
427 static BOOL IsNetxmsdProcess(UINT32 dwPID)
428 {
429 #ifdef _WIN32
430 HANDLE hProcess;
431 TCHAR szExtModule[MAX_PATH], szIntModule[MAX_PATH];
432 BOOL bRet = FALSE;
433
434 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
435 if (hProcess != NULL)
436 {
437 if ((GetModuleBaseName(hProcess, NULL, szExtModule, MAX_PATH) > 0) &&
438 (GetModuleBaseName(GetCurrentProcess(), NULL, szIntModule, MAX_PATH) > 0))
439 {
440 bRet = !_tcsicmp(szExtModule, szIntModule);
441 }
442 else
443 {
444 // Cannot read process name, for safety assume that it's a server process
445 bRet = TRUE;
446 }
447 CloseHandle(hProcess);
448 }
449 return bRet;
450 #else
451 return (kill((pid_t)dwPID, 0) != -1);
452 #endif
453 }
454
455 /**
456 * Database event handler
457 */
458 static void DBEventHandler(DWORD dwEvent, const WCHAR *pszArg1, const WCHAR *pszArg2, void *userArg)
459 {
460 if (!(g_dwFlags & AF_SERVER_INITIALIZED))
461 return; // Don't try to do anything if server is not ready yet
462
463 switch(dwEvent)
464 {
465 case DBEVENT_CONNECTION_LOST:
466 PostEvent(EVENT_DB_CONNECTION_LOST, g_dwMgmtNode, NULL);
467 g_dwFlags |= AF_DB_CONNECTION_LOST;
468 NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, FALSE);
469 break;
470 case DBEVENT_CONNECTION_RESTORED:
471 PostEvent(EVENT_DB_CONNECTION_RESTORED, g_dwMgmtNode, NULL);
472 g_dwFlags &= ~AF_DB_CONNECTION_LOST;
473 NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, TRUE);
474 break;
475 case DBEVENT_QUERY_FAILED:
476 PostEvent(EVENT_DB_QUERY_FAILED, g_dwMgmtNode, "uu", pszArg1, pszArg2);
477 break;
478 default:
479 break;
480 }
481 }
482
483 /**
484 * Send console message to session with open console
485 */
486 static void SendConsoleMessage(ClientSession *session, void *arg)
487 {
488 if (session->isConsoleOpen())
489 {
490 CSCPMessage msg;
491
492 msg.SetCode(CMD_ADM_MESSAGE);
493 msg.SetVariable(VID_MESSAGE, (TCHAR *)arg);
494 session->postMessage(&msg);
495 }
496 }
497
498 /**
499 * Console writer
500 */
501 static void LogConsoleWriter(const TCHAR *format, ...)
502 {
503 TCHAR buffer[8192];
504 va_list args;
505
506 va_start(args, format);
507 _vsntprintf(buffer, 8192, format, args);
508 buffer[8191] = 0;
509 va_end(args);
510
511 WriteToTerminal(buffer);
512
513 EnumerateClientSessions(SendConsoleMessage, buffer);
514 }
515
516 /**
517 * Server initialization
518 */
519 BOOL NXCORE_EXPORTABLE Initialize()
520 {
521 int i, iDBVersion;
522 UINT32 dwAddr;
523 TCHAR szInfo[256];
524
525 g_serverStartTime = time(NULL);
526 srand((unsigned int)g_serverStartTime);
527
528 if (!(g_dwFlags & AF_USE_SYSLOG))
529 {
530 if (!nxlog_set_rotation_policy((int)g_dwLogRotationMode, (int)g_dwMaxLogSize, (int)g_dwLogHistorySize, g_szDailyLogFileSuffix))
531 if (!(g_dwFlags & AF_DAEMON))
532 _tprintf(_T("WARNING: cannot set log rotation policy; using default values\n"));
533 }
534 nxlog_open((g_dwFlags & AF_USE_SYSLOG) ? NETXMSD_SYSLOG_NAME : g_szLogFile,
535 ((g_dwFlags & AF_USE_SYSLOG) ? NXLOG_USE_SYSLOG : 0) |
536 ((g_dwFlags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
537 _T("LIBNXSRV.DLL"),
538 #ifdef _WIN32
539 0, NULL);
540 #else
541 g_dwNumMessages, g_szMessages);
542 #endif
543 nxlog_set_console_writer(LogConsoleWriter);
544
545 // Set code page
546 #ifndef _WIN32
547 if (SetDefaultCodepage(g_szCodePage))
548 {
549 DbgPrintf(1, _T("Code page set to %hs"), g_szCodePage);
550 }
551 else
552 {
553 nxlog_write(MSG_CODEPAGE_ERROR, EVENTLOG_WARNING_TYPE, "m", g_szCodePage);
554 }
555 #endif
556
557 // Set process affinity mask
558 if (g_processAffinityMask != DEFAULT_AFFINITY_MASK)
559 {
560 #ifdef _WIN32
561 if (SetProcessAffinityMask(GetCurrentProcess(), g_processAffinityMask))
562 DbgPrintf(1, _T("Process affinity mask set to 0x%08X"), g_processAffinityMask);
563 #else
564 nxlog_write(MSG_SET_PROCESS_AFFINITY_NOT_SUPPORTED, EVENTLOG_WARNING_TYPE, NULL);
565 #endif
566 }
567
568 #ifdef _WIN32
569 WSADATA wsaData;
570 int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
571 if (wrc != 0)
572 {
573 nxlog_write(MSG_WSASTARTUP_FAILED, EVENTLOG_ERROR_TYPE, "e", wrc);
574 return FALSE;
575 }
576 #endif
577
578 InitLocalNetInfo();
579
580 // Create queue for delayed SQL queries
581 g_pLazyRequestQueue = new Queue(256, 64);
582 g_pIDataInsertQueue = new Queue(1024, 1024);
583
584 // Initialize database driver and connect to database
585 DBSetDebugPrintCallback(DbgPrintf2);
586 if (!DBInit(MSG_OTHER, (g_dwFlags & AF_LOG_SQL_ERRORS) ? MSG_SQL_ERROR : 0))
587 return FALSE;
588 g_dbDriver = DBLoadDriver(g_szDbDriver, g_szDbDrvParams, (g_debugLevel >= 9), DBEventHandler, NULL);
589 if (g_dbDriver == NULL)
590 return FALSE;
591
592 // Connect to database
593 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
594 for(i = 0; ; i++)
595 {
596 g_hCoreDB = DBConnect(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, errorText);
597 if ((g_hCoreDB != NULL) || (i == 5))
598 break;
599 ThreadSleep(5);
600 }
601 if (g_hCoreDB == NULL)
602 {
603 nxlog_write(MSG_DB_CONNFAIL, EVENTLOG_ERROR_TYPE, "s", errorText);
604 return FALSE;
605 }
606 DbgPrintf(1, _T("Successfully connected to database %s@%s"), g_szDbName, g_szDbServer);
607
608 // Check database version
609 iDBVersion = DBGetSchemaVersion(g_hCoreDB);
610 if (iDBVersion != DB_FORMAT_VERSION)
611 {
612 nxlog_write(MSG_WRONG_DB_VERSION, EVENTLOG_ERROR_TYPE, "dd", iDBVersion, DB_FORMAT_VERSION);
613 return FALSE;
614 }
615
616 int baseSize = ConfigReadInt(_T("ConnectionPoolBaseSize"), 5);
617 int maxSize = ConfigReadInt(_T("ConnectionPoolMaxSize"), 20);
618 int cooldownTime = ConfigReadInt(_T("ConnectionPoolCooldownTime"), 300);
619 DBConnectionPoolStartup(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, baseSize, maxSize, cooldownTime, 0, g_hCoreDB);
620 g_dwFlags |= AF_DB_CONNECTION_POOL_READY;
621
622 // Read database syntax
623 g_nDBSyntax = DBGetSyntax(g_hCoreDB);
624
625 // Read server ID
626 ConfigReadStr(_T("ServerID"), szInfo, 256, _T(""));
627 StrStrip(szInfo);
628 if (szInfo[0] != 0)
629 {
630 StrToBin(szInfo, (BYTE *)&g_qwServerId, sizeof(QWORD));
631 }
632 else
633 {
634 // Generate new ID
635 g_qwServerId = (((QWORD)time(NULL)) << 32) | rand();
636 BinToStr((BYTE *)&g_qwServerId, sizeof(QWORD), szInfo);
637 ConfigWriteStr(_T("ServerID"), szInfo, TRUE);
638 }
639
640 // Initialize locks
641 retry_db_lock:
642 if (!InitLocks(&dwAddr, szInfo))
643 {
644 if (dwAddr == UNLOCKED) // Some SQL problems
645 {
646 nxlog_write(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
647 }
648 else // Database already locked by another server instance
649 {
650 // Check for lock from crashed/terminated local process
651 if (dwAddr == GetLocalIpAddr())
652 {
653 UINT32 dwPID;
654
655 dwPID = ConfigReadULong(_T("DBLockPID"), 0);
656 if (!IsNetxmsdProcess(dwPID) || (dwPID == GetCurrentProcessId()))
657 {
658 UnlockDB();
659 nxlog_write(MSG_DB_LOCK_REMOVED, EVENTLOG_INFORMATION_TYPE, NULL);
660 goto retry_db_lock;
661 }
662 }
663 nxlog_write(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "as", dwAddr, szInfo);
664 }
665 return FALSE;
666 }
667 g_dwFlags |= AF_DB_LOCKED;
668
669 // Load global configuration parameters
670 LoadGlobalConfig();
671 CASReadSettings();
672 DbgPrintf(1, _T("Global configuration loaded"));
673
674 // Check data directory
675 if (!CheckDataDir())
676 return FALSE;
677
678 // Initialize cryptografy
679 if (!InitCryptografy())
680 {
681 nxlog_write(MSG_INIT_CRYPTO_FAILED, EVENTLOG_ERROR_TYPE, NULL);
682 return FALSE;
683 }
684
685 // Initialize certificate store and CA
686 InitCertificates();
687
688 // Create synchronization stuff
689 m_condShutdown = ConditionCreate(TRUE);
690
691 // Setup unique identifiers table
692 if (!InitIdTable())
693 return FALSE;
694 DbgPrintf(2, _T("ID table created"));
695
696 // Update status for unfinished jobs in job history
697 DBQuery(g_hCoreDB, _T("UPDATE job_history SET status=4,failure_message='Aborted due to server shutdown or crash' WHERE status NOT IN (3,4,5)"));
698
699 // Load and compile scripts
700 LoadScripts();
701
702 // Initialize watchdog
703 WatchdogInit();
704
705 // Load modules
706 if (!LoadNetXMSModules())
707 return FALSE; // Mandatory module not loaded
708
709 // Initialize mailer and SMS sender
710 InitMailer();
711 InitSMSSender();
712
713 // Load users from database
714 InitUsers();
715 if (!LoadUsers())
716 {
717 nxlog_write(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
718 return FALSE;
719 }
720 DbgPrintf(2, _T("User accounts loaded"));
721
722 // Initialize audit
723 InitAuditLog();
724
725 // Initialize objects infrastructure and load objects from database
726 LoadNetworkDeviceDrivers();
727 ObjectsInit();
728 if (!LoadObjects())
729 return FALSE;
730 DbgPrintf(1, _T("Objects loaded and initialized"));
731
732 // Initialize situations
733 if (!SituationsInit())
734 return FALSE;
735 DbgPrintf(1, _T("Situations loaded and initialized"));
736
737 // Initialize and load event actions
738 if (!InitActions())
739 {
740 nxlog_write(MSG_ACTION_INIT_ERROR, EVENTLOG_ERROR_TYPE, NULL);
741 return FALSE;
742 }
743
744 // Initialize event handling subsystem
745 if (!InitEventSubsystem())
746 return FALSE;
747
748 // Initialize alarms
749 if (!g_alarmMgr.init())
750 return FALSE;
751
752 // Initialize data collection subsystem
753 if (!InitDataCollector())
754 return FALSE;
755
756 InitLogAccess();
757 FileUploadJob::init();
758 InitMappingTables();
759
760 // Check if management node object presented in database
761 CheckForMgmtNode();
762 if (g_dwMgmtNode == 0)
763 {
764 nxlog_write(MSG_CANNOT_FIND_SELF, EVENTLOG_ERROR_TYPE, NULL);
765 return FALSE;
766 }
767
768 // Start threads
769 ThreadCreate(WatchdogThread, 0, NULL);
770 ThreadCreate(NodePoller, 0, NULL);
771 ThreadCreate(JobManagerThread, 0, NULL);
772 m_thSyncer = ThreadCreateEx(Syncer, 0, NULL);
773 m_thHouseKeeper = ThreadCreateEx(HouseKeeper, 0, NULL);
774 m_thPollManager = ThreadCreateEx(PollManager, 0, NULL);
775
776 // Start event processor
777 ThreadCreate(EventProcessor, 0, NULL);
778
779 // Start SNMP trapper
780 InitTraps();
781 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
782 ThreadCreate(SNMPTrapReceiver, 0, NULL);
783
784 // Start built-in syslog daemon
785 if (ConfigReadInt(_T("EnableSyslogDaemon"), 0))
786 m_thSyslogDaemon = ThreadCreateEx(SyslogDaemon, 0, NULL);
787
788 // Start database _T("lazy") write thread
789 StartDBWriter();
790
791 // Start local administartive interface listener if required
792 if (ConfigReadInt(_T("EnableAdminInterface"), 1))
793 ThreadCreate(LocalAdminListener, 0, NULL);
794
795 // Start beacon host poller
796 ThreadCreate(BeaconPoller, 0, NULL);
797
798 // Start inter-server communication listener
799 if (ConfigReadInt(_T("EnableISCListener"), 0))
800 ThreadCreate(ISCListener, 0, NULL);
801
802 // Start reporting server connector
803 if (ConfigReadInt(_T("EnableReportingServer"), 0))
804 ThreadCreate(ReportingServerConnector, 0, NULL);
805
806 // Allow clients to connect
807 InitClientListeners();
808 ThreadCreate(ClientListener, 0, NULL);
809 #ifdef WITH_IPV6
810 ThreadCreate(ClientListenerIPv6, 0, NULL);
811 #endif
812
813 // Allow mobile devices to connect
814 InitMobileDeviceListeners();
815 ThreadCreate(MobileDeviceListener, 0, NULL);
816 #ifdef WITH_IPV6
817 ThreadCreate(MobileDeviceListenerIPv6, 0, NULL);
818 #endif
819
820 // Start uptime calculator for SLM
821 ThreadCreate(UptimeCalculator, 0, NULL);
822
823 DbgPrintf(2, _T("Java VM: %s"), g_szJavaPath);
824 DbgPrintf(2, _T("Java LIBDIR: %s"), g_szJavaLibDir);
825 DbgPrintf(2, _T("LIBDIR: %s"), g_szLibDir);
826
827 // Call shutdown functions for the modules
828 for(UINT32 i = 0; i < g_dwNumModules; i++)
829 {
830 if (g_pModuleList[i].pfServerStarted != NULL)
831 g_pModuleList[i].pfServerStarted();
832 }
833
834 g_dwFlags |= AF_SERVER_INITIALIZED;
835 DbgPrintf(1, _T("Server initialization completed"));
836 return TRUE;
837 }
838
839 /**
840 * Server shutdown
841 */
842 void NXCORE_EXPORTABLE Shutdown()
843 {
844 // Notify clients
845 NotifyClientSessions(NX_NOTIFY_SHUTDOWN, 0);
846
847 nxlog_write(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
848 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
849 ConditionSet(m_condShutdown);
850
851 #ifndef _WIN32
852 if (IsStandalone() && (m_nShutdownReason != SHUTDOWN_BY_SIGNAL))
853 {
854 pthread_kill(m_signalHandlerThread, SIGUSR1); // Terminate signal handler
855 }
856 #endif
857
858 // Call shutdown functions for the modules
859 for(UINT32 i = 0; i < g_dwNumModules; i++)
860 {
861 if (g_pModuleList[i].pfShutdown != NULL)
862 g_pModuleList[i].pfShutdown();
863 }
864
865 // Stop event processor
866 g_pEventQueue->Clear();
867 g_pEventQueue->Put(INVALID_POINTER_VALUE);
868
869 ShutdownMailer();
870 ShutdownSMSSender();
871
872 ThreadSleep(1); // Give other threads a chance to terminate in a safe way
873 DbgPrintf(2, _T("All threads was notified, continue with shutdown"));
874
875 // Wait for critical threads
876 ThreadJoin(m_thHouseKeeper);
877 ThreadJoin(m_thPollManager);
878 ThreadJoin(m_thSyncer);
879 ThreadJoin(m_thSyslogDaemon);
880
881 SaveObjects(g_hCoreDB);
882 DbgPrintf(2, _T("All objects saved to database"));
883 SaveUsers(g_hCoreDB);
884 DbgPrintf(2, _T("All users saved to database"));
885 StopDBWriter();
886 DbgPrintf(1, _T("Database writer stopped"));
887
888 CleanupUsers();
889
890 // Remove database lock
891 UnlockDB();
892
893 // Disconnect from database and unload driver
894 if (g_hCoreDB != NULL)
895 DBDisconnect(g_hCoreDB);
896
897 DBConnectionPoolShutdown();
898
899 DBUnloadDriver(g_dbDriver);
900 DbgPrintf(1, _T("Database driver unloaded"));
901
902 CleanupActions();
903 ShutdownEventSubsystem();
904 DbgPrintf(1, _T("Event processing stopped"));
905
906 // Delete all objects
907 //for(i = 0; i < g_dwIdIndexSize; i++)
908 // delete g_pIndexById[i].pObject;
909
910 delete g_pScriptLibrary;
911
912 nxlog_close();
913
914 // Remove PID file
915 #ifndef _WIN32
916 _tremove(g_szPIDFile);
917 #endif
918
919 // Terminate process
920 #ifdef _WIN32
921 if (!(g_dwFlags & AF_DAEMON))
922 ExitProcess(0);
923 #else
924 exit(0);
925 #endif
926 }
927
928 /**
929 * Fast server shutdown - normally called only by Windows service on system shutdown
930 */
931 void NXCORE_EXPORTABLE FastShutdown()
932 {
933 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
934 ConditionSet(m_condShutdown);
935
936 SaveObjects(g_hCoreDB);
937 DbgPrintf(2, _T("All objects saved to database"));
938 SaveUsers(g_hCoreDB);
939 DbgPrintf(2, _T("All users saved to database"));
940
941 // Remove database lock first, because we have a chance to loose DB connection
942 UnlockDB();
943
944 // Stop database writers
945 StopDBWriter();
946 DbgPrintf(1, _T("Database writer stopped"));
947
948 nxlog_close();
949 }
950
951 /**
952 * Compare given string to command template with abbreviation possibility
953 */
954 static bool IsCommand(const TCHAR *pszTemplate, TCHAR *pszString, int iMinChars)
955 {
956 int i;
957
958 // Convert given string to uppercase
959 _tcsupr(pszString);
960
961 for(i = 0; pszString[i] != 0; i++)
962 if (pszString[i] != pszTemplate[i])
963 return false;
964 if (i < iMinChars)
965 return false;
966 return true;
967 }
968
969 /**
970 * Dump index
971 */
972 struct __dump_index_data
973 {
974 CONSOLE_CTX console;
975 bool indexByIP;
976 };
977
978 static void DumpIndexCallback(NetObj *object, void *data)
979 {
980 struct __dump_index_data *d = (struct __dump_index_data *)data;
981 if (d->indexByIP)
982 {
983 TCHAR buffer[16];
984 ConsolePrintf(d->console, _T("%08X [%-15s] %p %s\n"), object->IpAddr(),
985 IpToStr(object->IpAddr(), buffer),
986 object, object->Name());
987 }
988 else
989 {
990 ConsolePrintf(d->console, _T("%08X %p %s\n"), object->Id(), object, object->Name());
991 }
992 }
993
994 static void DumpIndex(CONSOLE_CTX pCtx, ObjectIndex *index, bool indexByIp)
995 {
996 struct __dump_index_data data;
997 data.console = pCtx;
998 data.indexByIP = indexByIp;
999 index->forEach(DumpIndexCallback, &data);
1000 }
1001
1002 /**
1003 * Process command entered from command line in standalone mode
1004 * Return TRUE if command was _T("down")
1005 */
1006 int ProcessConsoleCommand(const TCHAR *pszCmdLine, CONSOLE_CTX pCtx)
1007 {
1008 const TCHAR *pArg;
1009 TCHAR szBuffer[256], *eptr;
1010 int nExitCode = CMD_EXIT_CONTINUE;
1011
1012 // Get command
1013 pArg = ExtractWord(pszCmdLine, szBuffer);
1014
1015 if (IsCommand(_T("DEBUG"), szBuffer, 2))
1016 {
1017 // Get argument
1018 pArg = ExtractWord(pArg, szBuffer);
1019 int level = (int)_tcstol(szBuffer, &eptr, 0);
1020 if ((*eptr == 0) && (level >= 0) && (level <= 9))
1021 {
1022 g_debugLevel = (UINT32)level;
1023 ConsolePrintf(pCtx, (level == 0) ? _T("Debug mode turned off\n") : _T("Debug level set to %d\n"), level);
1024 }
1025 else if (IsCommand(_T("OFF"), szBuffer, 2))
1026 {
1027 g_debugLevel = 0;
1028 ConsolePrintf(pCtx, _T("Debug mode turned off\n"));
1029 }
1030 else
1031 {
1032 if (szBuffer[0] == 0)
1033 ConsolePrintf(pCtx, _T("ERROR: Missing argument\n\n"));
1034 else
1035 ConsolePrintf(pCtx, _T("ERROR: Invalid debug level\n\n"));
1036 }
1037 }
1038 else if (IsCommand(_T("DOWN"), szBuffer, 4))
1039 {
1040 ConsolePrintf(pCtx, _T("Proceeding with server shutdown...\n"));
1041 nExitCode = CMD_EXIT_SHUTDOWN;
1042 }
1043 else if (IsCommand(_T("DUMP"), szBuffer, 4))
1044 {
1045 DumpProcess(pCtx);
1046 }
1047 else if (IsCommand(_T("GET"), szBuffer, 3))
1048 {
1049 pArg = ExtractWord(pArg, szBuffer);
1050 if (szBuffer[0] != 0)
1051 {
1052 TCHAR value[256];
1053 ConfigReadStr(szBuffer, value, 256, _T(""));
1054 ConsolePrintf(pCtx, _T("%s = %s\n"), szBuffer, value);
1055 }
1056 else
1057 {
1058 ConsolePrintf(pCtx, _T("Variable name missing\n"));
1059 }
1060 }
1061 else if (IsCommand(_T("RAISE"), szBuffer, 5))
1062 {
1063 // Get argument
1064 pArg = ExtractWord(pArg, szBuffer);
1065
1066 if (IsCommand(_T("ACCESS"), szBuffer, 6))
1067 {
1068 ConsolePrintf(pCtx, _T("Raising exception...\n"));
1069 char *p = NULL;
1070 *p = 0;
1071 }
1072 else if (IsCommand(_T("BREAKPOINT"), szBuffer, 5))
1073 {
1074 #ifdef _WIN32
1075 ConsolePrintf(pCtx, _T("Raising exception...\n"));
1076 RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
1077 #else
1078 ConsolePrintf(pCtx, _T("ERROR: Not supported on current platform\n"));
1079 #endif
1080 }
1081 else
1082 {
1083 ConsolePrintf(pCtx, _T("Invalid exception name; possible names are:\nACCESS BREAKPOINT\n"));
1084 }
1085 }
1086 else if (IsCommand(_T("EXIT"), szBuffer, 4))
1087 {
1088 if ((pCtx->hSocket != -1) || (pCtx->session != NULL))
1089 {
1090 ConsolePrintf(pCtx, _T("Closing session...\n"));
1091 nExitCode = CMD_EXIT_CLOSE_SESSION;
1092 }
1093 else
1094 {
1095 ConsolePrintf(pCtx, _T("Cannot exit from local server console\n"));
1096 }
1097 }
1098 else if (IsCommand(_T("SET"), szBuffer, 3))
1099 {
1100 pArg = ExtractWord(pArg, szBuffer);
1101 if (szBuffer[0] != 0)
1102 {
1103 TCHAR value[256];
1104 pArg = ExtractWord(pArg, value);
1105 if (ConfigWriteStr(szBuffer, value, TRUE, TRUE, TRUE))
1106 {
1107 ConsolePrintf(pCtx, _T("Configuration variable %s updated\n"), szBuffer);
1108 }
1109 else
1110 {
1111 ConsolePrintf(pCtx, _T("ERROR: cannot update configuration variable %s\n"), szBuffer);
1112 }
1113 }
1114 else
1115 {
1116 ConsolePrintf(pCtx, _T("Variable name missing\n"));
1117 }
1118 }
1119 else if (IsCommand(_T("SHOW"), szBuffer, 2))
1120 {
1121 // Get argument
1122 pArg = ExtractWord(pArg, szBuffer);
1123
1124 if (IsCommand(_T("COMPONENTS"), szBuffer, 1))
1125 {
1126 // Get argument
1127 pArg = ExtractWord(pArg, szBuffer);
1128 UINT32 dwNode = _tcstoul(szBuffer, NULL, 0);
1129 if (dwNode != 0)
1130 {
1131 NetObj *pObject = FindObjectById(dwNode);
1132 if (pObject != NULL)
1133 {
1134 if (pObject->Type() == OBJECT_NODE)
1135 {
1136 ComponentTree *components = ((Node *)pObject)->getComponents();
1137 if (components != NULL)
1138 {
1139 components->print(pCtx);
1140 components->decRefCount();
1141 }
1142 else
1143 {
1144 ConsolePrintf(pCtx, _T("ERROR: Node does not have physical component information\n\n"));
1145 }
1146 }
1147 else
1148 {
1149 ConsolePrintf(pCtx, _T("ERROR: Object is not a node\n\n"));
1150 }
1151 }
1152 else
1153 {
1154 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode);
1155 }
1156 }
1157 else
1158 {
1159 ConsolePrintf(pCtx, _T("ERROR: Invalid or missing node ID\n\n"));
1160 }
1161 }
1162 else if (IsCommand(_T("FDB"), szBuffer, 3))
1163 {
1164 // Get argument
1165 pArg = ExtractWord(pArg, szBuffer);
1166 UINT32 dwNode = _tcstoul(szBuffer, NULL, 0);
1167 if (dwNode != 0)
1168 {
1169 NetObj *pObject = FindObjectById(dwNode);
1170 if (pObject != NULL)
1171 {
1172 if (pObject->Type() == OBJECT_NODE)
1173 {
1174 ForwardingDatabase *fdb = ((Node *)pObject)->getSwitchForwardingDatabase();
1175 if (fdb != NULL)
1176 {
1177 fdb->print(pCtx, (Node *)pObject);
1178 fdb->decRefCount();
1179 }
1180 else
1181 {
1182 ConsolePrintf(pCtx, _T("ERROR: Node does not have forwarding database information\n\n"));
1183 }
1184 }
1185 else
1186 {
1187 ConsolePrintf(pCtx, _T("ERROR: Object is not a node\n\n"));
1188 }
1189 }
1190 else
1191 {
1192 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode);
1193 }
1194 }
1195 else
1196 {
1197 ConsolePrintf(pCtx, _T("ERROR: Invalid or missing node ID\n\n"));
1198 }
1199 }
1200 else if (IsCommand(_T("FLAGS"), szBuffer, 1))
1201 {
1202 ConsolePrintf(pCtx, _T("Flags: 0x%08X\n"), g_dwFlags);
1203 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DAEMON));
1204 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_USE_SYSLOG));
1205 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_NETWORK_DISCOVERY));
1206 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ACTIVE_NETWORK_DISCOVERY));
1207 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_LOG_SQL_ERRORS));
1208 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DELETE_EMPTY_SUBNETS));
1209 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_SNMP_TRAPD));
1210 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_ZONING));
1211 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SYNC_NODE_NAMES_WITH_DNS));
1212 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CHECK_TRUSTED_NODES));
1213 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_NXSL_CONTAINER_FUNCS));
1214 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_USE_FQDN_FOR_NODE_NAMES));
1215 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_APPLY_TO_DISABLED_DCI_FROM_TEMPLATE));
1216 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DEBUG_CONSOLE_DISABLED));
1217 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_OBJECT_TRANSACTIONS));
1218 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_WRITE_FULL_DUMP));
1219 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_RESOLVE_NODE_NAMES));
1220 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CATCH_EXCEPTIONS));
1221 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_INTERNAL_CA));
1222 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DB_LOCKED));
1223 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_MULTIPLE_DB_CONN));
1224 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DB_CONNECTION_LOST));
1225 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_NO_NETWORK_CONNECTIVITY));
1226 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_EVENT_STORM_DETECTED));
1227 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SERVER_INITIALIZED));
1228 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SHUTDOWN));
1229 ConsolePrintf(pCtx, _T("\n"));
1230 }
1231 else if (IsCommand(_T("INDEX"), szBuffer, 1))
1232 {
1233 // Get argument
1234 pArg = ExtractWord(pArg, szBuffer);
1235
1236 if (IsCommand(_T("CONDITION"), szBuffer, 1))
1237 {
1238 DumpIndex(pCtx, &g_idxConditionById, false);
1239 }
1240 else if (IsCommand(_T("ID"), szBuffer, 2))
1241 {
1242 DumpIndex(pCtx, &g_idxObjectById, false);
1243 }
1244 else if (IsCommand(_T("INTERFACE"), szBuffer, 2))
1245 {
1246 DumpIndex(pCtx, &g_idxInterfaceByAddr, true);
1247 }
1248 else if (IsCommand(_T("NODEADDR"), szBuffer, 5))
1249 {
1250 DumpIndex(pCtx, &g_idxNodeByAddr, true);
1251 }
1252 else if (IsCommand(_T("NODEID"), szBuffer, 5))
1253 {
1254 DumpIndex(pCtx, &g_idxNodeById, false);
1255 }
1256 else if (IsCommand(_T("SUBNET"), szBuffer, 1))
1257 {
1258 DumpIndex(pCtx, &g_idxSubnetByAddr, true);
1259 }
1260 else if (IsCommand(_T("ZONE"), szBuffer, 1))
1261 {
1262 DumpIndex(pCtx, &g_idxZoneByGUID, true);
1263 }
1264 else
1265 {
1266 if (szBuffer[0] == 0)
1267 ConsolePrintf(pCtx, _T("ERROR: Missing index name\n")
1268 _T("Valid names are: CONDITION, ID, INTERFACE, NODEADDR, NODEID, SUBNET, ZONE\n\n"));
1269 else
1270 ConsolePrintf(pCtx, _T("ERROR: Invalid index name\n\n"));
1271 }
1272 }
1273 else if (IsCommand(_T("MEMORY"), szBuffer, 2))
1274 {
1275 #ifdef NETXMS_MEMORY_DEBUG
1276 PrintMemoryBlocks();
1277 #else
1278 ConsolePrintf(pCtx, _T("ERROR: Server was compiled without memory debugger\n\n"));
1279 #endif
1280 }
1281 else if (IsCommand(_T("OBJECTS"), szBuffer, 1))
1282 {
1283 DumpObjects(pCtx);
1284 }
1285 else if (IsCommand(_T("POLLERS"), szBuffer, 1))
1286 {
1287 ShowPollerState(pCtx);
1288 }
1289 else if (IsCommand(_T("QUEUES"), szBuffer, 1))
1290 {
1291 ShowQueueStats(pCtx, &g_conditionPollerQueue, _T("Condition poller"));
1292 ShowQueueStats(pCtx, &g_configPollQueue, _T("Configuration poller"));
1293 ShowQueueStats(pCtx, &g_topologyPollQueue, _T("Topology poller"));
1294 ShowQueueStats(pCtx, g_pItemQueue, _T("Data collector"));
1295 ShowQueueStats(pCtx, g_pLazyRequestQueue, _T("Database writer"));
1296 ShowQueueStats(pCtx, g_pIDataInsertQueue, _T("Database writer (IData)"));
1297 ShowQueueStats(pCtx, g_pEventQueue, _T("Event processor"));
1298 ShowQueueStats(pCtx, &g_discoveryPollQueue, _T("Network discovery poller"));
1299 ShowQueueStats(pCtx, &g_nodePollerQueue, _T("Node poller"));
1300 ShowQueueStats(pCtx, &g_routePollQueue, _T("Routing table poller"));
1301 ShowQueueStats(pCtx, &g_statusPollQueue, _T("Status poller"));
1302 ConsolePrintf(pCtx, _T("\n"));
1303 }
1304 else if (IsCommand(_T("ROUTING-TABLE"), szBuffer, 1))
1305 {
1306 UINT32 dwNode;
1307 NetObj *pObject;
1308
1309 pArg = ExtractWord(pArg, szBuffer);
1310 dwNode = _tcstoul(szBuffer, NULL, 0);
1311 if (dwNode != 0)
1312 {
1313 pObject = FindObjectById(dwNode);
1314 if (pObject != NULL)
1315 {
1316 if (pObject->Type() == OBJECT_NODE)
1317 {
1318 ROUTING_TABLE *pRT;
1319 TCHAR szIpAddr[16];
1320 int i;
1321
1322 ConsolePrintf(pCtx, _T("Routing table for node %s:\n\n"), pObject->Name());
1323 pRT = ((Node *)pObject)->getCachedRoutingTable();
1324 if (pRT != NULL)
1325 {
1326 for(i = 0; i < pRT->iNumEntries; i++)
1327 {
1328 _sntprintf(szBuffer, 256, _T("%s/%d"), IpToStr(pRT->pRoutes[i].dwDestAddr, szIpAddr),
1329 BitsInMask(pRT->pRoutes[i].dwDestMask));
1330 ConsolePrintf(pCtx, _T("%-18s %-15s %-6d %d\n"), szBuffer,
1331 IpToStr(pRT->pRoutes[i].dwNextHop, szIpAddr),
1332 pRT->pRoutes[i].dwIfIndex, pRT->pRoutes[i].dwRouteType);
1333 }
1334 ConsolePrintf(pCtx, _T("\n"));
1335 }
1336 else
1337 {
1338 ConsolePrintf(pCtx, _T("Node doesn't have cached routing table\n\n"));
1339 }
1340 }
1341 else
1342 {
1343 ConsolePrintf(pCtx, _T("ERROR: Object is not a node\n\n"));
1344 }
1345 }
1346 else
1347 {
1348 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode);
1349 }
1350 }
1351 else
1352 {
1353 ConsolePrintf(pCtx, _T("ERROR: Invalid or missing node ID\n\n"));
1354 }
1355 }
1356 else if (IsCommand(_T("SESSIONS"), szBuffer, 2))
1357 {
1358 ConsolePrintf(pCtx, _T("\x1b[1mCLIENT SESSIONS\x1b[0m\n============================================================\n"));
1359 DumpClientSessions(pCtx);
1360 ConsolePrintf(pCtx, _T("\n\x1b[1mMOBILE DEVICE SESSIONS\x1b[0m\n============================================================\n"));
1361 DumpMobileDeviceSessions(pCtx);
1362 }
1363 else if (IsCommand(_T("STATS"), szBuffer, 2))
1364 {
1365 ShowServerStats(pCtx);
1366 }
1367 else if (IsCommand(_T("USERS"), szBuffer, 1))
1368 {
1369 DumpUsers(pCtx);
1370 }
1371 else if (IsCommand(_T("VLANS"), szBuffer, 1))
1372 {
1373 UINT32 dwNode;
1374 NetObj *pObject;
1375
1376 pArg = ExtractWord(pArg, szBuffer);
1377 dwNode = _tcstoul(szBuffer, NULL, 0);
1378 if (dwNode != 0)
1379 {
1380 pObject = FindObjectById(dwNode);
1381 if (pObject != NULL)
1382 {
1383 if (pObject->Type() == OBJECT_NODE)
1384 {
1385 VlanList *vlans = ((Node *)pObject)->getVlans();
1386 if (vlans != NULL)
1387 {
1388 ConsolePrintf(pCtx, _T("\x1b[1mVLAN\x1b[0m | \x1b[1mName\x1b[0m | \x1b[1mPorts\x1b[0m\n")
1389 _T("-----+------------------+-----------------------------------------------------------------\n"));
1390 for(int i = 0; i < vlans->getSize(); i++)
1391 {
1392 VlanInfo *vlan = vlans->get(i);
1393 ConsolePrintf(pCtx, _T("%4d | %-16s |"), vlan->getVlanId(), vlan->getName());
1394 for(int j = 0; j < vlan->getNumPorts(); j++)
1395 ConsolePrintf(pCtx, _T(" %d.%d"), (int)(vlan->getPorts()[j] >> 16), (int)(vlan->getPorts()[j] & 0xFFFF));
1396 ConsolePrintf(pCtx, _T("\n"));
1397 }
1398 ConsolePrintf(pCtx, _T("\n"));
1399 vlans->decRefCount();
1400 }
1401 else
1402 {
1403 ConsolePrintf(pCtx, _T("\x1b[31mNode doesn't have VLAN information\x1b[0m\n\n"));
1404 }
1405 }
1406 else
1407 {
1408 ConsolePrintf(pCtx, _T("\x1b[31mERROR: Object is not a node\x1b[0m\n\n"));
1409 }
1410 }
1411 else
1412 {
1413 ConsolePrintf(pCtx, _T("\x1b[31mERROR: Object with ID %d does not exist\x1b[0m\n\n"), dwNode);
1414 }
1415 }
1416 else
1417 {
1418 ConsolePrintf(pCtx, _T("\x1b[31mERROR: Invalid or missing node ID\x1b[0m\n\n"));
1419 }
1420 }
1421 else if (IsCommand(_T("WATCHDOG"), szBuffer, 1))
1422 {
1423 WatchdogPrintStatus(pCtx);
1424 ConsolePrintf(pCtx, _T("\n"));
1425 }
1426 else
1427 {
1428 if (szBuffer[0] == 0)
1429 ConsolePrintf(pCtx, _T("ERROR: Missing subcommand\n\n"));
1430 else
1431 ConsolePrintf(pCtx, _T("ERROR: Invalid SHOW subcommand\n\n"));
1432 }
1433 }
1434 else if (IsCommand(_T("EXEC"), szBuffer, 3))
1435 {
1436 pArg = ExtractWord(pArg, szBuffer);
1437
1438 bool libraryLocked = true;
1439 g_pScriptLibrary->lock();
1440
1441 NXSL_Program *compiledScript = g_pScriptLibrary->findScript(szBuffer);
1442 if (compiledScript == NULL)
1443 {
1444 g_pScriptLibrary->unlock();
1445 libraryLocked = false;
1446 char *script;
1447 UINT32 fileSize;
1448 if ((script = (char *)LoadFile(szBuffer, &fileSize)) != NULL)
1449 {
1450 const int errorMsgLen = 512;
1451 TCHAR errorMsg[errorMsgLen];
1452 #ifdef UNICODE
1453 WCHAR *wscript = WideStringFromMBString(script);
1454 compiledScript = NXSLCompile(wscript, errorMsg, errorMsgLen);
1455 free(wscript);
1456 #else
1457 compiledScript = NXSLCompile(script, errorMsg, errorMsgLen);
1458 #endif
1459 free(script);
1460 if (compiledScript == NULL)
1461 {
1462 ConsolePrintf(pCtx, _T("ERROR: Script compilation error: %s\n\n"), errorMsg);
1463 }
1464 }
1465 else
1466 {
1467 ConsolePrintf(pCtx, _T("ERROR: Script \"%s\" not found\n\n"), szBuffer);
1468 }
1469 }
1470
1471 if (compiledScript != NULL)
1472 {
1473 NXSL_ServerEnv *pEnv = new NXSL_ServerEnv;
1474 pEnv->setConsole(pCtx);
1475
1476 NXSL_Value *argv[32];
1477 int argc = 0;
1478 while(argc < 32)
1479 {
1480 pArg = ExtractWord(pArg, szBuffer);
1481 if (szBuffer[0] == 0)
1482 break;
1483 argv[argc++] = new NXSL_Value(szBuffer);
1484 }
1485
1486 if (compiledScript->run(pEnv, argc, argv) == 0)
1487 {
1488 NXSL_Value *pValue = compiledScript->getResult();
1489 int retCode = pValue->getValueAsInt32();
1490 ConsolePrintf(pCtx, _T("INFO: Script finished with rc=%d\n\n"), retCode);
1491 }
1492 else
1493 {
1494 ConsolePrintf(pCtx, _T("ERROR: Script finished with error: %s\n\n"), compiledScript->getErrorText());
1495 }
1496 }
1497 if (libraryLocked)
1498 g_pScriptLibrary->unlock();
1499 }
1500 else if (IsCommand(_T("TRACE"), szBuffer, 1))
1501 {
1502 UINT32 dwNode1, dwNode2;
1503 NetObj *pObject1, *pObject2;
1504 NetworkPath *pTrace;
1505 TCHAR szNextHop[16];
1506 int i;
1507
1508 // Get arguments
1509 pArg = ExtractWord(pArg, szBuffer);
1510 dwNode1 = _tcstoul(szBuffer, NULL, 0);
1511
1512 pArg = ExtractWord(pArg, szBuffer);
1513 dwNode2 = _tcstoul(szBuffer, NULL, 0);
1514
1515 if ((dwNode1 != 0) && (dwNode2 != 0))
1516 {
1517 pObject1 = FindObjectById(dwNode1);
1518 if (pObject1 == NULL)
1519 {
1520 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode1);
1521 }
1522 else
1523 {
1524 pObject2 = FindObjectById(dwNode2);
1525 if (pObject2 == NULL)
1526 {
1527 ConsolePrintf(pCtx, _T("ERROR: Object with ID %d does not exist\n\n"), dwNode2);
1528 }
1529 else
1530 {
1531 if ((pObject1->Type() == OBJECT_NODE) && (pObject2->Type() == OBJECT_NODE))
1532 {
1533 pTrace = TraceRoute((Node *)pObject1, (Node *)pObject2);
1534 if (pTrace != NULL)
1535 {
1536 ConsolePrintf(pCtx, _T("Trace from %s to %s (%d hops, %s):\n"),
1537 pObject1->Name(), pObject2->Name(), pTrace->getHopCount(),
1538 pTrace->isComplete() ? _T("complete") : _T("incomplete"));
1539 for(i = 0; i < pTrace->getHopCount(); i++)
1540 {
1541 HOP_INFO *hop = pTrace->getHopInfo(i);
1542 ConsolePrintf(pCtx, _T("[%d] %s %s %s %d\n"),
1543 hop->object->Id(),
1544 hop->object->Name(),
1545 IpToStr(hop->nextHop, szNextHop),
1546 hop->isVpn ? _T("VPN Connector ID:") : _T("Interface Index: "),
1547 hop->ifIndex);
1548 }
1549 delete pTrace;
1550 ConsolePrintf(pCtx, _T("\n"));
1551 }
1552 else
1553 {
1554 ConsolePrintf(pCtx, _T("ERROR: Call to TraceRoute() failed\n\n"));
1555 }
1556 }
1557 else
1558 {
1559 ConsolePrintf(pCtx, _T("ERROR: Object is not a node\n\n"));
1560 }
1561 }
1562 }
1563 }
1564 else
1565 {
1566 ConsolePrintf(pCtx, _T("ERROR: Invalid or missing node id(s)\n\n"));
1567 }
1568 }
1569 else if (IsCommand(_T("HELP"), szBuffer, 2) || IsCommand(_T("?"), szBuffer, 1))
1570 {
1571 ConsolePrintf(pCtx, _T("Valid commands are:\n")
1572 _T(" debug [<level>|off] - Set debug level (valid range is 0..9)\n")
1573 _T(" down - Shutdown NetXMS server\n")
1574 _T(" exec <script> [<params>] - Executes NXSL script from script library\n")
1575 _T(" exit - Exit from remote session\n")
1576 _T(" get <variable> - Get value of server configuration variable\n")
1577 _T(" help - Display this help\n")
1578 _T(" raise <exception> - Raise exception\n")
1579 _T(" set <variable> <value> - Set value of server configuration variable\n")
1580 _T(" show components <node> - Show physical components of given node\n")
1581 _T(" show fdb <node> - Show forwarding database for node\n")
1582 _T(" show flags - Show internal server flags\n")
1583 _T(" show index <index> - Show internal index\n")
1584 _T(" show objects - Dump network objects to screen\n")
1585 _T(" show pollers - Show poller threads state information\n")
1586 _T(" show queues - Show internal queues statistics\n")
1587 _T(" show routing-table <node> - Show cached routing table for node\n")
1588 _T(" show sessions - Show active client sessions\n")
1589 _T(" show stats - Show server statistics\n")
1590 _T(" show users - Show users\n")
1591 _T(" show vlans <node> - Show cached VLAN information for node\n")
1592 _T(" show watchdog - Display watchdog information\n")
1593 _T(" trace <node1> <node2> - Show network path trace between two nodes\n")
1594 _T("\nAlmost all commands can be abbreviated to 2 or 3 characters\n")
1595 _T("\n"));
1596 }
1597 else
1598 {
1599 ConsolePrintf(pCtx, _T("UNKNOWN COMMAND\n\n"));
1600 }
1601
1602 return nExitCode;
1603 }
1604
1605 /**
1606 * Signal handler for UNIX platforms
1607 */
1608 #ifndef _WIN32
1609
1610 void SignalHandlerStub(int nSignal)
1611 {
1612 // should be unused, but JIC...
1613 if (nSignal == SIGCHLD)
1614 {
1615 while (waitpid(-1, NULL, WNOHANG) > 0)
1616 ;
1617 }
1618 }
1619
1620 THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL SignalHandler(void *pArg)
1621 {
1622 sigset_t signals;
1623 int nSignal;
1624 BOOL bCallShutdown = FALSE;
1625
1626 m_signalHandlerThread = pthread_self();
1627
1628 // default for SIGCHLD: ignore
1629 signal(SIGCHLD, &SignalHandlerStub);
1630
1631 sigemptyset(&signals);
1632 sigaddset(&signals, SIGTERM);
1633 sigaddset(&signals, SIGINT);
1634 sigaddset(&signals, SIGPIPE);
1635 sigaddset(&signals, SIGSEGV);
1636 sigaddset(&signals, SIGCHLD);
1637 sigaddset(&signals, SIGHUP);
1638 sigaddset(&signals, SIGUSR1);
1639 sigaddset(&signals, SIGUSR2);
1640
1641 sigprocmask(SIG_BLOCK, &signals, NULL);
1642
1643 while(1)
1644 {
1645 if (sigwait(&signals, &nSignal) == 0)
1646 {
1647 switch(nSignal)
1648 {
1649 case SIGTERM:
1650 case SIGINT:
1651 m_nShutdownReason = SHUTDOWN_BY_SIGNAL;
1652 if (IsStandalone())
1653 bCallShutdown = TRUE;
1654 ConditionSet(m_condShutdown);
1655 goto stop_handler;
1656 case SIGSEGV:
1657 abort();
1658 break;
1659 case SIGCHLD:
1660 while (waitpid(-1, NULL, WNOHANG) > 0)
1661 ;
1662 break;
1663 case SIGUSR1:
1664 if (g_dwFlags & AF_SHUTDOWN)
1665 goto stop_handler;
1666 break;
1667 default:
1668 break;
1669 }
1670 }
1671 else
1672 {
1673 ThreadSleepMs(100);
1674 }
1675 }
1676
1677 stop_handler:
1678 sigprocmask(SIG_UNBLOCK, &signals, NULL);
1679 if (bCallShutdown)
1680 Shutdown();
1681 return THREAD_OK;
1682 }
1683
1684 #endif
1685
1686 /**
1687 * Common main()
1688 */
1689 THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL Main(void *pArg)
1690 {
1691 nxlog_write(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
1692
1693 if (IsStandalone() && ((g_dwFlags & AF_DEBUG_CONSOLE_DISABLED) == 0))
1694 {
1695 char *ptr, szCommand[256];
1696 struct __console_ctx ctx;
1697 #ifdef UNICODE
1698 WCHAR wcCommand[256];
1699 #endif
1700
1701 ctx.hSocket = -1;
1702 ctx.socketMutex = INVALID_MUTEX_HANDLE;
1703 ctx.pMsg = NULL;
1704 ctx.session = NULL;
1705 WriteToTerminal(_T("\nNetXMS Server V") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" Ready\n")
1706 _T("Enter \"\x1b[1mhelp\x1b[0m\" for command list or \"\x1b[1mdown\x1b[0m\" for server shutdown\n")
1707 _T("System Console\n\n"));
1708
1709 #if USE_READLINE
1710 // Initialize readline library if we use it
1711 rl_bind_key('\t', RL_INSERT_CAST rl_insert);
1712 #endif
1713
1714 while(1)
1715 {
1716 #if USE_READLINE
1717 ptr = readline("\x1b[33mnetxmsd:\x1b[0m ");
1718 #else
1719 WriteToTerminal(_T("\x1b[33mnetxmsd:\x1b[0m "));
1720 fflush(stdout);
1721 if (fgets(szCommand, 255, stdin) == NULL)
1722 break; // Error reading stdin
1723 ptr = strchr(szCommand, '\n');
1724 if (ptr != NULL)
1725 *ptr = 0;
1726 ptr = szCommand;
1727 #endif
1728
1729 if (ptr != NULL)
1730 {
1731 #ifdef UNICODE
1732 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ptr, -1, wcCommand, 256);
1733 wcCommand[255] = 0;
1734 StrStrip(wcCommand);
1735 if (wcCommand[0] != 0)
1736 {
1737 if (ProcessConsoleCommand(wcCommand, &ctx) == CMD_EXIT_SHUTDOWN)
1738 #else
1739 StrStrip(ptr);
1740 if (*ptr != 0)
1741 {
1742 if (ProcessConsoleCommand(ptr, &ctx) == CMD_EXIT_SHUTDOWN)
1743 #endif
1744 break;
1745 #if USE_READLINE
1746 add_history(ptr);
1747 #endif
1748 }
1749 #if USE_READLINE
1750 free(ptr);
1751 #endif
1752 }
1753 else
1754 {
1755 _tprintf(_T("\n"));
1756 }
1757 }
1758
1759 #if USE_READLINE
1760 free(ptr);
1761 #endif
1762 m_nShutdownReason = SHUTDOWN_FROM_CONSOLE;
1763 Shutdown();
1764 }
1765 else
1766 {
1767 ConditionWait(m_condShutdown, INFINITE);
1768 // On Win32, Shutdown() will be called by service control handler
1769 #ifndef _WIN32
1770 Shutdown();
1771 #endif
1772 }
1773 return THREAD_OK;
1774 }
1775
1776 /**
1777 * Initiate server shutdown
1778 */
1779 void InitiateShutdown()
1780 {
1781 #ifdef _WIN32
1782 Shutdown();
1783 #else
1784 if (IsStandalone())
1785 {
1786 Shutdown();
1787 }
1788 else
1789 {
1790 pthread_kill(m_signalHandlerThread, SIGTERM);
1791 }
1792 #endif
1793 }
1794
1795 /**
1796 *DLL Entry point
1797 */
1798 #ifdef _WIN32
1799
1800 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1801 {
1802 if (dwReason == DLL_PROCESS_ATTACH)
1803 DisableThreadLibraryCalls(hInstance);
1804 return TRUE;
1805 }
1806
1807 #endif