Initial version of crash dump generator on Windows
[public/netxms.git] / src / server / netxmsd / netxmsd.cpp
1 /* $Id: netxmsd.cpp,v 1.17 2007-02-09 17:31:57 victor Exp $ */
2 /*
3 ** NetXMS - Network Management System
4 ** Server startup module
5 ** Copyright (C) 2003, 2004, 2005, 2006, 2007 NetXMS Team
6 **
7 ** This program is free software; you can redistribute it and/or modify
8 ** it under the terms of the GNU General Public License as published by
9 ** the Free Software Foundation; either version 2 of the License, or
10 ** (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software
19 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 **
21 ** File: netxmsd.cpp
22 **
23 **/
24
25 #include "netxmsd.h"
26 #include <dbghelp.h>
27
28
29 //
30 // Global data
31 //
32
33 BOOL g_bCheckDB = FALSE;
34
35
36 //
37 // Help text
38 //
39
40 static char help_text[]="NetXMS Server Version " NETXMS_VERSION_STRING "\n"
41 "Copyright (c) 2003, 2004, 2005 NetXMS Team\n\n"
42 "Usage: netxmsd [<options>] <command>\n\n"
43 "Valid options are:\n"
44 " --check-db : Run database check on startup\n"
45 " --config <file> : Set non-default configuration file\n"
46 " : Default is " DEFAULT_CONFIG_FILE "\n"
47 " --debug-all : Turn on all possible debug output\n"
48 " --debug-actions : Print debug information for event actions.\n"
49 " --debug-cscp : Print client-server communication protocol debug\n"
50 " : information to console.\n"
51 " --debug-dc : Print data collection debug information to console.\n"
52 " --debug-discovery : Print network discovery debug information to console.\n"
53 " --debug-events : Print events to console.\n"
54 " --debug-housekeeper : Print debug information for housekeeping thread.\n"
55 " --debug-locks : Print debug information about component locking.\n"
56 " --debug-objects : Print object manager debug information.\n"
57 " --debug-snmp : Print SNMP debug information.\n"
58 " --dump-sql : Dump all SQL queries to log.\n"
59 #ifdef _WIN32
60 " --login <user> : Login name for service account.\n"
61 " --password <passwd> : Password for service account.\n"
62 #else
63 " --pid-file <file> : Specify pid file.\n"
64 #endif
65 "\n"
66 "Valid commands are:\n"
67 " check-config : Check configuration file syntax\n"
68 #ifdef _WIN32
69 " install : Install Win32 service\n"
70 " install-events : Install Win32 event source\n"
71 #endif
72 " help : Display help and exit\n"
73 #ifdef _WIN32
74 " remove : Remove Win32 service\n"
75 " remove-events : Remove Win32 event source\n"
76 #endif
77 " standalone : Run in standalone mode (not as service)\n"
78 #ifdef _WIN32
79 " start : Start service\n"
80 " stop : Stop service\n"
81 #endif
82 " version : Display version and exit\n"
83 "\n"
84 "NOTE: All debug options will work only in standalone mode.\n\n";
85
86
87 //
88 // Windows exception handling
89 // ****************************************************
90 //
91
92 #ifdef _WIN32
93
94
95 //
96 // Static data
97 //
98
99 static FILE *m_pExInfoFile = NULL;
100
101
102 //
103 // Writer for SEHShowCallStack()
104 //
105
106 static void ExceptionDataWriter(char *pszText)
107 {
108 if (m_pExInfoFile != NULL)
109 fputs(pszText, m_pExInfoFile);
110 }
111
112
113 //
114 // Exception handler
115 //
116
117 static void ExceptionHandler(EXCEPTION_POINTERS *pInfo)
118 {
119 char szBuffer[MAX_PATH];
120 HANDLE hFile;
121 time_t t;
122 MINIDUMP_EXCEPTION_INFORMATION mei;
123 OSVERSIONINFO ver;
124 SYSTEM_INFO sysInfo;
125
126 t = time(NULL);
127
128 // Create info file
129 _snprintf(szBuffer, MAX_PATH, "%s\\netxmsd-%d-%u.info",
130 g_szDumpDir, GetCurrentProcessId(), (DWORD)t);
131 m_pExInfoFile = fopen(szBuffer, "w");
132 if (m_pExInfoFile != NULL)
133 {
134 fprintf(m_pExInfoFile, "NETXMSD CRASH DUMP\n%s\n", ctime(&t));
135 fprintf(m_pExInfoFile, "EXCEPTION: %08X (%s) at %08X\n",
136 pInfo->ExceptionRecord->ExceptionCode,
137 SEHExceptionName(pInfo->ExceptionRecord->ExceptionCode),
138 pInfo->ExceptionRecord->ExceptionAddress);
139
140 // NetXMS and OS version
141 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
142 GetVersionEx(&ver);
143 if (ver.dwMajorVersion == 5)
144 {
145 switch(ver.dwMinorVersion)
146 {
147 case 0:
148 strcpy(szBuffer, "2000");
149 break;
150 case 1:
151 strcpy(szBuffer, "XP");
152 break;
153 case 2:
154 strcpy(szBuffer, "Server 2003");
155 break;
156 default:
157 sprintf(szBuffer, "NT %d.%d", ver.dwMajorVersion, ver.dwMinorVersion);
158 break;
159 }
160 }
161 else
162 {
163 sprintf(szBuffer, "NT %d.%d", ver.dwMajorVersion, ver.dwMinorVersion);
164 }
165 fprintf(m_pExInfoFile, "\nNetXMS Server Version: " NETXMS_VERSION_STRING "\n"
166 "OS Version: Windows %s Build %d %s\n",
167 szBuffer, ver.dwBuildNumber, ver.szCSDVersion);
168
169 // Processor architecture
170 fprintf(m_pExInfoFile, "Processor architecture: ");
171 GetSystemInfo(&sysInfo);
172 switch(sysInfo.wProcessorArchitecture)
173 {
174 case PROCESSOR_ARCHITECTURE_INTEL:
175 fprintf(m_pExInfoFile, "Intel x86\n");
176 break;
177 case PROCESSOR_ARCHITECTURE_MIPS:
178 fprintf(m_pExInfoFile, "MIPS\n");
179 break;
180 case PROCESSOR_ARCHITECTURE_ALPHA:
181 fprintf(m_pExInfoFile, "ALPHA\n");
182 break;
183 case PROCESSOR_ARCHITECTURE_PPC:
184 fprintf(m_pExInfoFile, "PowerPC\n");
185 break;
186 case PROCESSOR_ARCHITECTURE_IA64:
187 fprintf(m_pExInfoFile, "Intel IA-64\n");
188 break;
189 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
190 fprintf(m_pExInfoFile, "Intel x86 on Win64\n");
191 break;
192 case PROCESSOR_ARCHITECTURE_AMD64:
193 fprintf(m_pExInfoFile, "AMD64 (Intel EM64T)\n");
194 break;
195 default:
196 fprintf(m_pExInfoFile, "UNKNOWN\n");
197 break;
198 }
199
200 #ifdef _X86_
201 fprintf(m_pExInfoFile, "\nRegister information:\n"
202 " eax=%08X ebx=%08X ecx=%08X edx=%08X\n"
203 " esi=%08X edi=%08X ebp=%08X esp=%08X\n"
204 " cs=%04X ds=%04X es=%04X ss=%04X fs=%04X gs=%04X flags=%08X\n",
205 pInfo->ContextRecord->Eax, pInfo->ContextRecord->Ebx,
206 pInfo->ContextRecord->Ecx, pInfo->ContextRecord->Edx,
207 pInfo->ContextRecord->Esi, pInfo->ContextRecord->Edi,
208 pInfo->ContextRecord->Ebp, pInfo->ContextRecord->Esp,
209 pInfo->ContextRecord->SegCs, pInfo->ContextRecord->SegDs,
210 pInfo->ContextRecord->SegEs, pInfo->ContextRecord->SegSs,
211 pInfo->ContextRecord->SegFs, pInfo->ContextRecord->SegGs,
212 pInfo->ContextRecord->EFlags);
213 #endif
214
215 fprintf(m_pExInfoFile, "\nCall stack:\n");
216 SEHShowCallStack(pInfo->ContextRecord);
217
218 fclose(m_pExInfoFile);
219 }
220
221 // Create minidump
222 _snprintf(szBuffer, MAX_PATH, "%s\\netxmsd-%d-%u.mdmp",
223 g_szDumpDir, GetCurrentProcessId(), (DWORD)t);
224 hFile = CreateFile(szBuffer, GENERIC_WRITE, 0, NULL,
225 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
226 if (hFile != INVALID_HANDLE_VALUE)
227 {
228 mei.ThreadId = GetCurrentThreadId();
229 mei.ExceptionPointers = pInfo;
230 mei.ClientPointers = FALSE;
231 MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
232 MiniDumpNormal, &mei, NULL, NULL);
233 CloseHandle(hFile);
234 }
235
236 if (IsStandalone())
237 {
238 printf("\n\n*************************************************************\n"
239 "EXCEPTION: %08X (%s) at %08X\nPROCESS TERMINATED",
240 pInfo->ExceptionRecord->ExceptionCode,
241 SEHExceptionName(pInfo->ExceptionRecord->ExceptionCode),
242 pInfo->ExceptionRecord->ExceptionAddress);
243 }
244 }
245
246
247 #endif /* _WIN32 */
248
249
250 //
251 // Execute command and wait
252 //
253
254 static BOOL ExecAndWait(TCHAR *pszCommand)
255 {
256 BOOL bSuccess = TRUE;
257
258 #ifdef _WIN32
259 STARTUPINFO si;
260 PROCESS_INFORMATION pi;
261
262 // Fill in process startup info structure
263 memset(&si, 0, sizeof(STARTUPINFO));
264 si.cb = sizeof(STARTUPINFO);
265 si.dwFlags = 0;
266
267 // Create new process
268 if (!CreateProcess(NULL, pszCommand, NULL, NULL, FALSE,
269 IsStandalone() ? 0 : CREATE_NO_WINDOW | DETACHED_PROCESS,
270 NULL, NULL, &si, &pi))
271 {
272 bSuccess = FALSE;
273 }
274 else
275 {
276 WaitForSingleObject(pi.hProcess, INFINITE);
277
278 // Close all handles
279 CloseHandle(pi.hThread);
280 CloseHandle(pi.hProcess);
281 }
282 #else
283 bSuccess = (system(pszCommand) != -1);
284 #endif
285
286 return bSuccess;
287 }
288
289
290 //
291 // Parse command line
292 // Returns TRUE on success and FALSE on failure
293 //
294
295 static BOOL ParseCommandLine(int argc, char *argv[])
296 {
297 int i;
298 TCHAR *pszLogin = NULL, *pszPassword = NULL;
299
300 for(i = 1; i < argc; i++)
301 {
302 if (!strcmp(argv[i], "help")) // Display help and exit
303 {
304 printf(help_text);
305 return FALSE;
306 }
307 else if (!strcmp(argv[i], "version")) // Display version and exit
308 {
309 printf("NetXMS Server Version " NETXMS_VERSION_STRING " Build of " __DATE__ "\n");
310 return FALSE;
311 }
312 else if (!strcmp(argv[i], "--config")) // Config file
313 {
314 i++;
315 nx_strncpy(g_szConfigFile, argv[i], MAX_PATH); // Next word should contain name of the config file
316 }
317 else if (!strcmp(argv[i], "--check-db"))
318 {
319 g_bCheckDB = TRUE;
320 }
321 #ifdef _WIN32
322 else if (!strcmp(argv[i], "--login"))
323 {
324 i++;
325 pszLogin = argv[i];
326 }
327 else if (!strcmp(argv[i], "--password"))
328 {
329 i++;
330 pszPassword = argv[i];
331 }
332 #else
333 else if (!strcmp(argv[i], "--pid-file")) // PID file
334 {
335 i++;
336 nx_strncpy(g_szPIDFile, argv[i], MAX_PATH); // Next word should contain name of the PID file
337 }
338 #endif
339 else if (!strcmp(argv[i], "--debug-all"))
340 {
341 g_dwFlags |= AF_DEBUG_ALL;
342 }
343 else if (!strcmp(argv[i], "--debug-events"))
344 {
345 g_dwFlags |= AF_DEBUG_EVENTS;
346 }
347 else if (!strcmp(argv[i], "--debug-cscp"))
348 {
349 g_dwFlags |= AF_DEBUG_CSCP;
350 }
351 else if (!strcmp(argv[i], "--debug-discovery"))
352 {
353 g_dwFlags |= AF_DEBUG_DISCOVERY;
354 }
355 else if (!strcmp(argv[i], "--debug-dc"))
356 {
357 g_dwFlags |= AF_DEBUG_DC;
358 }
359 else if (!strcmp(argv[i], "--debug-locks"))
360 {
361 g_dwFlags |= AF_DEBUG_LOCKS;
362 }
363 else if (!strcmp(argv[i], "--debug-objects"))
364 {
365 g_dwFlags |= AF_DEBUG_OBJECTS;
366 }
367 else if (!strcmp(argv[i], "--debug-housekeeper"))
368 {
369 g_dwFlags |= AF_DEBUG_HOUSEKEEPER;
370 }
371 else if (!strcmp(argv[i], "--debug-actions"))
372 {
373 g_dwFlags |= AF_DEBUG_ACTIONS;
374 }
375 else if (!strcmp(argv[i], "--debug-snmp"))
376 {
377 g_dwFlags |= AF_DEBUG_SNMP;
378 }
379 else if (!strcmp(argv[i], "--dump-sql"))
380 {
381 g_dwFlags |= AF_DEBUG_SQL;
382 }
383 else if (!strcmp(argv[i], "check-config"))
384 {
385 g_dwFlags |= AF_STANDALONE;
386 printf("Checking configuration file (%s):\n\n", g_szConfigFile);
387 LoadConfig();
388 return FALSE;
389 }
390 else if (!strcmp(argv[i], "standalone")) // Run in standalone mode
391 {
392 g_dwFlags |= AF_STANDALONE;
393 return TRUE;
394 }
395 #ifdef _WIN32
396 else if ((!strcmp(argv[i], "install"))||
397 (!strcmp(argv[i], "install-events")))
398 {
399 char exePath[MAX_PATH], dllPath[MAX_PATH], *ptr;
400
401 ptr = strrchr(argv[0], '\\');
402 if (ptr != NULL)
403 ptr++;
404 else
405 ptr = argv[0];
406
407 _fullpath(exePath, ptr, 255);
408
409 if (stricmp(&exePath[strlen(exePath)-4], ".exe"))
410 strcat(exePath, ".exe");
411 strcpy(dllPath, exePath);
412 ptr = strrchr(dllPath, '\\');
413 if (ptr != NULL) // Shouldn't be NULL
414 {
415 ptr++;
416 strcpy(ptr, "libnxsrv.dll");
417 }
418
419 if (!strcmp(argv[i], "install"))
420 {
421 if ((pszLogin != NULL) && (pszPassword == NULL))
422 pszPassword = _T("");
423 if ((pszLogin == NULL) && (pszPassword != NULL))
424 pszPassword = NULL;
425 InstallService(exePath, dllPath, pszLogin, pszPassword);
426 }
427 else
428 {
429 InstallEventSource(dllPath);
430 }
431 return FALSE;
432 }
433 else if (!strcmp(argv[i], "remove"))
434 {
435 RemoveService();
436 return FALSE;
437 }
438 else if (!strcmp(argv[i], "remove-events"))
439 {
440 RemoveEventSource();
441 return FALSE;
442 }
443 else if (!strcmp(argv[i], "start"))
444 {
445 StartCoreService();
446 return FALSE;
447 }
448 else if (!strcmp(argv[i], "stop"))
449 {
450 StopCoreService();
451 return FALSE;
452 }
453 #endif /* _WIN32 */
454 else
455 {
456 printf("ERROR: Invalid command line argument\n\n");
457 return FALSE;
458 }
459 }
460
461 return TRUE;
462 }
463
464
465 //
466 // Startup code
467 //
468
469 int main(int argc, char *argv[])
470 {
471 #ifdef _WIN32
472 HKEY hKey;
473 DWORD dwSize;
474 #else
475 int i;
476 FILE *fp;
477 char *pszEnv;
478 #endif
479
480 InitThreadLibrary();
481
482 #ifdef NETXMS_MEMORY_DEBUG
483 InitMemoryDebugger();
484 #endif
485
486 // Check for alternate config file location
487 #ifdef _WIN32
488 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Server"), 0,
489 KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
490 {
491 dwSize = MAX_PATH * sizeof(TCHAR);
492 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)g_szConfigFile, &dwSize);
493 RegCloseKey(hKey);
494 }
495 #else
496 pszEnv = getenv("NETXMSD_CONFIG");
497 if (pszEnv != NULL)
498 nx_strncpy(g_szConfigFile, pszEnv, MAX_PATH);
499 #endif
500
501 if (!ParseCommandLine(argc, argv))
502 return 1;
503
504 if (!LoadConfig())
505 {
506 if (IsStandalone())
507 printf("Error loading configuration file\n");
508 return 1;
509 }
510
511 // Set exception handler
512 #ifdef _WIN32
513 if (g_dwFlags & AF_CATCH_EXCEPTIONS)
514 SetExceptionHandler(ExceptionHandler, ExceptionDataWriter);
515 #endif
516
517 // Check database before start if requested
518 if (g_bCheckDB)
519 {
520 char szCmd[MAX_PATH + 128], *pszSep;
521
522 strncpy(szCmd, argv[0], MAX_PATH);
523 pszSep = strrchr(szCmd, FS_PATH_SEPARATOR[0]);
524 if (pszSep != NULL)
525 pszSep++;
526 else
527 pszSep = szCmd;
528 sprintf(pszSep, "nxdbmgr -c \"%s\" -f check", g_szConfigFile);
529 if (!ExecAndWait(szCmd))
530 if (IsStandalone())
531 printf("ERROR: Failed to execute command \"%s\"\n", szCmd);
532 }
533
534 #ifdef _WIN32
535 if (!IsStandalone())
536 {
537 InitService();
538 }
539 else
540 {
541 if (!Initialize())
542 {
543 printf("NetXMS Core initialization failed\n");
544
545 // Remove database lock
546 if (g_dwFlags & AF_DB_LOCKED)
547 {
548 UnlockDB();
549 ShutdownDB();
550 }
551 return 3;
552 }
553 Main(NULL);
554 }
555 #else /* not _WIN32 */
556 if (!IsStandalone())
557 {
558 if (daemon(0, 0) == -1)
559 {
560 perror("Call to daemon() failed");
561 return 2;
562 }
563 }
564
565 // Write PID file
566 fp = fopen(g_szPIDFile, "w");
567 if (fp != NULL)
568 {
569 fprintf(fp, "%d", getpid());
570 fclose(fp);
571 }
572
573 // Initialize server
574 if (!Initialize())
575 {
576 // Remove database lock
577 if (g_dwFlags & AF_DB_LOCKED)
578 {
579 UnlockDB();
580 ShutdownDB();
581 }
582 return 3;
583 }
584
585 // Everything is OK, start common main loop
586 StartMainLoop(SignalHandler, Main);
587 #endif /* _WIN32 */
588 return 0;
589 }