implemented alarm termination by regexp
[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
bc980b27 422BOOL NXCORE_EXPORTABLE Initialize()
5039dede
AK
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
035a4d73 687void NXCORE_EXPORTABLE Shutdown()
5039dede
AK
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));
5039dede 950 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SYNC_NODE_NAMES_WITH_DNS));
cbc777ee
VK
951 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CHECK_TRUSTED_NODES));
952 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_WRITE_FULL_DUMP));
953 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_RESOLVE_NODE_NAMES));
5039dede 954 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CATCH_EXCEPTIONS));
5039dede
AK
955 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_INTERNAL_CA));
956 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DB_LOCKED));
cbc777ee 957 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_MULTIPLE_DB_CONN));
5039dede 958 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_DB_CONNECTION_LOST));
cbc777ee 959 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_NO_NETWORK_CONNECTIVITY));
5039dede
AK
960 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_EVENT_STORM_DETECTED));
961 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SERVER_INITIALIZED));
962 ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SHUTDOWN));
963 ConsolePrintf(pCtx, "\n");
964 }
965 else if (IsCommand("INDEX", szBuffer, 1))
966 {
967 // Get argument
968 pArg = ExtractWord(pArg, szBuffer);
969
970 if (IsCommand("CONDITION", szBuffer, 1))
971 {
972 DumpIndex(pCtx, g_rwlockConditionIndex, g_pConditionIndex, g_dwConditionIndexSize, FALSE);
973 }
974 else if (IsCommand("ID", szBuffer, 2))
975 {
976 DumpIndex(pCtx, g_rwlockIdIndex, g_pIndexById, g_dwIdIndexSize, FALSE);
977 }
978 else if (IsCommand("INTERFACE", szBuffer, 2))
979 {
980 DumpIndex(pCtx, g_rwlockInterfaceIndex, g_pInterfaceIndexByAddr,
981 g_dwInterfaceAddrIndexSize, TRUE);
982 }
983 else if (IsCommand("NODE", szBuffer, 1))
984 {
985 DumpIndex(pCtx, g_rwlockNodeIndex, g_pNodeIndexByAddr,
986 g_dwNodeAddrIndexSize, TRUE);
987 }
988 else if (IsCommand("SUBNET", szBuffer, 1))
989 {
990 DumpIndex(pCtx, g_rwlockSubnetIndex, g_pSubnetIndexByAddr,
991 g_dwSubnetAddrIndexSize, TRUE);
992 }
993 else
994 {
995 if (szBuffer[0] == 0)
996 ConsolePrintf(pCtx, "ERROR: Missing index name\n"
997 "Valid names are: CONDITION, ID, INTERFACE, NODE, SUBNET\n\n");
998 else
999 ConsolePrintf(pCtx, "ERROR: Invalid index name\n\n");
1000 }
1001 }
1002 else if (IsCommand("MEMORY", szBuffer, 2))
1003 {
1004#ifdef NETXMS_MEMORY_DEBUG
1005 PrintMemoryBlocks();
1006#else
1007 ConsolePrintf(pCtx, "ERROR: Server was compiled without memory debugger\n\n");
1008#endif
1009 }
1010 else if (IsCommand("MUTEX", szBuffer, 2))
1011 {
1012 ConsolePrintf(pCtx, "Mutex status:\n");
1013 DbgTestRWLock(g_rwlockIdIndex, "g_hMutexIdIndex", pCtx);
1014 DbgTestRWLock(g_rwlockNodeIndex, "g_hMutexNodeIndex", pCtx);
1015 DbgTestRWLock(g_rwlockSubnetIndex, "g_hMutexSubnetIndex", pCtx);
1016 DbgTestRWLock(g_rwlockInterfaceIndex, "g_hMutexInterfaceIndex", pCtx);
1017 ConsolePrintf(pCtx, "\n");
1018 }
1019 else if (IsCommand("OBJECTS", szBuffer, 1))
1020 {
1021 DumpObjects(pCtx);
1022 }
1023 else if (IsCommand("POLLERS", szBuffer, 1))
1024 {
1025 ShowPollerState(pCtx);
1026 }
1027 else if (IsCommand("QUEUES", szBuffer, 1))
1028 {
1029 ShowQueueStats(pCtx, &g_conditionPollerQueue, "Condition poller");
1030 ShowQueueStats(pCtx, &g_configPollQueue, "Configuration poller");
1031 ShowQueueStats(pCtx, g_pItemQueue, "Data collector");
1032 ShowQueueStats(pCtx, g_pLazyRequestQueue, "Database writer");
1033 ShowQueueStats(pCtx, g_pEventQueue, "Event processor");
1034 ShowQueueStats(pCtx, &g_discoveryPollQueue, "Network discovery poller");
1035 ShowQueueStats(pCtx, &g_nodePollerQueue, "Node poller");
1036 ShowQueueStats(pCtx, &g_routePollQueue, "Routing table poller");
1037 ShowQueueStats(pCtx, &g_statusPollQueue, "Status poller");
1038 ConsolePrintf(pCtx, "\n");
1039 }
1040 else if (IsCommand("ROUTING-TABLE", szBuffer, 1))
1041 {
1042 DWORD dwNode;
1043 NetObj *pObject;
1044
1045 pArg = ExtractWord(pArg, szBuffer);
1046 dwNode = strtoul(szBuffer, NULL, 0);
1047 if (dwNode != 0)
1048 {
1049 pObject = FindObjectById(dwNode);
1050 if (pObject != NULL)
1051 {
1052 if (pObject->Type() == OBJECT_NODE)
1053 {
1054 ROUTING_TABLE *pRT;
1055 char szIpAddr[16];
1056 int i;
1057
1058 ConsolePrintf(pCtx, "Routing table for node %s:\n\n", pObject->Name());
58b3e451 1059 pRT = ((Node *)pObject)->getCachedRoutingTable();
5039dede
AK
1060 if (pRT != NULL)
1061 {
1062 for(i = 0; i < pRT->iNumEntries; i++)
1063 {
1064 sprintf(szBuffer, "%s/%d", IpToStr(pRT->pRoutes[i].dwDestAddr, szIpAddr),
1065 BitsInMask(pRT->pRoutes[i].dwDestMask));
1066 ConsolePrintf(pCtx, "%-18s %-15s %-6d %d\n", szBuffer,
1067 IpToStr(pRT->pRoutes[i].dwNextHop, szIpAddr),
1068 pRT->pRoutes[i].dwIfIndex, pRT->pRoutes[i].dwRouteType);
1069 }
1070 ConsolePrintf(pCtx, "\n");
1071 }
1072 else
1073 {
1074 ConsolePrintf(pCtx, "Node doesn't have cached routing table\n\n");
1075 }
1076 }
1077 else
1078 {
1079 ConsolePrintf(pCtx, "ERROR: Object is not a node\n\n");
1080 }
1081 }
1082 else
1083 {
1084 ConsolePrintf(pCtx, "ERROR: Object with ID %d does not exist\n\n", dwNode);
1085 }
1086 }
1087 else
1088 {
1089 ConsolePrintf(pCtx, "ERROR: Invalid or missing node ID\n\n");
1090 }
1091 }
1092 else if (IsCommand("SESSIONS", szBuffer, 2))
1093 {
1094 DumpSessions(pCtx);
1095 }
1096 else if (IsCommand("STATS", szBuffer, 2))
1097 {
1098 ShowServerStats(pCtx);
1099 }
1100 else if (IsCommand("USERS", szBuffer, 1))
1101 {
1102 DumpUsers(pCtx);
1103 }
1104 else if (IsCommand("WATCHDOG", szBuffer, 1))
1105 {
1106 WatchdogPrintStatus(pCtx);
1107 ConsolePrintf(pCtx, "\n");
1108 }
1109 else
1110 {
1111 if (szBuffer[0] == 0)
1112 ConsolePrintf(pCtx, "ERROR: Missing subcommand\n\n");
1113 else
1114 ConsolePrintf(pCtx, "ERROR: Invalid SHOW subcommand\n\n");
1115 }
1116 }
1117 else if (IsCommand("TRACE", szBuffer, 1))
1118 {
1119 DWORD dwNode1, dwNode2;
1120 NetObj *pObject1, *pObject2;
1121 NETWORK_PATH_TRACE *pTrace;
1122 char szNextHop[16];
1123 int i;
1124
1125 // Get arguments
1126 pArg = ExtractWord(pArg, szBuffer);
1127 dwNode1 = strtoul(szBuffer, NULL, 0);
1128
1129 pArg = ExtractWord(pArg, szBuffer);
1130 dwNode2 = strtoul(szBuffer, NULL, 0);
1131
1132 if ((dwNode1 != 0) && (dwNode2 != 0))
1133 {
1134 pObject1 = FindObjectById(dwNode1);
1135 if (pObject1 == NULL)
1136 {
1137 ConsolePrintf(pCtx, "ERROR: Object with ID %d does not exist\n\n", dwNode1);
1138 }
1139 else
1140 {
1141 pObject2 = FindObjectById(dwNode2);
1142 if (pObject2 == NULL)
1143 {
1144 ConsolePrintf(pCtx, "ERROR: Object with ID %d does not exist\n\n", dwNode2);
1145 }
1146 else
1147 {
1148 if ((pObject1->Type() == OBJECT_NODE) &&
1149 (pObject2->Type() == OBJECT_NODE))
1150 {
1151 pTrace = TraceRoute((Node *)pObject1, (Node *)pObject2);
1152 if (pTrace != NULL)
1153 {
1154 ConsolePrintf(pCtx, "Trace from %s to %s (%d hops):\n",
1155 pObject1->Name(), pObject2->Name(), pTrace->iNumHops);
1156 for(i = 0; i < pTrace->iNumHops; i++)
1157 ConsolePrintf(pCtx, "[%d] %s %s %s %d\n",
1158 pTrace->pHopList[i].pObject->Id(),
1159 pTrace->pHopList[i].pObject->Name(),
1160 IpToStr(pTrace->pHopList[i].dwNextHop, szNextHop),
1161 pTrace->pHopList[i].bIsVPN ? "VPN Connector ID:" : "Interface Index: ",
1162 pTrace->pHopList[i].dwIfIndex);
1163 DestroyTraceData(pTrace);
1164 ConsolePrintf(pCtx, "\n");
1165 }
1166 else
1167 {
1168 ConsolePrintf(pCtx, "ERROR: Call to TraceRoute() failed\n\n");
1169 }
1170 }
1171 else
1172 {
1173 ConsolePrintf(pCtx, "ERROR: Object is not a node\n\n");
1174 }
1175 }
1176 }
1177 }
1178 else
1179 {
1180 ConsolePrintf(pCtx, "ERROR: Invalid or missing node id(s)\n\n");
1181 }
1182 }
1183 else if (IsCommand("HELP", szBuffer, 2) || IsCommand("?", szBuffer, 1))
1184 {
1185 ConsolePrintf(pCtx, "Valid commands are:\n"
1186 " debug [on|off] - Turn debug mode on or off\n"
1187 " down - Down NetXMS server\n"
1188 " exit - Exit from remote session\n"
1189 " help - Display this help\n"
1190 " raise <exception> - Raise exception\n"
1191 " show flags - Show internal server flags\n"
1192 " show index <index> - Show internal index\n"
1193 " show mutex - Display mutex status\n"
1194 " show objects - Dump network objects to screen\n"
1195 " show pollers - Show poller threads state information\n"
1196 " show queues - Show internal queues statistics\n"
1197 " show routing-table <node> - Show cached routing table for node\n"
1198 " show sessions - Show active client sessions\n"
1199 " show stats - Show server statistics\n"
1200 " show users - Show users\n"
1201 " show watchdog - Display watchdog information\n"
1202 " trace <node1> <node2> - Show network path trace between two nodes\n"
1203 "\nAlmost all commands can be abbreviated to 2 or 3 characters\n"
1204 "\n");
1205 }
1206 else
1207 {
1208 ConsolePrintf(pCtx, "UNKNOWN COMMAND\n\n");
1209 }
1210
1211 return nExitCode;
1212}
1213
1214
1215//
1216// Signal handler for UNIX platforms
1217//
1218
1219#ifndef _WIN32
1220
1221void SignalHandlerStub(int nSignal)
1222{
1223 // should be unused, but JIC...
1224 if (nSignal == SIGCHLD)
1225 {
1226 while (waitpid(-1, NULL, WNOHANG) > 0)
1227 ;
1228 }
1229}
1230
1231THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL SignalHandler(void *pArg)
1232{
1233 sigset_t signals;
1234 int nSignal;
1235 BOOL bCallShutdown = FALSE;
1236
1237 m_signalHandlerThread = pthread_self();
1238
1239 // default for SIGCHLD: ignore
1240 signal(SIGCHLD, &SignalHandlerStub);
1241
1242 sigemptyset(&signals);
1243 sigaddset(&signals, SIGTERM);
1244 sigaddset(&signals, SIGINT);
1245 sigaddset(&signals, SIGPIPE);
1246 sigaddset(&signals, SIGSEGV);
1247 sigaddset(&signals, SIGCHLD);
1248 sigaddset(&signals, SIGHUP);
1249 sigaddset(&signals, SIGUSR1);
1250 sigaddset(&signals, SIGUSR2);
1251
1252 sigprocmask(SIG_BLOCK, &signals, NULL);
1253
1254 while(1)
1255 {
1256 if (sigwait(&signals, &nSignal) == 0)
1257 {
1258 switch(nSignal)
1259 {
1260 case SIGTERM:
1261 case SIGINT:
1262 m_nShutdownReason = SHUTDOWN_BY_SIGNAL;
1263 if (IsStandalone())
1264 bCallShutdown = TRUE;
1265 ConditionSet(m_condShutdown);
1266 goto stop_handler;
1267 case SIGSEGV:
1268 abort();
1269 break;
1270 case SIGCHLD:
1271 while (waitpid(-1, NULL, WNOHANG) > 0)
1272 ;
1273 break;
1274 case SIGUSR1:
1275 if (g_dwFlags & AF_SHUTDOWN)
1276 goto stop_handler;
1277 break;
1278 default:
1279 break;
1280 }
1281 }
1282 else
1283 {
1284 ThreadSleepMs(100);
1285 }
1286 }
1287
1288stop_handler:
1289 sigprocmask(SIG_UNBLOCK, &signals, NULL);
1290 if (bCallShutdown)
1291 Shutdown();
1292 return THREAD_OK;
1293}
1294
1295#endif
1296
1297
1298//
1299// Common main()
1300//
1301
1302THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL Main(void *pArg)
1303{
1304 nxlog_write(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
1305
1306 if (IsStandalone())
1307 {
1308 char *ptr, szCommand[256];
1309 struct __console_ctx ctx;
1310
1311 ctx.hSocket = -1;
1312 ctx.pMsg = NULL;
1313 printf("\nNetXMS Server V" NETXMS_VERSION_STRING " Ready\n"
1314 "Enter \"help\" for command list or \"down\" for server shutdown\n"
1315 "System Console\n\n");
1316
1317#if USE_READLINE
1318 // Initialize readline library if we use it
0322eed7 1319 rl_bind_key('\t', RL_INSERT_CAST rl_insert);
5039dede
AK
1320#endif
1321
1322 while(1)
1323 {
1324#if USE_READLINE
1325 ptr = readline("netxmsd: ");
1326#else
1327 printf("netxmsd: ");
1328 fflush(stdout);
1329 if (fgets(szCommand, 255, stdin) == NULL)
1330 break; // Error reading stdin
1331 ptr = strchr(szCommand, '\n');
1332 if (ptr != NULL)
1333 *ptr = 0;
1334 ptr = szCommand;
1335#endif
1336
1337 if (ptr != NULL)
1338 {
1339 StrStrip(ptr);
1340 if (*ptr != 0)
1341 {
1342 if (ProcessConsoleCommand(ptr, &ctx) == CMD_EXIT_SHUTDOWN)
1343 break;
1344#if USE_READLINE
1345 add_history(ptr);
1346#endif
1347 }
1348#if USE_READLINE
1349 free(ptr);
1350#endif
1351 }
1352 else
1353 {
1354 printf("\n");
1355 }
1356 }
1357
1358#if USE_READLINE
1359 free(ptr);
1360#endif
1361 m_nShutdownReason = SHUTDOWN_FROM_CONSOLE;
1362 Shutdown();
1363 }
1364 else
1365 {
1366 ConditionWait(m_condShutdown, INFINITE);
1367 // On Win32, Shutdown() will be called by service control handler
1368#ifndef _WIN32
1369 Shutdown();
1370#endif
1371 }
1372 return THREAD_OK;
1373}
1374
1375
1376//
1377// Initiate server shutdown
1378//
1379
035a4d73 1380void InitiateShutdown()
5039dede
AK
1381{
1382#ifdef _WIN32
1383 Shutdown();
1384#else
1385 if (IsStandalone())
1386 {
1387 Shutdown();
1388 }
1389 else
1390 {
1391 pthread_kill(m_signalHandlerThread, SIGTERM);
1392 }
1393#endif
1394}
1395
1396
1397//
1398// DLL Entry point
1399//
1400
1401#ifdef _WIN32
1402
1403BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1404{
1405 if (dwReason == DLL_PROCESS_ATTACH)
1406 DisableThreadLibraryCalls(hInstance);
1407 return TRUE;
1408}
1409
1410#endif