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