957eca7c1d4e229299dbd6ad5aede7dfe59d17b0
[public/netxms.git] / src / server / core / main.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 Raden Solutions
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 #include <netxms_mt.h>
26 #include <hdlink.h>
27
28 #if !defined(_WIN32) && HAVE_READLINE_READLINE_H && HAVE_READLINE && !defined(UNICODE)
29 #include <readline/readline.h>
30 #include <readline/history.h>
31 #define USE_READLINE 1
32 #endif
33
34 #ifdef _WIN32
35 #include <errno.h>
36 #include <psapi.h>
37 #include <conio.h>
38 #define open _open
39 #define write _write
40 #define close _close
41 #else
42 #include <signal.h>
43 #include <sys/wait.h>
44 #endif
45
46 #ifdef WITH_ZMQ
47 #include "zeromq.h"
48 #endif
49
50 /**
51 * Messages generated by mc.pl (for UNIX version only)
52 */
53 #ifndef _WIN32
54 extern unsigned int g_dwNumMessages;
55 extern const TCHAR *g_szMessages[];
56 #endif
57
58 /**
59 * Shutdown reasons
60 */
61 #define SHUTDOWN_DEFAULT 0
62 #define SHUTDOWN_FROM_CONSOLE 1
63 #define SHUTDOWN_BY_SIGNAL 2
64
65 /**
66 * Externals
67 */
68 extern Queue g_dciCacheLoaderQueue;
69
70 void InitClientListeners();
71 void InitMobileDeviceListeners();
72 void InitCertificates();
73 void InitUsers();
74 void CleanupUsers();
75 void LoadPerfDataStorageDrivers();
76 void ImportLocalConfiguration();
77
78 void ExecuteScheduledScript(const ScheduledTaskParameters *param);
79 void MaintenanceModeEnter(const ScheduledTaskParameters *params);
80 void MaintenanceModeLeave(const ScheduledTaskParameters *params);
81 void ScheduleDeployPolicy(const ScheduledTaskParameters *params);
82 void ScheduleUninstallPolicy(const ScheduledTaskParameters * params);
83
84 void InitCountryList();
85 void InitCurrencyList();
86
87 #if XMPP_SUPPORTED
88 void StartXMPPConnector();
89 void StopXMPPConnector();
90 #endif
91
92 /**
93 * Syslog server control
94 */
95 void StartSyslogServer();
96 void StopSyslogServer();
97
98 /**
99 * Thread functions
100 */
101 THREAD_RESULT THREAD_CALL Syncer(void *);
102 THREAD_RESULT THREAD_CALL NodePoller(void *);
103 THREAD_RESULT THREAD_CALL PollManager(void *);
104 THREAD_RESULT THREAD_CALL EventProcessor(void *);
105 THREAD_RESULT THREAD_CALL WatchdogThread(void *);
106 THREAD_RESULT THREAD_CALL ClientListener(void *);
107 THREAD_RESULT THREAD_CALL ClientListenerIPv6(void *);
108 THREAD_RESULT THREAD_CALL MobileDeviceListener(void *);
109 THREAD_RESULT THREAD_CALL MobileDeviceListenerIPv6(void *);
110 THREAD_RESULT THREAD_CALL ISCListener(void *);
111 THREAD_RESULT THREAD_CALL LocalAdminListener(void *);
112 THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *);
113 THREAD_RESULT THREAD_CALL BeaconPoller(void *);
114 THREAD_RESULT THREAD_CALL JobManagerThread(void *);
115 THREAD_RESULT THREAD_CALL UptimeCalculator(void *);
116 THREAD_RESULT THREAD_CALL ReportingServerConnector(void *);
117
118 /**
119 * Global variables
120 */
121 TCHAR NXCORE_EXPORTABLE g_szConfigFile[MAX_PATH] = _T("{search}");
122 TCHAR NXCORE_EXPORTABLE g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
123 UINT32 g_logRotationMode = NXLOG_ROTATION_BY_SIZE;
124 UINT64 g_maxLogSize = 16384 * 1024;
125 UINT32 g_logHistorySize = 4;
126 TCHAR g_szDailyLogFileSuffix[64] = _T("");
127 TCHAR NXCORE_EXPORTABLE g_szDumpDir[MAX_PATH] = DEFAULT_DUMP_DIR;
128 char g_szCodePage[256] = ICONV_DEFAULT_CODEPAGE;
129 TCHAR NXCORE_EXPORTABLE g_szListenAddress[MAX_PATH] = _T("*");
130 #ifndef _WIN32
131 TCHAR NXCORE_EXPORTABLE g_szPIDFile[MAX_PATH] = _T("/var/run/netxmsd.pid");
132 #endif
133 UINT32 g_dwDiscoveryPollingInterval;
134 UINT32 g_dwStatusPollingInterval;
135 UINT32 g_dwConfigurationPollingInterval;
136 UINT32 g_dwRoutingTableUpdateInterval;
137 UINT32 g_dwTopologyPollingInterval;
138 UINT32 g_dwConditionPollingInterval;
139 UINT32 g_instancePollingInterval;
140 UINT32 g_icmpPingSize;
141 UINT32 g_icmpPingTimeout = 1500; // ICMP ping timeout (milliseconds)
142 UINT32 g_auditFlags;
143 UINT32 g_slmPollingInterval;
144 UINT32 g_offlineDataRelevanceTime = 86400;
145 TCHAR NXCORE_EXPORTABLE g_netxmsdDataDir[MAX_PATH] = _T("");
146 TCHAR NXCORE_EXPORTABLE g_netxmsdLibDir[MAX_PATH] = _T("");
147 int g_dbSyntax = DB_SYNTAX_UNKNOWN;
148 UINT32 NXCORE_EXPORTABLE g_processAffinityMask = DEFAULT_AFFINITY_MASK;
149 UINT64 g_serverId = 0;
150 RSA *g_pServerKey = NULL;
151 time_t g_serverStartTime = 0;
152 UINT32 g_lockTimeout = 60000; // Default timeout for acquiring mutex
153 UINT32 g_agentCommandTimeout = 4000; // Default timeout for requests to agent
154 UINT32 g_thresholdRepeatInterval = 0; // Disabled by default
155 int g_requiredPolls = 1;
156 DB_DRIVER g_dbDriver = NULL;
157 ThreadPool NXCORE_EXPORTABLE *g_mainThreadPool = NULL;
158 INT16 g_defaultAgentCacheMode = AGENT_CACHE_OFF;
159
160 /**
161 * Static data
162 */
163 static CONDITION m_condShutdown = INVALID_CONDITION_HANDLE;
164 static THREAD m_thPollManager = INVALID_THREAD_HANDLE;
165 static THREAD m_thSyncer = INVALID_THREAD_HANDLE;
166 static int m_nShutdownReason = SHUTDOWN_DEFAULT;
167 static StringSet s_components;
168
169 #ifndef _WIN32
170 static pthread_t m_signalHandlerThread;
171 #endif
172
173 /**
174 * Register component
175 */
176 void NXCORE_EXPORTABLE RegisterComponent(const TCHAR *id)
177 {
178 s_components.add(id);
179 }
180
181 /**
182 * Check if component with given ID is registered
183 */
184 bool NXCORE_EXPORTABLE IsComponentRegistered(const TCHAR *id)
185 {
186 return s_components.contains(id);
187 }
188
189 /**
190 * Fill NXCP message with components data
191 */
192 void FillComponentsMessage(NXCPMessage *msg)
193 {
194 msg->setField(VID_NUM_COMPONENTS, (INT32)s_components.size());
195 UINT32 fieldId = VID_COMPONENT_LIST_BASE;
196 Iterator<const TCHAR> *it = s_components.iterator();
197 while(it->hasNext())
198 {
199 msg->setField(fieldId++, it->next());
200 }
201 delete it;
202 }
203
204 /**
205 * Sleep for specified number of seconds or until system shutdown arrives
206 * Function will return TRUE if shutdown event occurs
207 *
208 * @param seconds seconds to sleep
209 * @return true if server is shutting down
210 */
211 bool NXCORE_EXPORTABLE SleepAndCheckForShutdown(int seconds)
212 {
213 return ConditionWait(m_condShutdown, seconds * 1000);
214 }
215
216 /**
217 * Disconnect from database (exportable function for startup module)
218 */
219 void NXCORE_EXPORTABLE ShutdownDB()
220 {
221 DBConnectionPoolShutdown();
222 DBUnloadDriver(g_dbDriver);
223 }
224
225 /**
226 * Check data directory for existence
227 */
228 static BOOL CheckDataDir()
229 {
230 TCHAR szBuffer[MAX_PATH];
231
232 if (_tchdir(g_netxmsdDataDir) == -1)
233 {
234 nxlog_write(MSG_INVALID_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", g_netxmsdDataDir);
235 return FALSE;
236 }
237
238 #ifdef _WIN32
239 #define MKDIR(name) _tmkdir(name)
240 #else
241 #define MKDIR(name) _tmkdir(name, 0700)
242 #endif
243
244 // Create directory for package files if it doesn't exist
245 _tcscpy(szBuffer, g_netxmsdDataDir);
246 _tcscat(szBuffer, DDIR_PACKAGES);
247 if (MKDIR(szBuffer) == -1)
248 if (errno != EEXIST)
249 {
250 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
251 return FALSE;
252 }
253
254 // Create directory for map background images if it doesn't exist
255 _tcscpy(szBuffer, g_netxmsdDataDir);
256 _tcscat(szBuffer, DDIR_BACKGROUNDS);
257 if (MKDIR(szBuffer) == -1)
258 if (errno != EEXIST)
259 {
260 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
261 return FALSE;
262 }
263
264 // Create directory for image library is if does't exists
265 _tcscpy(szBuffer, g_netxmsdDataDir);
266 _tcscat(szBuffer, DDIR_IMAGES);
267 if (MKDIR(szBuffer) == -1)
268 {
269 if (errno != EEXIST)
270 {
271 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
272 return FALSE;
273 }
274 }
275
276 // Create directory for file store if does't exists
277 _tcscpy(szBuffer, g_netxmsdDataDir);
278 _tcscat(szBuffer, DDIR_FILES);
279 if (MKDIR(szBuffer) == -1)
280 {
281 if (errno != EEXIST)
282 {
283 nxlog_write(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
284 return FALSE;
285 }
286 }
287
288 #undef MKDIR
289
290 return TRUE;
291 }
292
293 /**
294 * Load global configuration parameters
295 */
296 static void LoadGlobalConfig()
297 {
298 g_dwDiscoveryPollingInterval = ConfigReadInt(_T("DiscoveryPollingInterval"), 900);
299 g_dwStatusPollingInterval = ConfigReadInt(_T("StatusPollingInterval"), 60);
300 g_dwConfigurationPollingInterval = ConfigReadInt(_T("ConfigurationPollingInterval"), 3600);
301 g_instancePollingInterval = ConfigReadInt(_T("InstancePollingInterval"), 600);
302 g_dwRoutingTableUpdateInterval = ConfigReadInt(_T("RoutingTableUpdateInterval"), 300);
303 g_dwTopologyPollingInterval = ConfigReadInt(_T("TopologyPollingInterval"), 1800);
304 g_dwConditionPollingInterval = ConfigReadInt(_T("ConditionPollingInterval"), 60);
305 g_slmPollingInterval = ConfigReadInt(_T("SlmPollingInterval"), 60);
306 DCObject::m_defaultPollingInterval = ConfigReadInt(_T("DefaultDCIPollingInterval"), 60);
307 DCObject::m_defaultRetentionTime = ConfigReadInt(_T("DefaultDCIRetentionTime"), 30);
308 g_defaultAgentCacheMode = (INT16)ConfigReadInt(_T("DefaultAgentCacheMode"), AGENT_CACHE_OFF);
309 if ((g_defaultAgentCacheMode != AGENT_CACHE_ON) && (g_defaultAgentCacheMode != AGENT_CACHE_OFF))
310 {
311 DbgPrintf(1, _T("Invalid value %d of DefaultAgentCacheMode: reset to %d (OFF)"), g_defaultAgentCacheMode, AGENT_CACHE_OFF);
312 ConfigWriteInt(_T("DefaultAgentCacheMode"), AGENT_CACHE_OFF, true, true, true);
313 g_defaultAgentCacheMode = AGENT_CACHE_OFF;
314 }
315 if (ConfigReadInt(_T("DeleteEmptySubnets"), 1))
316 g_flags |= AF_DELETE_EMPTY_SUBNETS;
317 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
318 g_flags |= AF_ENABLE_SNMP_TRAPD;
319 if (ConfigReadInt(_T("ProcessTrapsFromUnmanagedNodes"), 0))
320 g_flags |= AF_TRAPS_FROM_UNMANAGED_NODES;
321 if (ConfigReadInt(_T("EnableZoning"), 0))
322 g_flags |= AF_ENABLE_ZONING;
323 if (ConfigReadInt(_T("EnableObjectTransactions"), 0))
324 g_flags |= AF_ENABLE_OBJECT_TRANSACTIONS;
325 if (ConfigReadInt(_T("RunNetworkDiscovery"), 0))
326 g_flags |= AF_ENABLE_NETWORK_DISCOVERY;
327 if (ConfigReadInt(_T("ActiveNetworkDiscovery"), 0))
328 g_flags |= AF_ACTIVE_NETWORK_DISCOVERY;
329 if (ConfigReadInt(_T("UseSNMPTrapsForDiscovery"), 0))
330 g_flags |= AF_SNMP_TRAP_DISCOVERY;
331 if (ConfigReadInt(_T("ResolveNodeNames"), 1))
332 g_flags |= AF_RESOLVE_NODE_NAMES;
333 if (ConfigReadInt(_T("SyncNodeNamesWithDNS"), 0))
334 g_flags |= AF_SYNC_NODE_NAMES_WITH_DNS;
335 if (ConfigReadInt(_T("CheckTrustedNodes"), 1))
336 g_flags |= AF_CHECK_TRUSTED_NODES;
337 if (ConfigReadInt(_T("EnableNXSLContainerFunctions"), 1))
338 g_flags |= AF_ENABLE_NXSL_CONTAINER_FUNCS;
339 if (ConfigReadInt(_T("UseFQDNForNodeNames"), 1))
340 g_flags |= AF_USE_FQDN_FOR_NODE_NAMES;
341 if (ConfigReadInt(_T("ApplyDCIFromTemplateToDisabledDCI"), 1))
342 g_flags |= AF_APPLY_TO_DISABLED_DCI_FROM_TEMPLATE;
343 if (ConfigReadInt(_T("ResolveDNSToIPOnStatusPoll"), 0))
344 g_flags |= AF_RESOLVE_IP_FOR_EACH_STATUS_POLL;
345 if (ConfigReadInt(_T("CaseInsensitiveLoginNames"), 0))
346 g_flags |= AF_CASE_INSENSITIVE_LOGINS;
347 if (ConfigReadInt(_T("TrapSourcesInAllZones"), 0))
348 g_flags |= AF_TRAP_SOURCES_IN_ALL_ZONES;
349
350 if (g_netxmsdDataDir[0] == 0)
351 {
352 GetNetXMSDirectory(nxDirData, g_netxmsdDataDir);
353 DbgPrintf(1, _T("Data directory set to %s"), g_netxmsdDataDir);
354 }
355 else
356 {
357 DbgPrintf(1, _T("Using data directory %s"), g_netxmsdDataDir);
358 }
359
360 g_icmpPingTimeout = ConfigReadInt(_T("IcmpPingTimeout"), 1500);
361 g_icmpPingSize = ConfigReadInt(_T("IcmpPingSize"), 46);
362 g_lockTimeout = ConfigReadInt(_T("LockTimeout"), 60000);
363 g_agentCommandTimeout = ConfigReadInt(_T("AgentCommandTimeout"), 4000);
364 g_thresholdRepeatInterval = ConfigReadInt(_T("ThresholdRepeatInterval"), 0);
365 g_requiredPolls = ConfigReadInt(_T("PollCountForStatusChange"), 1);
366 g_offlineDataRelevanceTime = ConfigReadInt(_T("OfflineDataRelevanceTime"), 86400);
367
368 UINT32 snmpTimeout = ConfigReadInt(_T("SNMPRequestTimeout"), 1500);
369 SnmpSetDefaultTimeout(snmpTimeout);
370 }
371
372 /**
373 * Initialize cryptografic functions
374 */
375 static BOOL InitCryptografy()
376 {
377 #ifdef _WITH_ENCRYPTION
378 BOOL bResult = FALSE;
379
380 if (!InitCryptoLib(ConfigReadULong(_T("AllowedCiphers"), 0x7F)))
381 return FALSE;
382 nxlog_debug(4, _T("Supported ciphers: %s"), (const TCHAR *)NXCPGetSupportedCiphersAsText());
383
384 SSL_library_init();
385 SSL_load_error_strings();
386
387 TCHAR szKeyFile[MAX_PATH];
388 _tcscpy(szKeyFile, g_netxmsdDataDir);
389 _tcscat(szKeyFile, DFILE_KEYS);
390 g_pServerKey = LoadRSAKeys(szKeyFile);
391 if (g_pServerKey == NULL)
392 {
393 nxlog_debug(1, _T("Generating RSA key pair..."));
394 g_pServerKey = RSA_generate_key(NETXMS_RSA_KEYLEN, 17, NULL, 0);
395 if (g_pServerKey != NULL)
396 {
397 int fd = _topen(szKeyFile, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600);
398 if (fd != -1)
399 {
400 UINT32 dwLen = i2d_RSAPublicKey(g_pServerKey, NULL);
401 dwLen += i2d_RSAPrivateKey(g_pServerKey, NULL);
402 BYTE *pKeyBuffer = (BYTE *)malloc(dwLen);
403
404 BYTE *pBufPos = pKeyBuffer;
405 i2d_RSAPublicKey(g_pServerKey, &pBufPos);
406 i2d_RSAPrivateKey(g_pServerKey, &pBufPos);
407 write(fd, &dwLen, sizeof(UINT32));
408 write(fd, pKeyBuffer, dwLen);
409
410 BYTE hash[SHA1_DIGEST_SIZE];
411 CalculateSHA1Hash(pKeyBuffer, dwLen, hash);
412 write(fd, hash, SHA1_DIGEST_SIZE);
413
414 close(fd);
415 free(pKeyBuffer);
416 bResult = TRUE;
417 }
418 else
419 {
420 nxlog_debug(0, _T("Failed to open %s for writing"), szKeyFile);
421 }
422 }
423 else
424 {
425 nxlog_debug(0, _T("Failed to generate RSA key"));
426 }
427 }
428 else
429 {
430 bResult = TRUE;
431 }
432
433 int iPolicy = ConfigReadInt(_T("DefaultEncryptionPolicy"), 1);
434 if ((iPolicy < 0) || (iPolicy > 3))
435 iPolicy = 1;
436 SetAgentDEP(iPolicy);
437
438 return bResult;
439 #else
440 return TRUE;
441 #endif
442 }
443
444 /**
445 * Check if process with given PID exists and is a NetXMS server process
446 */
447 static BOOL IsNetxmsdProcess(UINT32 dwPID)
448 {
449 #ifdef _WIN32
450 HANDLE hProcess;
451 TCHAR szExtModule[MAX_PATH], szIntModule[MAX_PATH];
452 BOOL bRet = FALSE;
453
454 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
455 if (hProcess != NULL)
456 {
457 if ((GetModuleBaseName(hProcess, NULL, szExtModule, MAX_PATH) > 0) &&
458 (GetModuleBaseName(GetCurrentProcess(), NULL, szIntModule, MAX_PATH) > 0))
459 {
460 bRet = !_tcsicmp(szExtModule, szIntModule);
461 }
462 else
463 {
464 // Cannot read process name, for safety assume that it's a server process
465 bRet = TRUE;
466 }
467 CloseHandle(hProcess);
468 }
469 return bRet;
470 #else
471 return (kill((pid_t)dwPID, 0) != -1);
472 #endif
473 }
474
475 /**
476 * Database event handler
477 */
478 static void DBEventHandler(DWORD dwEvent, const WCHAR *pszArg1, const WCHAR *pszArg2, bool connLost, void *userArg)
479 {
480 if (!(g_flags & AF_SERVER_INITIALIZED))
481 return; // Don't try to do anything if server is not ready yet
482
483 switch(dwEvent)
484 {
485 case DBEVENT_CONNECTION_LOST:
486 PostEvent(EVENT_DB_CONNECTION_LOST, g_dwMgmtNode, NULL);
487 g_flags |= AF_DB_CONNECTION_LOST;
488 NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, FALSE);
489 break;
490 case DBEVENT_CONNECTION_RESTORED:
491 PostEvent(EVENT_DB_CONNECTION_RESTORED, g_dwMgmtNode, NULL);
492 g_flags &= ~AF_DB_CONNECTION_LOST;
493 NotifyClientSessions(NX_NOTIFY_DBCONN_STATUS, TRUE);
494 break;
495 case DBEVENT_QUERY_FAILED:
496 PostEvent(EVENT_DB_QUERY_FAILED, g_dwMgmtNode, "uud", pszArg1, pszArg2, connLost ? 1 : 0);
497 break;
498 default:
499 break;
500 }
501 }
502
503 /**
504 * Send console message to session with open console
505 */
506 static void SendConsoleMessage(ClientSession *session, void *arg)
507 {
508 if (session->isConsoleOpen())
509 {
510 NXCPMessage msg;
511
512 msg.setCode(CMD_ADM_MESSAGE);
513 msg.setField(VID_MESSAGE, (TCHAR *)arg);
514 session->postMessage(&msg);
515 }
516 }
517
518 /**
519 * Console writer
520 */
521 static void LogConsoleWriter(const TCHAR *format, ...)
522 {
523 TCHAR buffer[8192];
524 va_list args;
525
526 va_start(args, format);
527 _vsntprintf(buffer, 8192, format, args);
528 buffer[8191] = 0;
529 va_end(args);
530
531 WriteToTerminal(buffer);
532
533 EnumerateClientSessions(SendConsoleMessage, buffer);
534 }
535
536 /**
537 * Oracle session init callback
538 */
539 static void OracleSessionInitCallback(DB_HANDLE hdb)
540 {
541 DBQuery(hdb, _T("ALTER SESSION SET DDL_LOCK_TIMEOUT = 60"));
542 }
543
544 /**
545 * Server initialization
546 */
547 BOOL NXCORE_EXPORTABLE Initialize()
548 {
549 s_components.add(_T("CORE"));
550
551 g_serverStartTime = time(NULL);
552 srand((unsigned int)g_serverStartTime);
553
554 if (!(g_flags & AF_USE_SYSLOG))
555 {
556 if (!nxlog_set_rotation_policy((int)g_logRotationMode, g_maxLogSize, (int)g_logHistorySize, g_szDailyLogFileSuffix))
557 if (!(g_flags & AF_DAEMON))
558 _tprintf(_T("WARNING: cannot set log rotation policy; using default values\n"));
559 }
560 if (!nxlog_open((g_flags & AF_USE_SYSLOG) ? NETXMSD_SYSLOG_NAME : g_szLogFile,
561 ((g_flags & AF_USE_SYSLOG) ? NXLOG_USE_SYSLOG : 0) |
562 ((g_flags & AF_BACKGROUND_LOG_WRITER) ? NXLOG_BACKGROUND_WRITER : 0) |
563 ((g_flags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
564 _T("LIBNXSRV.DLL"),
565 #ifdef _WIN32
566 0, NULL, MSG_DEBUG))
567 #else
568 g_dwNumMessages, g_szMessages, MSG_DEBUG))
569 #endif
570 {
571 _ftprintf(stderr, _T("FATAL ERROR: Cannot open log file\n"));
572 return FALSE;
573 }
574 nxlog_set_console_writer(LogConsoleWriter);
575
576 if (g_netxmsdLibDir[0] == 0)
577 {
578 GetNetXMSDirectory(nxDirLib, g_netxmsdLibDir);
579 nxlog_debug(1, _T("LIB directory set to %s"), g_netxmsdLibDir);
580 }
581
582 // Set code page
583 #ifndef _WIN32
584 if (SetDefaultCodepage(g_szCodePage))
585 {
586 DbgPrintf(1, _T("Code page set to %hs"), g_szCodePage);
587 }
588 else
589 {
590 nxlog_write(MSG_CODEPAGE_ERROR, EVENTLOG_WARNING_TYPE, "m", g_szCodePage);
591 }
592 #endif
593
594 // Set process affinity mask
595 if (g_processAffinityMask != DEFAULT_AFFINITY_MASK)
596 {
597 #ifdef _WIN32
598 if (SetProcessAffinityMask(GetCurrentProcess(), g_processAffinityMask))
599 nxlog_debug(1, _T("Process affinity mask set to 0x%08X"), g_processAffinityMask);
600 #else
601 nxlog_write(MSG_SET_PROCESS_AFFINITY_NOT_SUPPORTED, EVENTLOG_WARNING_TYPE, NULL);
602 #endif
603 }
604
605 #ifdef _WIN32
606 WSADATA wsaData;
607 int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
608 if (wrc != 0)
609 {
610 nxlog_write(MSG_WSASTARTUP_FAILED, EVENTLOG_ERROR_TYPE, "e", wrc);
611 return FALSE;
612 }
613 #endif
614
615 InitLocalNetInfo();
616 SnmpSetMessageIds(MSG_OID_PARSE_ERROR, MSG_SNMP_UNKNOWN_TYPE, MSG_SNMP_GET_ERROR);
617
618 // Create queue for delayed SQL queries
619 g_dbWriterQueue = new Queue(256, 64);
620 g_dciDataWriterQueue = new Queue(1024, 1024);
621 g_dciRawDataWriterQueue = new Queue(1024, 1024);
622
623 // Initialize database driver and connect to database
624 if (!DBInit(MSG_OTHER, (g_flags & AF_LOG_SQL_ERRORS) ? MSG_SQL_ERROR : 0))
625 return FALSE;
626 g_dbDriver = DBLoadDriver(g_szDbDriver, g_szDbDrvParams, (nxlog_get_debug_level() >= 9), DBEventHandler, NULL);
627 if (g_dbDriver == NULL)
628 return FALSE;
629
630 // Connect to database
631 DB_HANDLE hdbBootstrap = NULL;
632 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
633 for(int i = 0; ; i++)
634 {
635 hdbBootstrap = DBConnect(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, errorText);
636 if ((hdbBootstrap != NULL) || (i == 5))
637 break;
638 ThreadSleep(5);
639 }
640 if (hdbBootstrap == NULL)
641 {
642 nxlog_write(MSG_DB_CONNFAIL, EVENTLOG_ERROR_TYPE, "s", errorText);
643 return FALSE;
644 }
645 nxlog_debug(1, _T("Successfully connected to database %s@%s"), g_szDbName, g_szDbServer);
646
647 // Check database schema version
648 int schemaVersion = DBGetSchemaVersion(hdbBootstrap);
649 if (schemaVersion != DB_FORMAT_VERSION)
650 {
651 nxlog_write(MSG_WRONG_DB_VERSION, EVENTLOG_ERROR_TYPE, "dd", schemaVersion, DB_FORMAT_VERSION);
652 DBDisconnect(hdbBootstrap);
653 return FALSE;
654 }
655
656 // Read database syntax
657 g_dbSyntax = DBGetSyntax(hdbBootstrap);
658 if (g_dbSyntax == DB_SYNTAX_ORACLE)
659 {
660 DBSetSessionInitCallback(OracleSessionInitCallback);
661 }
662
663 int baseSize = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolBaseSize"), 10);
664 int maxSize = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolMaxSize"), 30);
665 int cooldownTime = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolCooldownTime"), 300);
666 int ttl = ConfigReadIntEx(hdbBootstrap, _T("DBConnectionPoolMaxLifetime"), 14400);
667
668 DBDisconnect(hdbBootstrap);
669
670 if (!DBConnectionPoolStartup(g_dbDriver, g_szDbServer, g_szDbName, g_szDbLogin, g_szDbPassword, g_szDbSchema, baseSize, maxSize, cooldownTime, ttl))
671 {
672 nxlog_write(MSG_DBCONNPOOL_INIT_FAILED, EVENTLOG_ERROR_TYPE, NULL);
673 return FALSE;
674 }
675
676 UINT32 lrt = ConfigReadULong(_T("LongRunningQueryThreshold"), 0);
677 if (lrt != 0)
678 DBSetLongRunningThreshold(lrt);
679
680 MetaDataPreLoad();
681
682 // Read server ID
683 TCHAR buffer[256];
684 MetaDataReadStr(_T("ServerID"), buffer, 256, _T(""));
685 StrStrip(buffer);
686 if (buffer[0] != 0)
687 {
688 g_serverId = _tcstoull(buffer, NULL, 16);
689 }
690 else
691 {
692 // Generate new ID
693 g_serverId = ((UINT64)time(NULL) << 31) | (UINT64)((UINT32)rand() & 0x7FFFFFFF);
694 _sntprintf(buffer, 256, UINT64X_FMT(_T("016")), g_serverId);
695 MetaDataWriteStr(_T("ServerID"), buffer);
696 }
697 nxlog_debug(1, _T("Server ID ") UINT64X_FMT(_T("016")), g_serverId);
698
699 // Initialize locks
700 retry_db_lock:
701 InetAddress addr;
702 if (!InitLocks(&addr, buffer))
703 {
704 if (!addr.isValid()) // Some SQL problems
705 {
706 nxlog_write(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
707 }
708 else // Database already locked by another server instance
709 {
710 // Check for lock from crashed/terminated local process
711 if (GetLocalIpAddr().equals(addr))
712 {
713 UINT32 dwPID;
714
715 dwPID = ConfigReadULong(_T("DBLockPID"), 0);
716 if (!IsNetxmsdProcess(dwPID) || (dwPID == GetCurrentProcessId()))
717 {
718 UnlockDB();
719 nxlog_write(MSG_DB_LOCK_REMOVED, EVENTLOG_INFORMATION_TYPE, NULL);
720 goto retry_db_lock;
721 }
722 }
723 nxlog_write(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "As", &addr, buffer);
724 }
725 return FALSE;
726 }
727 g_flags |= AF_DB_LOCKED;
728
729 // Load global configuration parameters
730 LoadGlobalConfig();
731 CASReadSettings();
732 nxlog_debug(1, _T("Global configuration loaded"));
733
734 // Check data directory
735 if (!CheckDataDir())
736 return FALSE;
737
738 // Initialize cryptografy
739 if (!InitCryptografy())
740 {
741 nxlog_write(MSG_INIT_CRYPTO_FAILED, EVENTLOG_ERROR_TYPE, NULL);
742 return FALSE;
743 }
744
745 // Initialize certificate store and CA
746 InitCertificates();
747
748 // Create synchronization stuff
749 m_condShutdown = ConditionCreate(TRUE);
750
751 // Create thread pools
752 nxlog_debug(2, _T("Creating thread pools"));
753 g_mainThreadPool = ThreadPoolCreate(8, 256, _T("MAIN"));
754 g_agentConnectionThreadPool = ThreadPoolCreate(4, 256, _T("AGENT"));
755
756 // Setup unique identifiers table
757 if (!InitIdTable())
758 return FALSE;
759 nxlog_debug(2, _T("ID table created"));
760
761 InitCountryList();
762 InitCurrencyList();
763
764 // Update status for unfinished jobs in job history
765 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
766 DBQuery(hdb, _T("UPDATE job_history SET status=4,failure_message='Aborted due to server shutdown or crash' WHERE status NOT IN (3,4,5)"));
767 DBConnectionPoolReleaseConnection(hdb);
768
769 // Load and compile scripts
770 LoadScripts();
771
772 // Initialize watchdog
773 WatchdogInit();
774
775 // Load modules
776 if (!LoadNetXMSModules())
777 return FALSE; // Mandatory module not loaded
778
779 // Initialize mailer and SMS sender
780 InitMailer();
781 InitSMSSender();
782
783 // Load users from database
784 InitUsers();
785 if (!LoadUsers())
786 {
787 nxlog_write(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
788 return FALSE;
789 }
790 nxlog_debug(2, _T("User accounts loaded"));
791
792 // Initialize audit
793 InitAuditLog();
794
795 // Initialize event handling subsystem
796 if (!InitEventSubsystem())
797 return FALSE;
798
799 // Initialize alarms
800 LoadAlarmCategories();
801 if (!InitAlarmManager())
802 return FALSE;
803
804 // Initialize objects infrastructure and load objects from database
805 LoadNetworkDeviceDrivers();
806 ObjectsInit();
807 if (!LoadObjects())
808 return FALSE;
809 nxlog_debug(1, _T("Objects loaded and initialized"));
810
811 // Initialize situations
812 if (!SituationsInit())
813 return FALSE;
814 nxlog_debug(1, _T("Situations loaded and initialized"));
815
816 // Initialize and load event actions
817 if (!InitActions())
818 {
819 nxlog_write(MSG_ACTION_INIT_ERROR, EVENTLOG_ERROR_TYPE, NULL);
820 return FALSE;
821 }
822
823 // Initialize helpdesk link
824 SetHDLinkEntryPoints(ResolveAlarmByHDRef, TerminateAlarmByHDRef);
825 LoadHelpDeskLink();
826
827 // Initialize data collection subsystem
828 LoadPerfDataStorageDrivers();
829 if (!InitDataCollector())
830 return FALSE;
831
832 InitLogAccess();
833 FileUploadJob::init();
834 InitMappingTables();
835
836 InitClientListeners();
837 if (ConfigReadInt(_T("ImportConfigurationOnStartup"), 1))
838 ImportLocalConfiguration();
839
840 // Check if management node object presented in database
841 CheckForMgmtNode();
842 if (g_dwMgmtNode == 0)
843 {
844 nxlog_write(MSG_CANNOT_FIND_SELF, EVENTLOG_ERROR_TYPE, NULL);
845 return FALSE;
846 }
847
848 // Start threads
849 ThreadCreate(WatchdogThread, 0, NULL);
850 ThreadCreate(NodePoller, 0, NULL);
851 ThreadCreate(JobManagerThread, 0, NULL);
852 m_thSyncer = ThreadCreateEx(Syncer, 0, NULL);
853 m_thPollManager = ThreadCreateEx(PollManager, 0, NULL);
854
855 StartHouseKeeper();
856
857 // Start event processor
858 ThreadCreate(EventProcessor, 0, NULL);
859
860 // Start SNMP trapper
861 InitTraps();
862 if (ConfigReadInt(_T("EnableSNMPTraps"), 1))
863 ThreadCreate(SNMPTrapReceiver, 0, NULL);
864
865 // Start built-in syslog daemon
866 StartSyslogServer();
867
868 // Start database _T("lazy") write thread
869 StartDBWriter();
870
871 // Start local administartive interface listener if required
872 if (ConfigReadInt(_T("EnableAdminInterface"), 1))
873 ThreadCreate(LocalAdminListener, 0, NULL);
874
875 // Start beacon host poller
876 ThreadCreate(BeaconPoller, 0, NULL);
877
878 // Start inter-server communication listener
879 if (ConfigReadInt(_T("EnableISCListener"), 0))
880 ThreadCreate(ISCListener, 0, NULL);
881
882 // Start reporting server connector
883 if (ConfigReadInt(_T("EnableReportingServer"), 0))
884 ThreadCreate(ReportingServerConnector, 0, NULL);
885
886 //Start ldap synchronization
887 if (ConfigReadInt(_T("LdapSyncInterval"), 0))
888 ThreadCreate(SyncLDAPUsers, 0, NULL);
889
890 RegisterSchedulerTaskHandler(_T("Execute.Script"), ExecuteScheduledScript, SYSTEM_ACCESS_SCHEDULE_SCRIPT);
891 RegisterSchedulerTaskHandler(_T("Maintenance.Enter"), MaintenanceModeEnter, SYSTEM_ACCESS_SCHEDULE_MAINTENANCE);
892 RegisterSchedulerTaskHandler(_T("Maintenance.Leave"), MaintenanceModeLeave, SYSTEM_ACCESS_SCHEDULE_MAINTENANCE);
893 RegisterSchedulerTaskHandler(_T("Policy.Deploy"), ScheduleDeployPolicy, 0); //No access right beacause it will be used only by server
894 RegisterSchedulerTaskHandler(_T("Policy.Uninstall"), ScheduleUninstallPolicy, 0); //No access right beacause it will be used only by server
895 InitializeTaskScheduler();
896
897 // Allow clients to connect
898 ThreadCreate(ClientListener, 0, NULL);
899 #ifdef WITH_IPV6
900 ThreadCreate(ClientListenerIPv6, 0, NULL);
901 #endif
902
903 // Allow mobile devices to connect
904 InitMobileDeviceListeners();
905 ThreadCreate(MobileDeviceListener, 0, NULL);
906 #ifdef WITH_IPV6
907 ThreadCreate(MobileDeviceListenerIPv6, 0, NULL);
908 #endif
909
910 // Start uptime calculator for SLM
911 ThreadCreate(UptimeCalculator, 0, NULL);
912
913 nxlog_debug(2, _T("LIBDIR: %s"), g_netxmsdLibDir);
914
915 // Call startup functions for the modules
916 CALL_ALL_MODULES(pfServerStarted, ());
917
918 #if XMPP_SUPPORTED
919 if (ConfigReadInt(_T("EnableXMPPConnector"), 1))
920 {
921 StartXMPPConnector();
922 }
923 #endif
924
925 #if WITH_ZMQ
926 StartZMQConnector();
927 #endif
928
929 g_flags |= AF_SERVER_INITIALIZED;
930 nxlog_debug(1, _T("Server initialization completed"));
931 return TRUE;
932 }
933
934 /**
935 * Server shutdown
936 */
937 void NXCORE_EXPORTABLE Shutdown()
938 {
939 // Notify clients
940 NotifyClientSessions(NX_NOTIFY_SHUTDOWN, 0);
941
942 nxlog_write(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
943 g_flags |= AF_SHUTDOWN; // Set shutdown flag
944 ConditionSet(m_condShutdown);
945
946 CloseTaskScheduler();
947
948 // Stop DCI cache loading thread
949 g_dciCacheLoaderQueue.setShutdownMode();
950
951 #if XMPP_SUPPORTED
952 StopXMPPConnector();
953 #endif
954
955 #if WITH_ZMQ
956 StopZMQConnector();
957 #endif
958
959 g_pEventQueue->clear();
960 g_pEventQueue->put(INVALID_POINTER_VALUE);
961
962 ShutdownMailer();
963 ShutdownSMSSender();
964
965 ThreadSleep(1); // Give other threads a chance to terminate in a safe way
966 nxlog_debug(2, _T("All threads was notified, continue with shutdown"));
967
968 StopSyslogServer();
969 StopHouseKeeper();
970
971 // Wait for critical threads
972 ThreadJoin(m_thPollManager);
973 ThreadJoin(m_thSyncer);
974
975 // Call shutdown functions for the modules
976 // CALL_ALL_MODULES cannot be used here because it checks for shutdown flag
977 for(UINT32 i = 0; i < g_dwNumModules; i++)
978 {
979 if (g_pModuleList[i].pfShutdown != NULL)
980 g_pModuleList[i].pfShutdown();
981 }
982
983 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
984 SaveObjects(hdb);
985 nxlog_debug(2, _T("All objects saved to database"));
986 SaveUsers(hdb);
987 nxlog_debug(2, _T("All users saved to database"));
988 DBConnectionPoolReleaseConnection(hdb);
989
990 StopDBWriter();
991 nxlog_debug(1, _T("Database writer stopped"));
992
993 CleanupUsers();
994
995 // Remove database lock
996 UnlockDB();
997
998 DBConnectionPoolShutdown();
999 DBUnloadDriver(g_dbDriver);
1000 nxlog_debug(1, _T("Database driver unloaded"));
1001
1002 CleanupActions();
1003 ShutdownEventSubsystem();
1004 ShutdownAlarmManager();
1005 nxlog_debug(1, _T("Event processing stopped"));
1006
1007 ThreadPoolDestroy(g_agentConnectionThreadPool);
1008 ThreadPoolDestroy(g_mainThreadPool);
1009 MsgWaitQueue::shutdown();
1010
1011 delete g_pScriptLibrary;
1012
1013 nxlog_debug(1, _T("Server shutdown complete"));
1014 nxlog_close();
1015
1016 // Remove PID file
1017 #ifndef _WIN32
1018 _tremove(g_szPIDFile);
1019 #endif
1020
1021 // Terminate process
1022 #ifdef _WIN32
1023 if (!(g_flags & AF_DAEMON))
1024 ExitProcess(0);
1025 #else
1026 exit(0);
1027 #endif
1028 }
1029
1030 /**
1031 * Fast server shutdown - normally called only by Windows service on system shutdown
1032 */
1033 void NXCORE_EXPORTABLE FastShutdown()
1034 {
1035 DbgPrintf(1, _T("Using fast shutdown procedure"));
1036
1037 g_flags |= AF_SHUTDOWN; // Set shutdown flag
1038 ConditionSet(m_condShutdown);
1039
1040 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1041 SaveObjects(hdb);
1042 DbgPrintf(2, _T("All objects saved to database"));
1043 SaveUsers(hdb);
1044 DbgPrintf(2, _T("All users saved to database"));
1045 DBConnectionPoolReleaseConnection(hdb);
1046
1047 // Remove database lock first, because we have a chance to lose DB connection
1048 UnlockDB();
1049
1050 // Stop database writers
1051 StopDBWriter();
1052 DbgPrintf(1, _T("Database writer stopped"));
1053
1054 DbgPrintf(1, _T("Server shutdown complete"));
1055 nxlog_close();
1056 }
1057
1058 /**
1059 * Signal handler for UNIX platforms
1060 */
1061 #ifndef _WIN32
1062
1063 void SignalHandlerStub(int nSignal)
1064 {
1065 // should be unused, but JIC...
1066 if (nSignal == SIGCHLD)
1067 {
1068 while (waitpid(-1, NULL, WNOHANG) > 0)
1069 ;
1070 }
1071 }
1072
1073 THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL SignalHandler(void *pArg)
1074 {
1075 sigset_t signals;
1076 int nSignal;
1077
1078 m_signalHandlerThread = pthread_self();
1079
1080 // default for SIGCHLD: ignore
1081 signal(SIGCHLD, &SignalHandlerStub);
1082
1083 sigemptyset(&signals);
1084 sigaddset(&signals, SIGTERM);
1085 sigaddset(&signals, SIGINT);
1086 sigaddset(&signals, SIGSEGV);
1087 sigaddset(&signals, SIGCHLD);
1088 sigaddset(&signals, SIGHUP);
1089 sigaddset(&signals, SIGUSR1);
1090 sigaddset(&signals, SIGUSR2);
1091 #if !defined(__sun) && !defined(_AIX) && !defined(__hpux)
1092 sigaddset(&signals, SIGPIPE);
1093 #endif
1094
1095 sigprocmask(SIG_BLOCK, &signals, NULL);
1096
1097 while(1)
1098 {
1099 if (sigwait(&signals, &nSignal) == 0)
1100 {
1101 switch(nSignal)
1102 {
1103 case SIGTERM:
1104 case SIGINT:
1105 // avoid repeat Shutdown() call
1106 if (!(g_flags & AF_SHUTDOWN))
1107 {
1108 m_nShutdownReason = SHUTDOWN_BY_SIGNAL;
1109 if (IsStandalone())
1110 Shutdown(); // will never return
1111 else
1112 ConditionSet(m_condShutdown);
1113 }
1114 break;
1115 case SIGSEGV:
1116 abort();
1117 break;
1118 case SIGCHLD:
1119 while (waitpid(-1, NULL, WNOHANG) > 0)
1120 ;
1121 break;
1122 case SIGUSR1:
1123 if (g_flags & AF_SHUTDOWN)
1124 goto stop_handler;
1125 break;
1126 default:
1127 break;
1128 }
1129 }
1130 else
1131 {
1132 ThreadSleepMs(100);
1133 }
1134 }
1135
1136 stop_handler:
1137 sigprocmask(SIG_UNBLOCK, &signals, NULL);
1138 return THREAD_OK;
1139 }
1140
1141 #endif
1142
1143 /**
1144 * Common main()
1145 */
1146 THREAD_RESULT NXCORE_EXPORTABLE THREAD_CALL Main(void *pArg)
1147 {
1148 nxlog_write(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
1149
1150 if (IsStandalone())
1151 {
1152 if (!(g_flags & AF_DEBUG_CONSOLE_DISABLED))
1153 {
1154 char *ptr, szCommand[256];
1155 struct __console_ctx ctx;
1156 #ifdef UNICODE
1157 WCHAR wcCommand[256];
1158 #endif
1159
1160 ctx.hSocket = -1;
1161 ctx.socketMutex = INVALID_MUTEX_HANDLE;
1162 ctx.pMsg = NULL;
1163 ctx.session = NULL;
1164 ctx.output = NULL;
1165 WriteToTerminal(_T("\nNetXMS Server V") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING IS_UNICODE_BUILD_STRING _T(" Ready\n")
1166 _T("Enter \"\x1b[1mhelp\x1b[0m\" for command list or \"\x1b[1mdown\x1b[0m\" for server shutdown\n")
1167 _T("System Console\n\n"));
1168
1169 #if USE_READLINE
1170 // Initialize readline library if we use it
1171 rl_bind_key('\t', RL_INSERT_CAST rl_insert);
1172 #endif
1173
1174 while(1)
1175 {
1176 #if USE_READLINE
1177 ptr = readline("\x1b[33mnetxmsd:\x1b[0m ");
1178 #else
1179 WriteToTerminal(_T("\x1b[33mnetxmsd:\x1b[0m "));
1180 fflush(stdout);
1181 if (fgets(szCommand, 255, stdin) == NULL)
1182 break; // Error reading stdin
1183 ptr = strchr(szCommand, '\n');
1184 if (ptr != NULL)
1185 *ptr = 0;
1186 ptr = szCommand;
1187 #endif
1188
1189 if (ptr != NULL)
1190 {
1191 #ifdef UNICODE
1192 #if HAVE_MBSTOWCS
1193 mbstowcs(wcCommand, ptr, 255);
1194 #else
1195 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ptr, -1, wcCommand, 256);
1196 #endif
1197 wcCommand[255] = 0;
1198 StrStrip(wcCommand);
1199 if (wcCommand[0] != 0)
1200 {
1201 if (ProcessConsoleCommand(wcCommand, &ctx) == CMD_EXIT_SHUTDOWN)
1202 #else
1203 StrStrip(ptr);
1204 if (*ptr != 0)
1205 {
1206 if (ProcessConsoleCommand(ptr, &ctx) == CMD_EXIT_SHUTDOWN)
1207 #endif
1208 break;
1209 #if USE_READLINE
1210 add_history(ptr);
1211 #endif
1212 }
1213 #if USE_READLINE
1214 free(ptr);
1215 #endif
1216 }
1217 else
1218 {
1219 _tprintf(_T("\n"));
1220 }
1221 }
1222
1223 #if USE_READLINE
1224 free(ptr);
1225 #endif
1226 if (!(g_flags & AF_SHUTDOWN))
1227 {
1228 m_nShutdownReason = SHUTDOWN_FROM_CONSOLE;
1229 Shutdown();
1230 }
1231 }
1232 else
1233 {
1234 // standalone with debug console disabled
1235 #ifdef _WIN32
1236 _tprintf(_T("Server running. Press ESC to shutdown.\n"));
1237 while(1)
1238 {
1239 if (_getch() == 27)
1240 break;
1241 }
1242 _tprintf(_T("Server shutting down...\n"));
1243 Shutdown();
1244 #else
1245 _tprintf(_T("Server running. Press Ctrl+C to shutdown.\n"));
1246 // Shutdown will be called from signal handler
1247 ConditionWait(m_condShutdown, INFINITE);
1248 #endif
1249 }
1250 }
1251 else
1252 {
1253 ConditionWait(m_condShutdown, INFINITE);
1254 // On Win32, Shutdown() will be called by service control handler
1255 #ifndef _WIN32
1256 Shutdown();
1257 #endif
1258 }
1259 return THREAD_OK;
1260 }
1261
1262 /**
1263 * Initiate server shutdown
1264 */
1265 void InitiateShutdown()
1266 {
1267 #ifdef _WIN32
1268 Shutdown();
1269 #else
1270 if (IsStandalone())
1271 {
1272 Shutdown();
1273 }
1274 else
1275 {
1276 pthread_kill(m_signalHandlerThread, SIGTERM);
1277 }
1278 #endif
1279 }
1280
1281 /**
1282 *DLL Entry point
1283 */
1284 #ifdef _WIN32
1285
1286 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1287 {
1288 if (dwReason == DLL_PROCESS_ATTACH)
1289 DisableThreadLibraryCalls(hInstance);
1290 return TRUE;
1291 }
1292
1293 #endif