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