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