- Changes in logging and debug output
[public/netxms.git] / src / server / core / main.cpp
CommitLineData
9a19737f 1/*
f4c99d5a
VK
2** NetXMS - Network Management System
3** Copyright (C) 2003, 2004 NetXMS Team
9a19737f
VK
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** $module: main.cpp
20**
21**/
22
23#include "nms_core.h"
2260ffe5 24#include <netxmsdb.h>
78bf9c68 25
fb1a5953
VK
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
06e7be2f 32#ifdef _WIN32
c9ccd685
AK
33# include <direct.h>
34# include <errno.h>
35#else
36# include <sys/stat.h>
06e7be2f
VK
37#endif
38
3a2f672c
VK
39
40//
41// Thread functions
42//
43
ccdbbb52
VK
44THREAD_RESULT THREAD_CALL HouseKeeper(void *pArg);
45THREAD_RESULT THREAD_CALL DiscoveryThread(void *pArg);
46THREAD_RESULT THREAD_CALL Syncer(void *pArg);
47THREAD_RESULT THREAD_CALL NodePoller(void *pArg);
48THREAD_RESULT THREAD_CALL StatusPoller(void *pArg);
49THREAD_RESULT THREAD_CALL ConfigurationPoller(void *pArg);
50THREAD_RESULT THREAD_CALL EventProcessor(void *pArg);
51THREAD_RESULT THREAD_CALL WatchdogThread(void *pArg);
52THREAD_RESULT THREAD_CALL ClientListener(void *pArg);
53THREAD_RESULT THREAD_CALL LocalAdminListener(void *pArg);
9a19737f
VK
54
55
56//
57// Global variables
58//
59
3e4e127f 60DWORD g_dwFlags = AF_USE_EVENT_LOG;
4385fa12
VK
61char g_szConfigFile[MAX_PATH] = DEFAULT_CONFIG_FILE;
62char g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
3a2f672c 63DB_HANDLE g_hCoreDB = 0;
48b1c0ac 64DWORD g_dwDiscoveryPollingInterval;
f00307b7 65DWORD g_dwStatusPollingInterval;
364aa19a 66DWORD g_dwConfigurationPollingInterval;
06e7be2f 67char g_szDataDir[MAX_PATH];
3a2f672c
VK
68
69
70//
71// Static data
72//
73
d16cf8a5 74static CONDITION m_hEventShutdown;
9a19737f
VK
75
76
d627ae40
VK
77//
78// Sleep for specified number of seconds or until system shutdown arrives
79// Function will return TRUE if shutdown event occurs
80//
81
82BOOL SleepAndCheckForShutdown(int iSeconds)
83{
d16cf8a5 84 return ConditionWait(m_hEventShutdown, iSeconds * 1000);
d627ae40
VK
85}
86
87
06e7be2f
VK
88//
89// Check data directory for existence
90//
91
92static BOOL CheckDataDir(void)
93{
94 char szBuffer[MAX_PATH];
95
96 if (chdir(g_szDataDir) == -1)
97 {
98 WriteLog(MSG_INVALID_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", g_szDataDir);
99 return FALSE;
100 }
101
c9ccd685
AK
102#ifdef _WIN32
103# define MKDIR(name) mkdir(name)
104#else
105# define MKDIR(name) mkdir(name, 0700)
106#endif
107
e641b7d0
VK
108 // Create directory for mib files if it doesn't exist
109 strcpy(szBuffer, g_szDataDir);
110 strcat(szBuffer, DDIR_MIBS);
c9ccd685 111 if (MKDIR(szBuffer) == -1)
06e7be2f
VK
112 if (errno != EEXIST)
113 {
114 WriteLog(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
115 return FALSE;
116 }
e641b7d0
VK
117
118 // Create directory for image files if it doesn't exist
119 strcpy(szBuffer, g_szDataDir);
120 strcat(szBuffer, DDIR_IMAGES);
121 if (MKDIR(szBuffer) == -1)
122 if (errno != EEXIST)
123 {
124 WriteLog(MSG_ERROR_CREATING_DATA_DIR, EVENTLOG_ERROR_TYPE, "s", szBuffer);
125 return FALSE;
126 }
127
c9ccd685 128#undef MKDIR
06e7be2f
VK
129
130 return TRUE;
131}
132
133
48b1c0ac
VK
134//
135// Load global configuration parameters
136//
137
d627ae40 138static void LoadGlobalConfig()
48b1c0ac 139{
364aa19a 140 g_dwDiscoveryPollingInterval = ConfigReadInt("DiscoveryPollingInterval", 900);
f00307b7 141 g_dwStatusPollingInterval = ConfigReadInt("StatusPollingInterval", 60);
364aa19a 142 g_dwConfigurationPollingInterval = ConfigReadInt("ConfigurationPollingInterval", 3600);
3e4e127f
VK
143 if (ConfigReadInt("EnableAccessControl", 1))
144 g_dwFlags |= AF_ENABLE_ACCESS_CONTROL;
145 if (ConfigReadInt("EnableEventsAccessControl", 1))
146 g_dwFlags |= AF_ENABLE_EVENTS_ACCESS_CONTROL;
3ea35b38
VK
147 if (ConfigReadInt("DeleteEmptySubnets", 1))
148 g_dwFlags |= AF_DELETE_EMPTY_SUBNETS;
5add75d8
VK
149 if (ConfigReadInt("EnableSNMPTraps", 1))
150 g_dwFlags |= AF_ENABLE_SNMP_TRAPD;
06e7be2f 151 ConfigReadStr("DataDirectory", g_szDataDir, MAX_PATH, DEFAULT_DATA_DIR);
48b1c0ac
VK
152}
153
154
9a19737f
VK
155//
156// Server initialization
157//
158
159BOOL Initialize(void)
160{
2260ffe5 161 int i, iNumThreads, iDBVersion;
c7ca9142
VK
162 DWORD dwAddr;
163 char szInfo[256];
347b5bc6 164
4385fa12 165 InitLog();
0ee0c807
VK
166
167#ifdef _WIN32
168 WSADATA wsaData;
169 WSAStartup(0x0002, &wsaData);
170#endif
171
067f2689
VK
172 // Create queue for delayed SQL queries
173 g_pLazyRequestQueue = new Queue(64, 16);
174
d7d76e50 175 // Initialize SNMP stuff
be0a5a53 176 SnmpInit();
d7d76e50 177
d9c2934a 178 // Initialize database driver and connect to database
3a2f672c
VK
179 if (!DBInit())
180 return FALSE;
181
182 g_hCoreDB = DBConnect();
183 if (g_hCoreDB == 0)
184 {
185 WriteLog(MSG_DB_CONNFAIL, EVENTLOG_ERROR_TYPE, NULL);
186 return FALSE;
187 }
b1dd534d 188 DbgPrintf(AF_DEBUG_MISC, "Successfully connected to database %s@%s", g_szDbName, g_szDbServer);
3a2f672c 189
2260ffe5
VK
190 // Check database version
191 iDBVersion = ConfigReadInt("DBFormatVersion", 0);
192 if (iDBVersion != DB_FORMAT_VERSION)
193 {
194 WriteLog(MSG_WRONG_DB_VERSION, EVENTLOG_ERROR_TYPE, "dd", iDBVersion, DB_FORMAT_VERSION);
195 return FALSE;
196 }
197
c7ca9142
VK
198 // Initialize locks
199 if (!InitLocks(&dwAddr, szInfo))
200 {
201 if (dwAddr == UNLOCKED) // Some SQL problems
202 WriteLog(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
203 else // Database already locked by another server instance
204 WriteLog(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "as", dwAddr, szInfo);
205 return FALSE;
206 }
2260ffe5 207 g_dwFlags |= AF_DB_LOCKED;
c7ca9142 208
48b1c0ac
VK
209 // Load global configuration parameters
210 LoadGlobalConfig();
b1dd534d 211 DbgPrintf(AF_DEBUG_MISC, "Global configuration loaded");
48b1c0ac 212
06e7be2f
VK
213 // Check data directory
214 if (!CheckDataDir())
215 return FALSE;
216
e641b7d0
VK
217 // Update hashes for image files
218 UpdateImageHashes();
219
3a2f672c 220 // Create synchronization stuff
d16cf8a5 221 m_hEventShutdown = ConditionCreate(TRUE);
3a2f672c 222
3ea35b38
VK
223 // Setup unique identifiers table
224 if (!InitIdTable())
225 return FALSE;
226
b86ba1c0
VK
227 // Initialize mailer
228 InitMailer();
229
da54bd8d
VK
230 // Load users from database
231 if (!LoadUsers())
347b5bc6
VK
232 {
233 WriteLog(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
da54bd8d 234 return FALSE;
347b5bc6 235 }
da54bd8d 236
2da1357c 237 // Initialize objects infrastructure and load objects from database
74b83361 238 ObjectsInit();
2da1357c
VK
239 if (!LoadObjects())
240 return FALSE;
b1dd534d 241 DbgPrintf(AF_DEBUG_MISC, "Objects loaded and initialized");
74b83361 242
c9363772
VK
243 // Initialize and load event actions
244 if (!InitActions())
245 {
246 WriteLog(MSG_ACTION_INIT_ERROR, EVENTLOG_ERROR_TYPE, NULL);
7e495d97 247 return FALSE;
c9363772 248 }
7e495d97 249
c7ca9142
VK
250 // Initialize event handling subsystem
251 if (!InitEventSubsystem())
252 return FALSE;
253
2260ffe5
VK
254 // Initialize alarms
255 if (!g_alarmMgr.Init())
256 return FALSE;
257
1275c750
VK
258 // Initialize data collection subsystem
259 if (!InitDataCollector())
260 return FALSE;
261
f91fa4c2
VK
262 // Initialize watchdog
263 WatchdogInit();
264
3a2f672c 265 // Start threads
f91fa4c2 266 ThreadCreate(WatchdogThread, 0, NULL);
3a2f672c 267 ThreadCreate(HouseKeeper, 0, NULL);
74b83361 268 ThreadCreate(Syncer, 0, NULL);
a68e217b 269 ThreadCreate(NodePoller, 0, NULL);
f00307b7 270 ThreadCreate(StatusPoller, 0, NULL);
323f8bec 271 ThreadCreate(ConfigurationPoller, 0, NULL);
5d0ff6bd 272 ThreadCreate(ClientListener, 0, NULL);
3e4e127f
VK
273
274 // Start network discovery thread if required
275 if (ConfigReadInt("RunNetworkDiscovery", 1))
276 ThreadCreate(DiscoveryThread, 0, NULL);
277 else
278 CheckForMgmtNode();
279
f9b7e653
VK
280 // Start event processors
281 iNumThreads = ConfigReadInt("NumberOfEventProcessors", 1);
282 for(i = 0; i < iNumThreads; i++)
d09b1909 283 ThreadCreate(EventProcessor, 0, (void *)(i + 1));
4385fa12 284
067f2689 285 // Start database "lazy" write thread
ccdbbb52 286 StartDBWriter();
067f2689 287
3e4e127f
VK
288 // Start local administartive interface listener if required
289 if (ConfigReadInt("EnableAdminInterface", 1))
290 ThreadCreate(LocalAdminListener, 0, NULL);
291
b1dd534d 292 DbgPrintf(AF_DEBUG_MISC, "Server initialization completed");
9a19737f
VK
293 return TRUE;
294}
295
296
83f0529c
VK
297//
298// Handler for client notification
299//
300
3421c063 301void NotifyClient(ClientSession *pSession, void *pArg)
83f0529c 302{
3421c063 303 pSession->Notify((DWORD)pArg);
83f0529c
VK
304}
305
306
9a19737f
VK
307//
308// Server shutdown
309//
310
311void Shutdown(void)
312{
d09b1909 313 DWORD i, dwNumThreads;
e0d3b217 314
83f0529c 315 // Notify clients
3421c063 316 EnumerateClientSessions(NotifyClient, (void *)NX_NOTIFY_SHUTDOWN);
83f0529c 317
3a2f672c 318 WriteLog(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
c7ca9142 319 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
d16cf8a5 320 ConditionSet(m_hEventShutdown);
c7ca9142 321
d09b1909
VK
322 // Stop event processor(s)
323 g_pEventQueue->Clear();
324 dwNumThreads = ConfigReadInt("NumberOfEventProcessors", 1);
325 for(i = 0; i < dwNumThreads; i++)
326 g_pEventQueue->Put(INVALID_POINTER_VALUE);
327
b86ba1c0
VK
328 ShutdownMailer();
329
330 ThreadSleep(3); // Give other threads a chance to terminate in a safe way
b1dd534d 331 DbgPrintf(AF_DEBUG_MISC, "All threads was notified, continue with shutdown");
d09b1909 332
d627ae40 333 SaveObjects();
b1dd534d 334 DbgPrintf(AF_DEBUG_MISC, "All objects saved to database");
ff550544 335 SaveUsers();
b1dd534d 336 DbgPrintf(AF_DEBUG_MISC, "All users saved to database");
83f0529c 337 StopDBWriter();
b1dd534d 338 DbgPrintf(AF_DEBUG_MISC, "Database writer stopped");
c7ca9142
VK
339
340 // Remove database lock
341 DBQuery(g_hCoreDB, "UPDATE locks SET lock_status=-1,owner_info='' WHERE component_id=0");
342
343 // Disconnect from database and unload driver
3a2f672c
VK
344 if (g_hCoreDB != 0)
345 DBDisconnect(g_hCoreDB);
cc140cce 346 DBUnloadDriver();
c7ca9142 347
c9363772 348 CleanupActions();
e0d3b217
VK
349 ShutdownEventSubsystem();
350
351 // Delete all objects
352 for(i = 0; i < g_dwIdIndexSize; i++)
353 delete g_pIndexById[i].pObject;
7e495d97 354
4385fa12 355 CloseLog();
9a19737f
VK
356}
357
358
c868331f
VK
359//
360// Compare given string to command template with abbreviation possibility
361//
362
363static BOOL IsCommand(char *pszTemplate, char *pszString, int iMinChars)
364{
365 int i;
366
367 // Convert given string to uppercase
368 strupr(pszString);
369
370 for(i = 0; pszString[i] != 0; i++)
371 if (pszString[i] != pszTemplate[i])
372 return FALSE;
373 if (i < iMinChars)
374 return FALSE;
375 return TRUE;
376}
377
378
379//
380// Process command entered from command line in standalone mode
381// Return TRUE if command was "down"
382//
383
384static BOOL ProcessCommand(char *pszCmdLine)
385{
386 char *pArg;
387 char szBuffer[256];
388 BOOL bExitCode = FALSE;
389
390 // Get command
391 pArg = ExtractWord(pszCmdLine, szBuffer);
392
393 if (IsCommand("DOWN", szBuffer, 4))
394 {
395 bExitCode = TRUE;
396 }
397 else if (IsCommand("DUMP", szBuffer, 2))
398 {
399 // Get argument
400 pArg = ExtractWord(pArg, szBuffer);
401
402 if (IsCommand("OBJECTS", szBuffer, 1))
403 {
04738fd8 404 DumpObjects();
c868331f
VK
405 }
406 else if (IsCommand("SESSIONS", szBuffer, 1))
407 {
408 DumpSessions();
409 }
410 else if (IsCommand("USERS", szBuffer, 1))
411 {
412 DumpUsers();
413 }
414 else
415 {
416 printf("ERROR: Invalid command argument\n\n");
417 }
418 }
419 else if (IsCommand("HELP", szBuffer, 2))
420 {
421 printf("Valid commands are:\n"
422 " down - Down NetXMS server\n"
423 " dump objects - Dump network objects to screen\n"
424 " dump sessions - Dump active client sessions to screen\n"
425 " dump users - Dump users to screen\n"
426 " mutex - Display mutex status\n"
427 " wd - Display watchdog information\n"
428 "\nAlmost all commands can be abbreviated to 2 or 3 characters\n"
429 "\n");
430 }
431 else if (IsCommand("MUTEX", szBuffer, 2))
432 {
433 printf("Mutex status:\n");
434 DbgTestMutex(g_hMutexIdIndex, "g_hMutexIdIndex");
435 DbgTestMutex(g_hMutexNodeIndex, "g_hMutexNodeIndex");
436 DbgTestMutex(g_hMutexSubnetIndex, "g_hMutexSubnetIndex");
437 DbgTestMutex(g_hMutexInterfaceIndex, "g_hMutexInterfaceIndex");
438 DbgTestMutex(g_hMutexObjectAccess, "g_hMutexObjectAccess");
439 printf("\n");
440 }
441 else if (IsCommand("WD", szBuffer, 2))
442 {
443 WatchdogPrintStatus();
444 printf("\n");
445 }
446 else
447 {
448 printf("INVALID COMMAND\n\n");
449 }
450
451 return bExitCode;
452}
453
454
9a19737f
VK
455//
456// Common main()
457//
458
459void Main(void)
460{
3a2f672c
VK
461 WriteLog(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
462
3a2f672c
VK
463 if (IsStandalone())
464 {
c868331f 465 char *ptr, szCommand[256];
3a2f672c 466
c868331f
VK
467 printf("\nNetXMS Server V" NETXMS_VERSION_STRING " Ready\n"
468 "Enter \"help\" for command list or \"down\" for server shutdown\n"
469 "System Console\n\n");
fb1a5953
VK
470
471#if USE_READLINE
472 // Initialize readline library if we use it
473 rl_bind_key('\t', rl_insert);
474#endif
c868331f 475
3a2f672c
VK
476 while(1)
477 {
fb1a5953
VK
478#if USE_READLINE
479 ptr = readline("netxmsd: ");
480#else
c868331f
VK
481 printf("netxmsd: ");
482 fflush(stdout);
483 fgets(szCommand, 255, stdin);
484 ptr = strchr(szCommand, '\n');
485 if (ptr != NULL)
486 *ptr = 0;
fb1a5953
VK
487 ptr = szCommand;
488#endif
c868331f 489
fb1a5953
VK
490 if (ptr != NULL)
491 {
492 StrStrip(ptr);
493 if (*ptr != 0)
494 {
495 if (ProcessCommand(ptr))
496 break;
497#if USE_READLINE
498 add_history(ptr);
499#endif
500 }
501#if USE_READLINE
502 free(ptr);
503#endif
504 }
505 else
506 {
507 printf("\n");
508 }
3a2f672c
VK
509 }
510
fb1a5953
VK
511#if USE_READLINE
512 free(ptr);
513#endif
3a2f672c
VK
514 Shutdown();
515 }
516 else
517 {
d16cf8a5 518 ConditionWait(m_hEventShutdown, INFINITE);
3a2f672c 519 }
9a19737f
VK
520}
521
522
523//
524// Startup code
525//
526
527int main(int argc, char *argv[])
528{
4385fa12 529 if (!ParseCommandLine(argc, argv))
9a19737f
VK
530 return 1;
531
4385fa12 532 if (!LoadConfig())
77312b6f
VK
533 {
534 if (IsStandalone())
535 printf("Error loading configuration file\n");
9a19737f 536 return 1;
77312b6f 537 }
9a19737f 538
b7271ccb 539#ifdef _WIN32
9a19737f
VK
540 if (!IsStandalone())
541 {
542 InitService();
543 }
544 else
545 {
546 if (!Initialize())
547 {
548 printf("NMS Core initialization failed\n");
06e7be2f
VK
549
550 // Remove database lock
2260ffe5 551 if (g_dwFlags & AF_DB_LOCKED)
06e7be2f
VK
552 {
553 DBQuery(g_hCoreDB, "UPDATE locks SET lock_status=-1,owner_info='' WHERE component_id=0");
554 DBDisconnect(g_hCoreDB);
555 }
b7271ccb 556 return 3;
9a19737f
VK
557 }
558 Main();
559 }
b7271ccb
VK
560#else /* _WIN32 */
561 if (!IsStandalone())
562 {
85303064 563#if HAVE_DAEMON
b7271ccb
VK
564 if (daemon(0, 0) == -1)
565 {
566 perror("Call to daemon() failed");
567 return 2;
568 }
85303064
VK
569#else
570 pid_t pid;
571
572 pid = fork();
573 if (pid == -1)
574 {
575 perror("Call to fork() failed");
576 return 2;
577 }
578 if (pid != 0) // Parent process?
579 return 0;
580
581 /* TODO: add full daemon initialization */
582#endif
b7271ccb
VK
583 }
584 if (!Initialize())
06e7be2f
VK
585 {
586 // Remove database lock
2260ffe5 587 if (g_dwFlags & AF_DB_LOCKED)
06e7be2f
VK
588 {
589 DBQuery(g_hCoreDB, "UPDATE locks SET lock_status=-1,owner_info='' WHERE component_id=0");
590 DBDisconnect(g_hCoreDB);
591 }
b7271ccb 592 return 3;
06e7be2f 593 }
b7271ccb
VK
594 Main();
595#endif /* _WIN32 */
9a19737f
VK
596 return 0;
597}