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