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