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