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