new NXSL functions: AgentReadParameter, ManageObject, UnmanageObject
[public/netxms.git] / src / server / netxmsd / netxmsd.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
3** Server startup module
1621a079 4** Copyright (C) 2003-2011 NetXMS Team
5039dede
AK
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** File: netxmsd.cpp
21**
22**/
23
24#include "netxmsd.h"
25
26#if HAVE_GETOPT_H
27#include <getopt.h>
28#endif
29
30#ifdef _WIN32
31#include <dbghelp.h>
32#endif
33
44cdd527
VK
34#ifdef __sun
35#include <signal.h>
36#endif
37
f375019e
VK
38/**
39 * Global data
40 */
5039dede 41BOOL g_bCheckDB = FALSE;
8782d780 42BOOL g_bDisableInput = FALSE;
5039dede 43
f375019e
VK
44/**
45 * Help text
46 */
e0f99bf0 47static TCHAR help_text[] = _T("NetXMS Server Version ") NETXMS_VERSION_STRING _T("\n")
f375019e 48 _T("Copyright (c) 2003-2013 NetXMS Team\n\n")
e0f99bf0
VK
49 _T("Usage: netxmsd [<options>]\n\n")
50 _T("Valid options are:\n")
51 _T(" -e : Run database check on startup\n")
52 _T(" -c <file> : Set non-default configuration file\n")
53 _T(" : Default is ") DEFAULT_CONFIG_FILE _T("\n")
54 _T(" -d : Run as daemon/service\n")
55 _T(" -D <level> : Set debug level (valid levels are 0..9)\n")
8782d780 56 _T(" -q : Disable interactive console\n")
e0f99bf0 57 _T(" -h : Display help and exit\n")
5039dede 58#ifdef _WIN32
e0f99bf0
VK
59 _T(" -I : Install Windows service\n")
60 _T(" -L <user> : Login name for service account.\n")
61 _T(" -P <passwd> : Password for service account.\n")
5039dede 62#else
e0f99bf0 63 _T(" -p <file> : Specify pid file.\n")
5039dede
AK
64#endif
65#ifdef _WIN32
e0f99bf0
VK
66 _T(" -R : Remove Windows service\n")
67 _T(" -s : Start Windows service\n")
68 _T(" -S : Stop Windows service\n")
5039dede 69#endif
e0f99bf0
VK
70 _T(" -v : Display version and exit\n")
71 _T("\n");
5039dede 72
f375019e
VK
73/**
74 * Execute command and wait
75 */
e0f99bf0 76static BOOL ExecAndWait(char *pszCommand)
5039dede
AK
77{
78 BOOL bSuccess = TRUE;
79
80#ifdef _WIN32
e0f99bf0 81 STARTUPINFOA si;
5039dede
AK
82 PROCESS_INFORMATION pi;
83
84 // Fill in process startup info structure
977e7d9d
VK
85 memset(&si, 0, sizeof(STARTUPINFOA));
86 si.cb = sizeof(STARTUPINFOA);
5039dede
AK
87 si.dwFlags = 0;
88
89 // Create new process
e0f99bf0
VK
90 if (!CreateProcessA(NULL, pszCommand, NULL, NULL, FALSE,
91 IsStandalone() ? 0 : CREATE_NO_WINDOW | DETACHED_PROCESS,
92 NULL, NULL, &si, &pi))
5039dede
AK
93 {
94 bSuccess = FALSE;
95 }
96 else
97 {
98 WaitForSingleObject(pi.hProcess, INFINITE);
99
100 // Close all handles
101 CloseHandle(pi.hThread);
102 CloseHandle(pi.hProcess);
103 }
104#else
105 bSuccess = (system(pszCommand) != -1);
106#endif
107
108 return bSuccess;
109}
110
111
112//
113// Create minidump of given process
114//
115
116#ifdef _WIN32
117
118static void CreateMiniDump(DWORD pid)
119{
120 HANDLE hFile, hProcess;
121 TCHAR error[256];
122
e0f99bf0 123 _tprintf(_T("INFO: Starting minidump for process %d\n"), pid);
5039dede
AK
124 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
125 if (hProcess != NULL)
126 {
127 hFile = CreateFile(_T("C:\\netxmsd.mdmp"), GENERIC_WRITE, 0, NULL,
128 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
129 if (hFile != INVALID_HANDLE_VALUE)
130 {
131 MiniDumpWriteDump(hProcess, pid, hFile, MiniDumpNormal, NULL, NULL, NULL);
132 CloseHandle(hFile);
e0f99bf0 133 _tprintf(_T("INFO: Minidump created successfully\n"));
5039dede
AK
134 }
135 else
136 {
e0f99bf0 137 _tprintf(_T("ERROR: cannot create file for minidump (%s)\n"), GetSystemErrorText(GetLastError(), error, 256));
5039dede
AK
138 }
139 }
140 else
141 {
e0f99bf0 142 _tprintf(_T("ERROR: cannot open process %d (%s)\n"), pid, GetSystemErrorText(GetLastError(), error, 256));
5039dede
AK
143 }
144}
145
146#endif
147
148
149//
150// Parse command line
151// Returns TRUE on success and FALSE on failure
152//
153
154#ifdef _WIN32
8782d780 155#define VALID_OPTIONS "c:CdD:qehIL:P:RsSv"
5039dede 156#else
8782d780 157#define VALID_OPTIONS "c:CdD:qehp:v"
5039dede
AK
158#endif
159
160static BOOL ParseCommandLine(int argc, char *argv[])
161{
162 int ch;
e0f99bf0 163 char *eptr;
5039dede 164#ifdef _WIN32
e0f99bf0
VK
165 TCHAR login[256] = _T(""), password[256] = _T("");
166 char exePath[MAX_PATH], dllPath[MAX_PATH], *ptr;
5039dede
AK
167 BOOL useLogin = FALSE;
168#endif
169#if defined(_WIN32) || HAVE_DECL_GETOPT_LONG
170 static struct option longOptions[] =
171 {
0b8da006
AK
172 { (char *)"check-db", 0, NULL, 'e' },
173 { (char *)"config", 1, NULL, 'c' },
174 { (char *)"daemon", 0, NULL, 'd' },
175 { (char *)"debug", 1, NULL, 'D' },
176 { (char *)"help", 0, NULL, 'h' },
8782d780 177 { (char *)"quiet", 1, NULL, 'q' },
5039dede 178#ifdef _WIN32
0b8da006
AK
179 { (char *)"check-service", 0, NULL, '!' },
180 { (char *)"dump", 1, NULL, '~' },
181 { (char *)"install", 0, NULL, 'I' },
182 { (char *)"login", 1, NULL, 'L' },
183 { (char *)"password", 1, NULL, 'P' },
184 { (char *)"remove", 0, NULL, 'R' },
185 { (char *)"start", 0, NULL, 's' },
186 { (char *)"stop", 0, NULL, 'S' },
5039dede 187#else
0b8da006 188 { (char *)"pid-file", 1, NULL, 'p' },
5039dede
AK
189#endif
190 { NULL, 0, 0, 0 }
191 };
192#endif
193
194#if defined(_WIN32) || HAVE_DECL_GETOPT_LONG
195 while((ch = getopt_long(argc, argv, VALID_OPTIONS, longOptions, NULL)) != -1)
196#else
197 while((ch = getopt(argc, argv, VALID_OPTIONS)) != -1)
198#endif
199 {
200 switch(ch)
201 {
202 case 'h':
e0f99bf0 203 _tprintf(help_text);
5039dede
AK
204 return FALSE;
205 case 'v':
e0f99bf0 206 _tprintf(_T("NetXMS Server Version ") NETXMS_VERSION_STRING _T(" Build of %hs\n"), __DATE__);
5039dede
AK
207 return FALSE;
208 case 'c':
e0f99bf0
VK
209#ifdef UNICODE
210 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szConfigFile, MAX_PATH);
211 g_szConfigFile[MAX_PATH - 1] = 0;
212#else
5039dede 213 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
e0f99bf0 214#endif
5039dede
AK
215 break;
216 case 'C': // Check config
217 g_dwFlags &= ~AF_DAEMON;
e0f99bf0 218 _tprintf(_T("Checking configuration file (%s):\n\n"), g_szConfigFile);
5039dede
AK
219 LoadConfig();
220 return FALSE;
221 case 'd':
222 g_dwFlags |= AF_DAEMON;
223 break;
224 case 'D': // Debug level
225 g_nDebugLevel = strtol(optarg, &eptr, 0);
226 if ((*eptr != 0) || (g_nDebugLevel < 0) || (g_nDebugLevel > 9))
227 {
e0f99bf0 228 _tprintf(_T("Invalid debug level \"%hs\" - should be in range 0..9\n"), optarg);
5039dede
AK
229 g_nDebugLevel = 0;
230 }
231 break;
8782d780
AK
232 case 'q': // disable interactive console
233 g_bDisableInput = TRUE;
5039dede
AK
234 case 'e':
235 g_bCheckDB = TRUE;
236 break;
237#ifdef _WIN32
238 case 'L':
e0f99bf0
VK
239#ifdef UNICODE
240 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, login, 256);
241 login[255] = 0;
242#else
5039dede 243 nx_strncpy(login, optarg, 256);
e0f99bf0 244#endif
5039dede
AK
245 useLogin = TRUE;
246 break;
247 case 'P':
e0f99bf0
VK
248#ifdef UNICODE
249 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, password, 256);
250 password[255] = 0;
251#else
5039dede 252 nx_strncpy(password, optarg, 256);
e0f99bf0 253#endif
5039dede
AK
254 break;
255 case 'I': // Install service
256 ptr = strrchr(argv[0], '\\');
257 if (ptr != NULL)
258 ptr++;
259 else
260 ptr = argv[0];
261
262 _fullpath(exePath, ptr, 255);
263
264 if (stricmp(&exePath[strlen(exePath)-4], ".exe"))
265 strcat(exePath, ".exe");
266 strcpy(dllPath, exePath);
267 ptr = strrchr(dllPath, '\\');
268 if (ptr != NULL) // Shouldn't be NULL
269 {
270 ptr++;
271 strcpy(ptr, "libnxsrv.dll");
272 }
e0f99bf0
VK
273#ifdef UNICODE
274 WCHAR wexePath[MAX_PATH], wdllPath[MAX_PATH];
275 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, exePath, -1, wexePath, MAX_PATH);
276 wexePath[MAX_PATH - 1] = 0;
277 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, dllPath, -1, wdllPath, MAX_PATH);
278 wdllPath[MAX_PATH - 1] = 0;
279 InstallService(wexePath, wdllPath, useLogin ? login : NULL, useLogin ? password : NULL);
280#else
5039dede 281 InstallService(exePath, dllPath, useLogin ? login : NULL, useLogin ? password : NULL);
e0f99bf0 282#endif
5039dede
AK
283 return FALSE;
284 case 'R': // Remove service
285 RemoveService();
286 return FALSE;
287 case 's': // Start service
288 StartCoreService();
289 return FALSE;
290 case 'S': // Stop service
291 StopCoreService();
292 return FALSE;
293 case '!': // Check service configuration (for migration from pre-0.2.20)
294 CheckServiceConfig();
295 return FALSE;
296 case '~':
e0f99bf0 297 CreateMiniDump(strtoul(optarg, NULL, 0));
5039dede
AK
298 return FALSE;
299#endif
300 default:
301 break;
302 }
303 }
304 return TRUE;
305}
306
307
308//
309// Startup code
310//
311
312int main(int argc, char *argv[])
313{
314#ifdef _WIN32
315 HKEY hKey;
316 DWORD dwSize;
317#else
318 int i;
319 FILE *fp;
c601fc15 320 TCHAR *pszEnv;
5039dede
AK
321#endif
322
44cdd527
VK
323#ifdef __sun
324 signal(SIGPIPE, SIG_IGN);
325 signal(SIGHUP, SIG_IGN);
326 signal(SIGINT, SIG_IGN);
327 signal(SIGQUIT, SIG_IGN);
328 signal(SIGUSR1, SIG_IGN);
329 signal(SIGUSR2, SIG_IGN);
330#endif
331
5039dede
AK
332 InitThreadLibrary();
333
334#ifdef NETXMS_MEMORY_DEBUG
335 InitMemoryDebugger();
336#endif
337
338 // Check for alternate config file location
339#ifdef _WIN32
42acd918 340 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Server"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
5039dede
AK
341 {
342 dwSize = MAX_PATH * sizeof(TCHAR);
343 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)g_szConfigFile, &dwSize);
344 RegCloseKey(hKey);
345 }
346#else
82d4fb3d 347 pszEnv = _tgetenv(_T("NETXMSD_CONFIG"));
5039dede
AK
348 if (pszEnv != NULL)
349 nx_strncpy(g_szConfigFile, pszEnv, MAX_PATH);
350#endif
351
352 if (!ParseCommandLine(argc, argv))
353 return 1;
354
355 if (!LoadConfig())
356 {
357 if (IsStandalone())
e0f99bf0 358 _tprintf(_T("Error loading configuration file\n"));
5039dede
AK
359 return 1;
360 }
361
362 // Set exception handler
363#ifdef _WIN32
364 if (g_dwFlags & AF_CATCH_EXCEPTIONS)
4262c0dc 365 SetExceptionHandler(SEHServiceExceptionHandler, SEHServiceExceptionDataWriter,
e0f99bf0 366 g_szDumpDir, _T("netxmsd"), MSG_EXCEPTION, g_dwFlags & AF_WRITE_FULL_DUMP, IsStandalone());
5039dede
AK
367 __try {
368#endif
369
712dd47d
VK
370 // Fix path to Java VM
371#ifdef _WIN32
372 if (!_tcscmp(g_szJavaPath, _T("java")))
373 {
374 TCHAR path[MAX_PATH];
375
376 if (GetModuleFileName(NULL, path, MAX_PATH) != 0)
377 {
378 TCHAR *p = _tcsrchr(path, _T('\\'));
379 if (p != NULL)
380 {
381 p--;
382 p = _tcsrchr(p, _T('\\'));
383 if (p != NULL)
384 {
385 p++;
386 _tcscpy(p, _T("jre\\bin\\java.exe"));
387 if (_taccess(path, 4))
388 {
389 _tcscpy(g_szJavaPath, path);
390 }
391 }
392 }
393 }
394 }
395#endif
396
5039dede
AK
397 // Check database before start if requested
398 if (g_bCheckDB)
399 {
400 char szCmd[MAX_PATH + 128], *pszSep;
401
402 strncpy(szCmd, argv[0], MAX_PATH);
403 pszSep = strrchr(szCmd, FS_PATH_SEPARATOR[0]);
404 if (pszSep != NULL)
405 pszSep++;
406 else
407 pszSep = szCmd;
e0f99bf0
VK
408#ifdef UNICODE
409 snprintf(pszSep, 128, "nxdbmgr -c \"%S\" -f check", g_szConfigFile);
410#else
411 snprintf(pszSep, 128, "nxdbmgr -c \"%s\" -f check", g_szConfigFile);
412#endif
5039dede
AK
413 if (!ExecAndWait(szCmd))
414 if (IsStandalone())
e0f99bf0 415 _tprintf(_T("ERROR: Failed to execute command \"%hs\"\n"), szCmd);
5039dede
AK
416 }
417
418#ifdef _WIN32
419 if (!IsStandalone())
420 {
421 InitService();
422 }
423 else
424 {
425 if (!Initialize())
426 {
e0f99bf0 427 _tprintf(_T("NetXMS Core initialization failed\n"));
5039dede
AK
428
429 // Remove database lock
430 if (g_dwFlags & AF_DB_LOCKED)
431 {
432 UnlockDB();
433 ShutdownDB();
434 }
435 return 3;
436 }
437 Main(NULL);
438 }
439#else /* not _WIN32 */
440 if (!IsStandalone())
441 {
442 if (daemon(0, 0) == -1)
443 {
444 perror("Call to daemon() failed");
445 return 2;
446 }
447 }
448
449 // Write PID file
e0f99bf0 450 fp = _tfopen(g_szPIDFile, _T("w"));
5039dede
AK
451 if (fp != NULL)
452 {
e0f99bf0 453 _ftprintf(fp, _T("%d"), getpid());
5039dede
AK
454 fclose(fp);
455 }
456
457 // Initialize server
458 if (!Initialize())
459 {
460 // Remove database lock
461 if (g_dwFlags & AF_DB_LOCKED)
462 {
463 UnlockDB();
464 ShutdownDB();
465 }
466 return 3;
467 }
468
469 // Everything is OK, start common main loop
470 StartMainLoop(SignalHandler, Main);
471#endif /* _WIN32 */
472
473#ifdef _WIN32
474 LIBNETXMS_EXCEPTION_HANDLER
475#endif
476 return 0;
477}