004ccf01db7a6214bc1fd48c4b249b47d8bda1a5
[public/netxms.git] / src / server / core / main.cpp
1 /*
2 ** Project X - Network Management System
3 ** Copyright (C) 2003 Victor Kirhenshtein
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 #ifdef _WIN32
25 #include <conio.h>
26 #endif /* _WIN32 */
27
28 void DumpUsers(void);
29 void DumpSessions(void);
30
31
32 //
33 // Thread functions
34 //
35
36 void HouseKeeper(void *pArg);
37 void DiscoveryThread(void *pArg);
38 void Syncer(void *pArg);
39 void NodePoller(void *pArg);
40 void StatusPoller(void *pArg);
41 void ConfigurationPoller(void *pArg);
42 void EventProcessor(void *pArg);
43 void WatchdogThread(void *pArg);
44 void ClientListener(void *pArg);
45 void LocalAdminListener(void *pArg);
46 void DBWriteThread(void *pArg);
47
48
49 //
50 // Global variables
51 //
52
53 DWORD g_dwFlags = AF_USE_EVENT_LOG;
54 char g_szConfigFile[MAX_PATH] = DEFAULT_CONFIG_FILE;
55 char g_szLogFile[MAX_PATH] = DEFAULT_LOG_FILE;
56 DB_HANDLE g_hCoreDB = 0;
57 DWORD g_dwDiscoveryPollingInterval;
58 DWORD g_dwStatusPollingInterval;
59 DWORD g_dwConfigurationPollingInterval;
60
61
62 //
63 // Static data
64 //
65
66 #ifdef _WIN32
67 static HANDLE m_hEventShutdown = INVALID_HANDLE_VALUE;
68 #else /* _WIN32 */
69 static pthread_cond_t m_hCondShutdown;
70 #endif
71
72
73 //
74 // Sleep for specified number of seconds or until system shutdown arrives
75 // Function will return TRUE if shutdown event occurs
76 //
77
78 BOOL SleepAndCheckForShutdown(int iSeconds)
79 {
80 #ifdef _WIN32
81 return WaitForSingleObject(m_hEventShutdown, iSeconds * 1000) == WAIT_OBJECT_0;
82 #else /* _WIN32 */
83 /* TODO: Implement UNIX code */
84 #endif
85 }
86
87
88 //
89 // Load global configuration parameters
90 //
91
92 static void LoadGlobalConfig()
93 {
94 g_dwDiscoveryPollingInterval = ConfigReadInt("DiscoveryPollingInterval", 900);
95 g_dwStatusPollingInterval = ConfigReadInt("StatusPollingInterval", 60);
96 g_dwConfigurationPollingInterval = ConfigReadInt("ConfigurationPollingInterval", 3600);
97 if (ConfigReadInt("EnableAccessControl", 1))
98 g_dwFlags |= AF_ENABLE_ACCESS_CONTROL;
99 if (ConfigReadInt("EnableEventsAccessControl", 1))
100 g_dwFlags |= AF_ENABLE_EVENTS_ACCESS_CONTROL;
101 if (ConfigReadInt("DeleteEmptySubnets", 1))
102 g_dwFlags |= AF_DELETE_EMPTY_SUBNETS;
103 if (ConfigReadInt("EnableSNMPTraps", 1))
104 g_dwFlags |= AF_ENABLE_SNMP_TRAPD;
105 }
106
107
108 //
109 // Server initialization
110 //
111
112 BOOL Initialize(void)
113 {
114 int i, iNumThreads;
115 DWORD dwAddr;
116 char szInfo[256];
117
118 InitLog();
119
120 #ifdef _WIN32
121 WSADATA wsaData;
122 WSAStartup(0x0002, &wsaData);
123 #endif
124
125 // Initialize SSL library
126 SSL_library_init();
127 SSL_load_error_strings();
128
129 // Create queue for delayed SQL queries
130 g_pLazyRequestQueue = new Queue(64, 16);
131
132 // Initialize SNMP stuff
133 SnmpInit();
134
135 if (!DBInit())
136 return FALSE;
137
138 g_hCoreDB = DBConnect();
139 if (g_hCoreDB == 0)
140 {
141 WriteLog(MSG_DB_CONNFAIL, EVENTLOG_ERROR_TYPE, NULL);
142 return FALSE;
143 }
144
145 // Initialize locks
146 if (!InitLocks(&dwAddr, szInfo))
147 {
148 if (dwAddr == UNLOCKED) // Some SQL problems
149 WriteLog(MSG_INIT_LOCKS_FAILED, EVENTLOG_ERROR_TYPE, NULL);
150 else // Database already locked by another server instance
151 WriteLog(MSG_DB_LOCKED, EVENTLOG_ERROR_TYPE, "as", dwAddr, szInfo);
152 return FALSE;
153 }
154
155 // Load global configuration parameters
156 LoadGlobalConfig();
157
158 // Create synchronization stuff
159 #ifdef _WIN32
160 m_hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
161 #else
162 pthread_cond_init(&m_hCondShutdown, NULL);
163 #endif
164
165 // Setup unique identifiers table
166 if (!InitIdTable())
167 return FALSE;
168
169 // Load users from database
170 if (!LoadUsers())
171 {
172 WriteLog(MSG_ERROR_LOADING_USERS, EVENTLOG_ERROR_TYPE, NULL);
173 return FALSE;
174 }
175
176 // Initialize objects infrastructure and load objects from database
177 ObjectsInit();
178 if (!LoadObjects())
179 return FALSE;
180
181 // Load event actions
182 if (!LoadActions())
183 return FALSE;
184
185 // Initialize event handling subsystem
186 if (!InitEventSubsystem())
187 return FALSE;
188
189 // Initialize data collection subsystem
190 if (!InitDataCollector())
191 return FALSE;
192
193 // Initialize watchdog
194 WatchdogInit();
195
196 // Start threads
197 ThreadCreate(WatchdogThread, 0, NULL);
198 ThreadCreate(HouseKeeper, 0, NULL);
199 ThreadCreate(Syncer, 0, NULL);
200 ThreadCreate(NodePoller, 0, NULL);
201 ThreadCreate(StatusPoller, 0, NULL);
202 ThreadCreate(ConfigurationPoller, 0, NULL);
203 ThreadCreate(ClientListener, 0, NULL);
204
205 // Start network discovery thread if required
206 if (ConfigReadInt("RunNetworkDiscovery", 1))
207 ThreadCreate(DiscoveryThread, 0, NULL);
208 else
209 CheckForMgmtNode();
210
211 // Start event processors
212 iNumThreads = ConfigReadInt("NumberOfEventProcessors", 1);
213 for(i = 0; i < iNumThreads; i++)
214 ThreadCreate(EventProcessor, 0, NULL);
215
216 // Start database "lazy" write thread
217 ThreadCreate(DBWriteThread, 0, NULL);
218
219 // Start local administartive interface listener if required
220 if (ConfigReadInt("EnableAdminInterface", 1))
221 ThreadCreate(LocalAdminListener, 0, NULL);
222
223 return TRUE;
224 }
225
226
227 //
228 // Handler for client notification
229 //
230
231 static void NotifyClient(ClientSession *pSession, void *pArg)
232 {
233 pSession->Notify(NX_NOTIFY_SHUTDOWN);
234 }
235
236
237 //
238 // Server shutdown
239 //
240
241 void Shutdown(void)
242 {
243 // Notify clients
244 EnumerateClientSessions(NotifyClient, NULL);
245
246 WriteLog(MSG_SERVER_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
247 g_dwFlags |= AF_SHUTDOWN; // Set shutdown flag
248 #ifdef _WIN32
249 SetEvent(m_hEventShutdown);
250 #else /* _WIN32 */
251 pthread_cond_broadcast(&m_hCondShutdown);
252 #endif
253
254 g_pEventQueue->Put(INVALID_POINTER_VALUE); // Stop event processor
255 ThreadSleep(5); // Give other threads a chance to terminate in a safe way
256 SaveObjects();
257 StopDBWriter();
258
259 // Remove database lock
260 DBQuery(g_hCoreDB, "UPDATE locks SET lock_status=-1,owner_info='' WHERE component_id=0");
261
262 // Disconnect from database and unload driver
263 if (g_hCoreDB != 0)
264 DBDisconnect(g_hCoreDB);
265 DBUnloadDriver();
266
267 DestroyActionList();
268
269 CloseLog();
270 }
271
272
273 //
274 // Common main()
275 //
276
277 void Main(void)
278 {
279 WriteLog(MSG_SERVER_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
280
281 #ifdef _WIN32
282 if (IsStandalone())
283 {
284 int ch;
285
286 printf("\n*** NMS Server operational. Press ESC to terminate. ***\n");
287 while(1)
288 {
289 ch = getch();
290 if (ch == 0)
291 ch = -getch();
292
293 if (ch == 27)
294 break;
295
296 #ifdef _DEBUG
297 switch(ch)
298 {
299 case 't': // Thread status
300 case 'T':
301 WatchdogPrintStatus();
302 printf("*** Done ***\n");
303 break;
304 case 'd': // Dump objects
305 case 'D':
306 {
307 DWORD i;
308 char *pBuffer;
309 static char *objTypes[]={ "Generic", "Subnet", "Node", "Interface", "Network" };
310
311 pBuffer = (char *)malloc(128000);
312 MutexLock(g_hMutexIdIndex, INFINITE);
313 for(i = 0; i < g_dwIdIndexSize; i++)
314 {
315 printf("Object ID %d\n"
316 " Name='%s' Type=%s Addr=%s Status=%d IsModified=%d\n",
317 g_pIndexById[i].pObject->Id(),g_pIndexById[i].pObject->Name(),
318 objTypes[g_pIndexById[i].pObject->Type()],
319 IpToStr(g_pIndexById[i].pObject->IpAddr(), pBuffer),
320 g_pIndexById[i].pObject->Status(), g_pIndexById[i].pObject->IsModified());
321 printf(" Parents: <%s> Childs: <%s>\n",
322 g_pIndexById[i].pObject->ParentList(pBuffer),
323 g_pIndexById[i].pObject->ChildList(&pBuffer[4096]));
324 if (g_pIndexById[i].pObject->Type() == OBJECT_NODE)
325 printf(" IsSNMP:%d IsAgent:%d IsLocal:%d OID='%s'\n",
326 ((Node *)(g_pIndexById[i].pObject))->IsSNMPSupported(),
327 ((Node *)(g_pIndexById[i].pObject))->IsNativeAgent(),
328 ((Node *)(g_pIndexById[i].pObject))->IsLocalManagenet(),
329 ((Node *)(g_pIndexById[i].pObject))->ObjectId());
330 }
331 MutexUnlock(g_hMutexIdIndex);
332 free(pBuffer);
333 printf("*** Object dump complete ***\n");
334 }
335 break;
336 case 'i': // Dump interface index by IP
337 case 'I':
338 {
339 DWORD i;
340 char szBuffer[32];
341
342 for(i = 0; i < g_dwInterfaceAddrIndexSize; i++)
343 {
344 printf("%10u %-15s -> %d [0x%08x]\n",
345 g_pInterfaceIndexByAddr[i].dwKey,
346 IpToStr(g_pInterfaceIndexByAddr[i].dwKey, szBuffer),
347 g_pInterfaceIndexByAddr[i].pObject->Id(),
348 g_pInterfaceIndexByAddr[i].pObject);
349 }
350 printf("*** Interface IP index dump complete ***\n");
351 }
352 break;
353 case 'm': // Print mutex status
354 case 'M':
355 printf("Mutex status:\n");
356 DbgTestMutex(g_hMutexIdIndex, "g_hMutexIdIndex");
357 DbgTestMutex(g_hMutexNodeIndex, "g_hMutexNodeIndex");
358 DbgTestMutex(g_hMutexSubnetIndex, "g_hMutexSubnetIndex");
359 DbgTestMutex(g_hMutexInterfaceIndex, "g_hMutexInterfaceIndex");
360 DbgTestMutex(g_hMutexObjectAccess, "g_hMutexObjectAccess");
361 printf("*** Done ***\n");
362 break;
363 case 'g': // Generate test objects
364 case 'G':
365 {
366 int i;
367 char szQuery[1024];
368
369 for(i = 0; i < 10000; i++)
370 {
371 sprintf(szQuery, "INSERT INTO newnodes (id,ip_addr,ip_netmask,discovery_flags) VALUES (%d,%d,65535,%d)",
372 i + 1000, htonl(0x0A800001 + i), DF_DEFAULT);
373 DBQuery(g_hCoreDB, szQuery);
374 }
375 }
376 break;
377 case 'u': // Dump users
378 case 'U':
379 DumpUsers();
380 break;
381 case 's': // Dump sessions
382 case 'S':
383 DumpSessions();
384 break;
385 default:
386 break;
387 }
388 #endif
389 }
390
391 Shutdown();
392 }
393 else
394 {
395 WaitForSingleObject(m_hEventShutdown, INFINITE);
396 }
397 #else /* _WIN32 */
398 /* TODO: insert UNIX main thread code here */
399 #endif
400 }
401
402
403 //
404 // Startup code
405 //
406
407 int main(int argc, char *argv[])
408 {
409 if (!ParseCommandLine(argc, argv))
410 return 1;
411
412 if (!LoadConfig())
413 return 1;
414
415 #ifdef _WIN32
416 if (!IsStandalone())
417 {
418 InitService();
419 }
420 else
421 {
422 if (!Initialize())
423 {
424 printf("NMS Core initialization failed\n");
425 return 3;
426 }
427 Main();
428 }
429 #else /* _WIN32 */
430 if (!IsStandalone())
431 {
432 if (daemon(0, 0) == -1)
433 {
434 perror("Call to daemon() failed");
435 return 2;
436 }
437 }
438 if (!Initialize())
439 return 3;
440 Main();
441 #endif /* _WIN32 */
442 return 0;
443 }