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