fixed build error on Linux
[public/netxms.git] / src / agent / core / nxagentd.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS multiplatform core agent
1621a079 3** Copyright (C) 2003-2011 Victor Kirhenshtein
5039dede
AK
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**
f0c1d2a4
AK
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
5039dede
AK
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** File: nxagentd.cpp
20**
21**/
22
23#include "nxagentd.h"
b34606f7 24#include <nxdbapi.h>
5039dede
AK
25
26#if defined(_WIN32)
27#include <conio.h>
28#include <locale.h>
29#elif defined(_NETWARE)
30#include <screen.h>
31#include <library.h>
32#else
33#include <signal.h>
34#include <sys/wait.h>
35#endif
36
37#if HAVE_SYS_UTSNAME_H
38#include <sys/utsname.h>
39#endif
40
41#if HAVE_SYS_SYSCTL_H
42#include <sys/sysctl.h>
43#endif
44
45
46//
47// Externals
48//
49
50THREAD_RESULT THREAD_CALL ListenerThread(void *);
51THREAD_RESULT THREAD_CALL SessionWatchdog(void *);
52THREAD_RESULT THREAD_CALL TrapSender(void *);
53
54void ShutdownTrapSender();
55
9fcdfda7
VK
56void StartWatchdog();
57void StopWatchdog();
58int WatchdogMain(DWORD pid);
59
6aab2983
AK
60void InitSessionList();
61
f480bdd4
VK
62BOOL PushData(const TCHAR *parameter, const TCHAR *value);
63
5039dede 64#if !defined(_WIN32) && !defined(_NETWARE)
203e9d8a 65void InitStaticSubagents();
5039dede
AK
66#endif
67
9f96fc88
VK
68#ifdef _WIN32
69extern TCHAR g_windowsServiceName[];
70extern TCHAR g_windowsServiceDisplayName[];
71#endif
72
5039dede
AK
73
74//
75// Messages generated by mc.pl (for UNIX version only)
76//
77
78#ifndef _WIN32
79extern unsigned int g_dwNumMessages;
80extern const TCHAR *g_szMessages[];
81#endif
82
83
84//
85// Valid options for getopt()
86//
87
88#if defined(_WIN32)
175bd003 89#define VALID_OPTIONS "c:CdD:e:EfhHiIM:n:N:P:r:RsSUvX:W:Z:"
5039dede 90#elif defined(_NETWARE)
c303351c 91#define VALID_OPTIONS "c:CD:fhM:P:r:vZ:"
5039dede 92#else
c303351c 93#define VALID_OPTIONS "c:CdD:fhM:p:P:r:vX:W:Z:"
5039dede
AK
94#endif
95
96
97//
98// Actions
99//
100
101#define ACTION_NONE 0
102#define ACTION_RUN_AGENT 1
103#define ACTION_INSTALL_SERVICE 2
104#define ACTION_REMOVE_SERVICE 3
105#define ACTION_START_SERVICE 4
106#define ACTION_STOP_SERVICE 5
107#define ACTION_CHECK_CONFIG 6
108#define ACTION_INSTALL_EVENT_SOURCE 7
109#define ACTION_REMOVE_EVENT_SOURCE 8
110#define ACTION_CREATE_CONFIG 9
111#define ACTION_HELP 10
9fcdfda7 112#define ACTION_RUN_WATCHDOG 11
5039dede
AK
113
114
115//
116// Global variables
117//
118
119DWORD g_dwFlags = AF_ENABLE_ACTIONS | AF_ENABLE_AUTOLOAD;
bf3b7f79
VK
120TCHAR g_szLogFile[MAX_PATH] = AGENT_DEFAULT_LOG;
121TCHAR g_szSharedSecret[MAX_SECRET_LENGTH] = _T("admin");
122TCHAR g_szConfigFile[MAX_PATH] = AGENT_DEFAULT_CONFIG;
123TCHAR g_szFileStore[MAX_PATH] = AGENT_DEFAULT_FILE_STORE;
124TCHAR g_szDataDirectory[MAX_PATH] = AGENT_DEFAULT_DATA_DIR;
125TCHAR g_szPlatformSuffix[MAX_PSUFFIX_LENGTH] = _T("");
126TCHAR g_szConfigServer[MAX_DB_STRING] = _T("not_set");
127TCHAR g_szRegistrar[MAX_DB_STRING] = _T("not_set");
128TCHAR g_szListenAddress[MAX_PATH] = _T("*");
6ca3b41c 129TCHAR g_szConfigIncludeDir[MAX_PATH] = AGENT_DEFAULT_CONFIG_D;
5039dede
AK
130WORD g_wListenPort = AGENT_LISTEN_PORT;
131SERVER_INFO g_pServerList[MAX_SERVERS];
132DWORD g_dwServerCount = 0;
133DWORD g_dwExecTimeout = 2000; // External process execution timeout in milliseconds
134DWORD g_dwSNMPTimeout = 3000;
135time_t g_tmAgentStartTime;
136DWORD g_dwStartupDelay = 0;
137DWORD g_dwMaxSessions = 32;
15e11d1b 138DWORD g_debugLevel = 0;
e6c91aac 139Config *g_config;
5039dede
AK
140#ifdef _WIN32
141DWORD g_dwIdleTimeout = 60; // Session idle timeout
142#else
143DWORD g_dwIdleTimeout = 120; // Session idle timeout
144#endif
145
146#if !defined(_WIN32) && !defined(_NETWARE)
bf3b7f79 147TCHAR g_szPidFile[MAX_PATH] = _T("/var/run/nxagentd.pid");
5039dede
AK
148#endif
149
150#ifdef _WIN32
5039dede
AK
151DWORD (__stdcall *imp_HrLanConnectionNameFromGuidOrPath)(LPWSTR, LPWSTR, LPWSTR, LPDWORD);
152#endif /* _WIN32 */
153
154#ifdef _NETWARE
155int g_nThreadCount = 0;
156#endif
157
158
159//
160// Static variables
161//
162
bf3b7f79
VK
163static TCHAR *m_pszActionList = NULL;
164static TCHAR *m_pszShellActionList = NULL;
165static TCHAR *m_pszServerList = NULL;
166static TCHAR *m_pszControlServerList = NULL;
167static TCHAR *m_pszMasterServerList = NULL;
168static TCHAR *m_pszSubagentList = NULL;
169static TCHAR *m_pszExtParamList = NULL;
170static TCHAR *m_pszShExtParamList = NULL;
9795113c 171static TCHAR *m_pszParamProviderList = NULL;
204ace22 172static TCHAR *m_pszExtSubagentList = NULL;
5039dede
AK
173static DWORD m_dwEnabledCiphers = 0xFFFF;
174static THREAD m_thSessionWatchdog = INVALID_THREAD_HANDLE;
175static THREAD m_thListener = INVALID_THREAD_HANDLE;
176static THREAD m_thTrapSender = INVALID_THREAD_HANDLE;
bf3b7f79
VK
177static TCHAR m_szProcessToWait[MAX_PATH] = _T("");
178static TCHAR m_szDumpDir[MAX_PATH] = _T("C:\\");
4addc3a3
VK
179static DWORD m_dwMaxLogSize = 16384 * 1024;
180static DWORD m_dwLogHistorySize = 4;
203e9d8a 181static Config *s_registry = NULL;
5039dede
AK
182
183#if defined(_WIN32) || defined(_NETWARE)
184static CONDITION m_hCondShutdown = INVALID_CONDITION_HANDLE;
185#endif
186
187#if !defined(_WIN32) && !defined(_NETWARE)
188static pid_t m_pid;
189#endif
190
191
192//
193// Configuration file template
194//
195
196static NX_CFG_TEMPLATE m_cfgTemplate[] =
197{
198 { "Action", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszActionList },
199 { "ActionShellExec", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszShellActionList },
200 { "ControlServers", CT_STRING_LIST, ',', 0, 0, 0, &m_pszControlServerList },
9fcdfda7 201 { "CreateCrashDumps", CT_BOOLEAN, 0, 0, AF_CATCH_EXCEPTIONS, 0, &g_dwFlags },
203e9d8a 202 { "DataDirectory", CT_STRING, 0, 0, MAX_PATH, 0, g_szDataDirectory },
15e11d1b 203 { "DebugLevel", CT_LONG, 0, 0, 0, 0, &g_debugLevel },
9fcdfda7 204 { "DumpDirectory", CT_STRING, 0, 0, MAX_PATH, 0, m_szDumpDir },
5039dede 205 { "EnableActions", CT_BOOLEAN, 0, 0, AF_ENABLE_ACTIONS, 0, &g_dwFlags },
1a5e0c22 206 { "EnableArbitraryFileUpload", CT_BOOLEAN, 0, 0, AF_ARBITRARY_FILE_UPLOAD, 0, &g_dwFlags },
5039dede
AK
207 { "EnabledCiphers", CT_LONG, 0, 0, 0, 0, &m_dwEnabledCiphers },
208 { "EnableProxy", CT_BOOLEAN, 0, 0, AF_ENABLE_PROXY, 0, &g_dwFlags },
209 { "EnableSNMPProxy", CT_BOOLEAN, 0, 0, AF_ENABLE_SNMP_PROXY, 0, &g_dwFlags },
210 { "EnableSubagentAutoload", CT_BOOLEAN, 0, 0, AF_ENABLE_AUTOLOAD, 0, &g_dwFlags },
9fcdfda7 211 { "EnableWatchdog", CT_BOOLEAN, 0, 0, AF_ENABLE_WATCHDOG, 0, &g_dwFlags },
5039dede
AK
212 { "ExecTimeout", CT_LONG, 0, 0, 0, 0, &g_dwExecTimeout },
213 { "ExternalParameter", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszExtParamList },
214 { "ExternalParameterShellExec", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszShExtParamList },
9795113c 215 { "ExternalParametersProvider", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszParamProviderList },
204ace22 216 { "ExternalSubagent", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszExtSubagentList },
5039dede 217 { "FileStore", CT_STRING, 0, 0, MAX_PATH, 0, g_szFileStore },
cbc777ee 218 { "FullCrashDumps", CT_BOOLEAN, 0, 0, AF_WRITE_FULL_DUMP, 0, &g_dwFlags },
5039dede
AK
219 { "ListenAddress", CT_STRING, 0, 0, MAX_PATH, 0, g_szListenAddress },
220 { "ListenPort", CT_WORD, 0, 0, 0, 0, &g_wListenPort },
221 { "LogFile", CT_STRING, 0, 0, MAX_PATH, 0, g_szLogFile },
4addc3a3 222 { "LogHistorySize", CT_LONG, 0, 0, 0, 0, &m_dwLogHistorySize },
5039dede
AK
223 { "LogUnresolvedSymbols", CT_BOOLEAN, 0, 0, AF_LOG_UNRESOLVED_SYMBOLS, 0, &g_dwFlags },
224 { "MasterServers", CT_STRING_LIST, ',', 0, 0, 0, &m_pszMasterServerList },
4addc3a3 225 { "MaxLogSize", CT_LONG, 0, 0, 0, 0, &m_dwMaxLogSize },
5039dede
AK
226 { "MaxSessions", CT_LONG, 0, 0, 0, 0, &g_dwMaxSessions },
227 { "PlatformSuffix", CT_STRING, 0, 0, MAX_PSUFFIX_LENGTH, 0, g_szPlatformSuffix },
228 { "RequireAuthentication", CT_BOOLEAN, 0, 0, AF_REQUIRE_AUTH, 0, &g_dwFlags },
229 { "RequireEncryption", CT_BOOLEAN, 0, 0, AF_REQUIRE_ENCRYPTION, 0, &g_dwFlags },
230 { "Servers", CT_STRING_LIST, ',', 0, 0, 0, &m_pszServerList },
231 { "SessionIdleTimeout", CT_LONG, 0, 0, 0, 0, &g_dwIdleTimeout },
232 { "SharedSecret", CT_STRING, 0, 0, MAX_SECRET_LENGTH, 0, g_szSharedSecret },
233 { "StartupDelay", CT_LONG, 0, 0, 0, 0, &g_dwStartupDelay },
234 { "SubAgent", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszSubagentList },
235 { "TimeOut", CT_IGNORE, 0, 0, 0, 0, NULL },
236 { "WaitForProcess", CT_STRING, 0, 0, MAX_PATH, 0, m_szProcessToWait },
237 { "", CT_END_OF_LIST, 0, 0, 0, 0, NULL }
238};
239
240
241//
242// Help text
243//
244
245static char m_szHelpText[] =
246 "Usage: nxagentd [options]\n"
247 "Where valid options are:\n"
248 " -c <file> : Use configuration file <file> (default " AGENT_DEFAULT_CONFIG ")\n"
249 " -C : Check configuration file and exit\n"
250#ifndef _NETWARE
251 " -d : Run as daemon/service\n"
252#endif
c303351c 253 " -D <level> : Set debug level (0..9)\n"
9f96fc88
VK
254#ifdef _WIN32
255 " -e <name> : Windows event source name\n"
256#endif
5039dede
AK
257 " -f : Run in foreground\n"
258 " -h : Display help and exit\n"
259#ifdef _WIN32
260 " -H : Hide agent's window when in standalone mode\n"
175bd003 261 " -i : Installed Windows service must be interactive\n"
5039dede
AK
262 " -I : Install Windows service\n"
263#endif
264 " -M <addr> : Download config from management server <addr>\n"
9f96fc88
VK
265#ifdef _WIN32
266 " -n <name> : Service name\n"
267 " -N <name> : Service display name\n"
268#endif
5039dede
AK
269#if !defined(_WIN32) && !defined(_NETWARE)
270 " -p : Path to pid file (default: /var/run/nxagentd.pid)\n"
271#endif
272 " -P <text> : Set platform suffix to <text>\n"
273 " -r <addr> : Register agent on management server <addr>\n"
274#ifdef _WIN32
275 " -R : Remove Windows service\n"
276 " -s : Start Windows servive\n"
277 " -S : Stop Windows service\n"
278#endif
279 " -v : Display version and exit\n"
280 "\n";
281
282
203e9d8a
VK
283//
284// Save registry
285//
286
287static void SaveRegistry()
288{
289 TCHAR regPath[MAX_PATH];
290 nx_strncpy(regPath, g_szDataDirectory, MAX_PATH - _tcslen(REGISTRY_FILE_NAME) - 1);
291 if (regPath[_tcslen(regPath) - 1] != FS_PATH_SEPARATOR_CHAR)
292 _tcscat(regPath, FS_PATH_SEPARATOR);
293 _tcscat(regPath, REGISTRY_FILE_NAME);
294
295 String xml = s_registry->createXml();
296 FILE *f = _tfopen(regPath, _T("w"));
297 if (f != NULL)
298 {
299 _fputts((const TCHAR *)xml, f);
300 fclose(f);
301 }
302 else
303 {
304 nxlog_write(MSG_REGISTRY_SAVE_FAILED, NXLOG_ERROR, "ss", regPath, strerror(errno));
305 }
306}
307
308
309//
310// Open registry
311//
312
313Config *OpenRegistry()
314{
315 s_registry->lock();
316 return s_registry;
317}
318
319
320//
321// Close registry
322//
323
324void CloseRegistry(bool modified)
325{
326 if (modified)
327 SaveRegistry();
328 s_registry->unlock();
329}
330
331
5039dede
AK
332#ifdef _WIN32
333
334//
335// Get our own console window handle (an alternative to Microsoft's GetConsoleWindow)
336//
337
203e9d8a 338static HWND GetConsoleHWND()
5039dede
AK
339{
340 HWND hWnd;
341 DWORD wpid, cpid;
342
343 cpid = GetCurrentProcessId();
344 while(1)
345 {
346 hWnd = FindWindowEx(NULL, NULL, _T("ConsoleWindowClass"), NULL);
347 if (hWnd == NULL)
348 break;
349
350 GetWindowThreadProcessId(hWnd, &wpid);
351 if (cpid == wpid)
352 break;
353 }
354
355 return hWnd;
356}
357
358
359//
360// Get proc address and write log file
361//
362
363static FARPROC GetProcAddressAndLog(HMODULE hModule, LPCSTR procName)
364{
365 FARPROC ptr;
366
367 ptr = GetProcAddress(hModule, procName);
368 if ((ptr == NULL) && (g_dwFlags & AF_LOG_UNRESOLVED_SYMBOLS))
369 nxlog_write(MSG_NO_FUNCTION, EVENTLOG_WARNING_TYPE, "s", procName);
370 return ptr;
371}
372
373
374//
375// Import symbols
376//
377
203e9d8a 378static void ImportSymbols()
5039dede
AK
379{
380 HMODULE hModule;
381
5039dede 382 // NETMAN.DLL
bf3b7f79 383 hModule = LoadLibrary(_T("NETMAN.DLL"));
5039dede
AK
384 if (hModule != NULL)
385 {
386 imp_HrLanConnectionNameFromGuidOrPath =
387 (DWORD (__stdcall *)(LPWSTR, LPWSTR, LPWSTR, LPDWORD))GetProcAddressAndLog(hModule,
388 "HrLanConnectionNameFromGuidOrPath");
389 }
390 else
391 {
392 nxlog_write(MSG_NO_DLL, EVENTLOG_WARNING_TYPE, "s", "NETMAN.DLL");
393 }
394}
395
396
397//
398// Shutdown thread (created by H_RestartAgent)
399//
400
401static THREAD_RESULT THREAD_CALL ShutdownThread(void *pArg)
402{
8e954797 403 DebugPrintf(INVALID_INDEX, 1, _T("Shutdown thread started"));
5039dede
AK
404 Shutdown();
405 ExitProcess(0);
406 return THREAD_OK; // Never reached
407}
408
409#endif /* _WIN32 */
410
411
412//
413// Restart agent
414//
415
6173bea8 416static LONG H_RestartAgent(const TCHAR *action, StringList *args, const TCHAR *data)
5039dede 417{
5b2c3e2d
VK
418 DebugPrintf(INVALID_INDEX, 1, _T("H_RestartAgent() called"));
419
5039dede
AK
420#ifdef _NETWARE
421 return ERR_NOT_IMPLEMENTED;
422#else
423 TCHAR szCmdLine[4096], szPlatformSuffixOption[MAX_PSUFFIX_LENGTH + 16];
424#ifdef _WIN32
425 TCHAR szExecName[MAX_PATH];
426 DWORD dwResult;
427 STARTUPINFO si;
428 PROCESS_INFORMATION pi;
429
430 GetModuleFileName(GetModuleHandle(NULL), szExecName, MAX_PATH);
431#else
432 TCHAR szExecName[MAX_PATH] = PREFIX _T("/bin/nxagentd");
433#endif
434
435 if (g_szPlatformSuffix[0] != 0)
436 {
437 _sntprintf(szPlatformSuffixOption, MAX_PSUFFIX_LENGTH + 16, _T("-P \"%s\" "), g_szPlatformSuffix);
438 }
439 else
440 {
441 szPlatformSuffixOption[0] = 0;
442 }
443
444#ifdef _WIN32
c303351c 445 _sntprintf(szCmdLine, 4096, _T("\"%s\" -c \"%s\" -n \"%s\" -e \"%s\" %s%s%s%s%s-D %d %s-X %u"), szExecName,
93599cfd
VK
446 g_szConfigFile, g_windowsServiceName, g_windowsEventSourceName,
447 (g_dwFlags & AF_DAEMON) ? _T("-d ") : _T(""),
5039dede
AK
448 (g_dwFlags & AF_HIDE_WINDOW) ? _T("-H ") : _T(""),
449 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T("-M ") : _T(""),
450 (g_dwFlags & AF_CENTRAL_CONFIG) ? g_szConfigServer : _T(""),
451 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T(" ") : _T(""),
c303351c 452 g_debugLevel, szPlatformSuffixOption,
5039dede 453 (g_dwFlags & AF_DAEMON) ? 0 : GetCurrentProcessId());
c303351c 454 DebugPrintf(INVALID_INDEX, 1, _T("Restarting agent with command line '%s'"), szCmdLine);
5039dede 455
5039dede
AK
456 // Fill in process startup info structure
457 memset(&si, 0, sizeof(STARTUPINFO));
458 si.cb = sizeof(STARTUPINFO);
459
460 // Create new process
461 if (!CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE,
462 (g_dwFlags & AF_DAEMON) ? (CREATE_NO_WINDOW | DETACHED_PROCESS) : (CREATE_NEW_CONSOLE),
463 NULL, NULL, &si, &pi))
464 {
465 nxlog_write(MSG_CREATE_PROCESS_FAILED, EVENTLOG_ERROR_TYPE, "se", szCmdLine, GetLastError());
466 dwResult = ERR_EXEC_FAILED;
467 }
468 else
469 {
470 // Close all handles
471 CloseHandle(pi.hThread);
472 CloseHandle(pi.hProcess);
473 dwResult = ERR_SUCCESS;
474 }
475 if ((dwResult == ERR_SUCCESS) && (!(g_dwFlags & AF_DAEMON)))
476 {
477 if (g_dwFlags & AF_HIDE_WINDOW)
478 {
479 ConditionSet(m_hCondShutdown);
480 }
481 else
482 {
483 ThreadCreate(ShutdownThread, 0, NULL);
484 }
485 }
486 return dwResult;
487#else
c303351c 488 _sntprintf(szCmdLine, 4096, _T("\"%s\" -c \"%s\" %s%s%s%s-D %d %s-X %lu"), szExecName,
5039dede
AK
489 g_szConfigFile, (g_dwFlags & AF_DAEMON) ? _T("-d ") : _T(""),
490 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T("-M ") : _T(""),
491 (g_dwFlags & AF_CENTRAL_CONFIG) ? g_szConfigServer : _T(""),
492 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T(" ") : _T(""),
53c96e2d 493 (int)g_debugLevel, szPlatformSuffixOption,
5039dede 494 (unsigned long)m_pid);
5b2c3e2d 495 DebugPrintf(INVALID_INDEX, 1, _T("Restarting agent with command line '%s'"), szCmdLine);
9fcdfda7 496 return ExecuteCommand(szCmdLine, NULL, NULL);
5039dede
AK
497#endif
498#endif /* _NETWARE */
499}
500
501
502//
503// This function writes message from subagent to agent's log
504//
505
c303351c 506static void WriteSubAgentMsg(int logLevel, int debugLevel, const TCHAR *pszMsg)
5039dede 507{
c303351c 508 if (logLevel == EVENTLOG_DEBUG_TYPE)
5039dede 509 {
15e11d1b 510 if (debugLevel <= (int)g_debugLevel)
c303351c 511 nxlog_write(MSG_DEBUG, EVENTLOG_DEBUG_TYPE, "s", pszMsg);
5039dede
AK
512 }
513 else
514 {
c303351c 515 nxlog_write(MSG_SUBAGENT_MSG, logLevel, "s", pszMsg);
5039dede
AK
516 }
517}
518
519
520//
521// Signal handler for UNIX platforms
522//
523
524#if !defined(_WIN32) && !defined(_NETWARE)
525
526static THREAD_RESULT THREAD_CALL SignalHandler(void *pArg)
527{
528 sigset_t signals;
529 int nSignal;
530
531 sigemptyset(&signals);
532 sigaddset(&signals, SIGTERM);
533 sigaddset(&signals, SIGINT);
534 sigaddset(&signals, SIGPIPE);
535 sigaddset(&signals, SIGSEGV);
536 sigaddset(&signals, SIGHUP);
537 sigaddset(&signals, SIGUSR1);
538 sigaddset(&signals, SIGUSR2);
539
540 sigprocmask(SIG_BLOCK, &signals, NULL);
541
542 while(1)
543 {
544 if (sigwait(&signals, &nSignal) == 0)
545 {
546 switch(nSignal)
547 {
548 case SIGTERM:
549 case SIGINT:
550 goto stop_handler;
551 case SIGSEGV:
552 abort();
553 break;
554 default:
555 break;
556 }
557 }
558 else
559 {
560 ThreadSleepMs(100);
561 }
562 }
563
564stop_handler:
565 sigprocmask(SIG_UNBLOCK, &signals, NULL);
566 return THREAD_OK;
567}
568
569#endif
570
571
572//
573// Load subagent for Windows NT or Windows 9x or platform subagent on UNIX
574//
575
576#ifdef _WIN32
577
203e9d8a 578void LoadWindowsSubagent()
5039dede
AK
579{
580 OSVERSIONINFO ver;
581
582 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
583 if (GetVersionEx(&ver))
584 {
585 switch(ver.dwPlatformId)
586 {
587 case VER_PLATFORM_WIN32_WINDOWS: // Windows 9x
588 LoadSubAgent("WIN9X.NSM");
589 break;
590 case VER_PLATFORM_WIN32_NT: // Windows NT or higher
591 LoadSubAgent("WINNT.NSM");
592 break;
593 default:
594 break;
595 }
596 }
597 else
598 {
599 nxlog_write(MSG_GETVERSION_FAILED, EVENTLOG_WARNING_TYPE, "e", GetLastError());
600 }
601}
602
603#else
604
203e9d8a 605void LoadPlatformSubagent()
5039dede
AK
606{
607#if defined(_NETWARE)
608 LoadSubAgent("NETWARE.NSM");
609#elif HAVE_SYS_UTSNAME_H && !defined(_STATIC_AGENT)
610 struct utsname un;
611 char szName[MAX_PATH];
612 int i;
613
614 if (uname(&un) != -1)
615 {
616 // Convert system name to lowercase
617 for(i = 0; un.sysname[i] != 0; i++)
618 un.sysname[i] = tolower(un.sysname[i]);
619 if (!strcmp(un.sysname, "hp-ux"))
620 strcpy(un.sysname, "hpux");
b0032c83 621 snprintf(szName, MAX_PATH, LIBDIR "/libnsm_%s" SHL_SUFFIX, un.sysname);
5039dede
AK
622 LoadSubAgent(szName);
623 }
624#endif
625}
626
627#endif
628
629
6173bea8
VK
630//
631// Send file to server (subagent API)
632//
633
634static BOOL SendFileToServer(void *session, DWORD requestId, const TCHAR *file, long offset)
635{
4685a2ad
VK
636 if (session == NULL)
637 return FALSE;
638
639 return ((CommSession *)session)->sendFile(requestId, file, offset);
6173bea8
VK
640}
641
642
b34606f7
VK
643//
644// Debug callback for DB library
645//
646
647static void DBLibraryDebugCallback(int level, const TCHAR *format, va_list args)
648{
15e11d1b 649 if (level <= (int)g_debugLevel)
b34606f7
VK
650 {
651 TCHAR buffer[4096];
652
653 _vsntprintf(buffer, 4096, format, args);
654 nxlog_write(MSG_DEBUG, EVENTLOG_DEBUG_TYPE, "s", buffer);
655 }
656}
657
658
5039dede
AK
659//
660// Initialization routine
661//
662
c6fbf758 663BOOL Initialize()
5039dede 664{
bf3b7f79
VK
665 TCHAR *pItem, *pEnd;
666 TCHAR regPath[MAX_PATH];
5039dede
AK
667#ifdef _NETWARE
668 char szLoadPath[1024], szSearchPath[1024];
669#endif
670
671 // Open log file
4addc3a3
VK
672 if (!(g_dwFlags & AF_USE_SYSLOG))
673 {
674 if (!nxlog_set_rotation_policy((int)m_dwMaxLogSize, (int)m_dwLogHistorySize))
675 if (!(g_dwFlags & AF_DAEMON))
bf3b7f79 676 _tprintf(_T("WARNING: cannot set log rotation policy; using default values\n"));
4addc3a3 677 }
9fcdfda7
VK
678 if (!nxlog_open((g_dwFlags & AF_USE_SYSLOG) ? NXAGENTD_SYSLOG_NAME : g_szLogFile,
679 ((g_dwFlags & AF_USE_SYSLOG) ? NXLOG_USE_SYSLOG : 0) |
680 ((g_dwFlags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
681 _T("NXAGENTD.EXE"),
5039dede 682#ifdef _WIN32
9fcdfda7 683 0, NULL))
5039dede 684#else
9fcdfda7 685 g_dwNumMessages, g_szMessages))
5039dede 686#endif
9fcdfda7 687 {
bf3b7f79 688 _ftprintf(stderr, _T("FATAL ERROR: Cannot open log file\n"));
9fcdfda7
VK
689 return FALSE;
690 }
203e9d8a
VK
691 nxlog_write(MSG_USE_CONFIG_D, NXLOG_INFO, "s", g_szConfigIncludeDir);
692 nxlog_write(MSG_DEBUG_LEVEL, NXLOG_INFO, "d", g_debugLevel);
693
694 // Initialize persistent storage
695 s_registry = new Config;
696 s_registry->setTopLevelTag(_T("registry"));
697 nx_strncpy(regPath, g_szDataDirectory, MAX_PATH - _tcslen(REGISTRY_FILE_NAME) - 1);
698 if (regPath[_tcslen(regPath) - 1] != FS_PATH_SEPARATOR_CHAR)
699 _tcscat(regPath, FS_PATH_SEPARATOR);
700 _tcscat(regPath, REGISTRY_FILE_NAME);
701 if (!s_registry->loadXmlConfig(regPath, "registry"))
702 {
703 nxlog_write(MSG_REGISTRY_LOAD_FAILED, NXLOG_ERROR, "s", regPath);
704 SaveRegistry();
705 }
5039dede
AK
706
707#ifdef _WIN32
708 WSADATA wsaData;
1ddf3f0c
VK
709 int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
710 if (wrc != 0)
5039dede 711 {
1ddf3f0c 712 nxlog_write(MSG_WSASTARTUP_FAILED, NXLOG_ERROR, "e", wrc);
5039dede
AK
713 return FALSE;
714 }
5039dede
AK
715#endif
716
717 // Add NLM load path to search list
718#ifdef _NETWARE
719 if (getnlmloadpath(szLoadPath) != NULL)
720 {
721 int i, nIsDOS;
722 BOOL bExist = FALSE;
723
724 for(i = 0; ; i++)
725 {
726 if (GetSearchPathElement(i, &nIsDOS, szSearchPath) != 0)
727 break;
728 if (strlen(szLoadPath) == szSearchPath[0])
729 if (!strncasecmp(&szSearchPath[1], szLoadPath, szSearchPath[0]))
730 {
731 bExist = TRUE;
732 break;
733 }
734 }
735 if (!bExist)
736 InsertSearchPath(getnetwarelogger(), 0, szLoadPath);
737 }
738#endif
739
6aab2983
AK
740 InitSessionList();
741
5039dede 742 // Initialize API for subagents
f480bdd4 743 InitSubAgentAPI(WriteSubAgentMsg, SendTrap, SendTrap, SendFileToServer, PushData);
bf3b7f79 744 DebugPrintf(INVALID_INDEX, 1, _T("Subagent API initialized"));
5039dede
AK
745
746 // Initialize cryptografy
747 if (!InitCryptoLib(m_dwEnabledCiphers))
748 {
749 nxlog_write(MSG_INIT_CRYPTO_FAILED, EVENTLOG_ERROR_TYPE, "e", WSAGetLastError());
750 return FALSE;
751 }
752
753 // Initialize built-in parameters
754 if (!InitParameterList())
755 return FALSE;
756
757#ifdef _WIN32
758 // Dynamically import functions that may not be presented in all Windows versions
759 ImportSymbols();
760#endif
761
762 // Parse server list
763 if (m_pszServerList != NULL)
764 {
e6c91aac 765 for(pItem = pEnd = m_pszServerList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
766 {
767 pEnd = strchr(pItem, ',');
768 if (pEnd != NULL)
769 *pEnd = 0;
770 StrStrip(pItem);
771 g_pServerList[g_dwServerCount].dwIpAddr = ResolveHostName(pItem);
772 if ((g_pServerList[g_dwServerCount].dwIpAddr == INADDR_NONE) ||
773 (g_pServerList[g_dwServerCount].dwIpAddr == INADDR_ANY))
774 {
775 if (!(g_dwFlags & AF_DAEMON))
776 printf("Invalid server address '%s'\n", pItem);
777 }
778 else
779 {
780 g_pServerList[g_dwServerCount].bMasterServer = FALSE;
781 g_pServerList[g_dwServerCount].bControlServer = FALSE;
782 g_dwServerCount++;
783 }
784 }
785 free(m_pszServerList);
786 }
787
788 // Parse master server list
789 if (m_pszMasterServerList != NULL)
790 {
791 DWORD i, dwAddr;
792
e6c91aac 793 for(pItem = pEnd = m_pszMasterServerList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
794 {
795 pEnd = strchr(pItem, ',');
796 if (pEnd != NULL)
797 *pEnd = 0;
798 StrStrip(pItem);
799
800 dwAddr = ResolveHostName(pItem);
801 if ((dwAddr == INADDR_NONE) ||
802 (dwAddr == INADDR_ANY))
803 {
804 if (!(g_dwFlags & AF_DAEMON))
bf3b7f79 805 _tprintf(_T("Invalid server address '%s'\n"), pItem);
5039dede
AK
806 }
807 else
808 {
809 for(i = 0; i < g_dwServerCount; i++)
810 if (g_pServerList[i].dwIpAddr == dwAddr)
811 break;
812
813 if (i == g_dwServerCount)
814 {
815 g_pServerList[g_dwServerCount].dwIpAddr = dwAddr;
816 g_pServerList[g_dwServerCount].bMasterServer = TRUE;
817 g_pServerList[g_dwServerCount].bControlServer = TRUE;
818 g_dwServerCount++;
819 }
820 else
821 {
822 g_pServerList[i].bMasterServer = TRUE;
823 g_pServerList[i].bControlServer = TRUE;
824 }
825 }
826 }
827 free(m_pszMasterServerList);
828 }
829
830 // Parse control server list
831 if (m_pszControlServerList != NULL)
832 {
833 DWORD i, dwAddr;
834
e6c91aac 835 for(pItem = pEnd = m_pszControlServerList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
836 {
837 pEnd = strchr(pItem, ',');
838 if (pEnd != NULL)
839 *pEnd = 0;
840 StrStrip(pItem);
841
842 dwAddr = ResolveHostName(pItem);
843 if ((dwAddr == INADDR_NONE) ||
844 (dwAddr == INADDR_ANY))
845 {
846 if (!(g_dwFlags & AF_DAEMON))
bf3b7f79 847 _tprintf(_T("Invalid server address '%s'\n"), pItem);
5039dede
AK
848 }
849 else
850 {
851 for(i = 0; i < g_dwServerCount; i++)
852 if (g_pServerList[i].dwIpAddr == dwAddr)
853 break;
854
855 if (i == g_dwServerCount)
856 {
857 g_pServerList[g_dwServerCount].dwIpAddr = dwAddr;
858 g_pServerList[g_dwServerCount].bMasterServer = FALSE;
859 g_pServerList[g_dwServerCount].bControlServer = TRUE;
860 g_dwServerCount++;
861 }
862 else
863 {
864 g_pServerList[i].bControlServer = TRUE;
865 }
866 }
867 }
868 free(m_pszControlServerList);
869 }
870
871 // Add built-in actions
872 AddAction("Agent.Restart", AGENT_ACTION_SUBAGENT, NULL, H_RestartAgent, "CORE", "Restart agent");
873
874 // Load platform subagents
875#if !defined(_WIN32) && !defined(_NETWARE)
876 InitStaticSubagents();
877#endif
878 if (g_dwFlags & AF_ENABLE_AUTOLOAD)
879 {
880#ifdef _WIN32
881 LoadWindowsSubagent();
882#else
883 LoadPlatformSubagent();
884#endif
885 }
886
887 // Wait for external process if requested
888 if (m_szProcessToWait[0] != 0)
889 {
c303351c 890 DebugPrintf(INVALID_INDEX, 1, "Waiting for process %s", m_szProcessToWait);
5039dede
AK
891 if (!WaitForProcess(m_szProcessToWait))
892 nxlog_write(MSG_WAITFORPROCESS_FAILED, EVENTLOG_ERROR_TYPE, "s", m_szProcessToWait);
893 }
894
b34606f7
VK
895 DBSetDebugPrintCallback(DBLibraryDebugCallback);
896 DBInit(MSG_DB_LIBRARY, MSG_SQL_ERROR);
897
5039dede
AK
898 // Load other subagents
899 if (m_pszSubagentList != NULL)
900 {
e6c91aac 901 for(pItem = pEnd = m_pszSubagentList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
902 {
903 pEnd = strchr(pItem, '\n');
904 if (pEnd != NULL)
905 *pEnd = 0;
906 StrStrip(pItem);
907 LoadSubAgent(pItem);
908 }
909 free(m_pszSubagentList);
910 }
911
912 // Parse action list
913 if (m_pszActionList != NULL)
914 {
e6c91aac 915 for(pItem = pEnd = m_pszActionList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
916 {
917 pEnd = strchr(pItem, '\n');
918 if (pEnd != NULL)
919 *pEnd = 0;
920 StrStrip(pItem);
921 if (!AddActionFromConfig(pItem, FALSE))
922 nxlog_write(MSG_ADD_ACTION_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
923 }
924 free(m_pszActionList);
925 }
926 if (m_pszShellActionList != NULL)
927 {
e6c91aac 928 for(pItem = pEnd = m_pszShellActionList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede 929 {
bf3b7f79 930 pEnd = _tcschr(pItem, _T('\n'));
5039dede
AK
931
932 if (pEnd != NULL)
933 *pEnd = 0;
934 StrStrip(pItem);
935 if (!AddActionFromConfig(pItem, TRUE))
936 nxlog_write(MSG_ADD_ACTION_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
937 }
938 free(m_pszShellActionList);
939 }
940
941 // Parse external parameters list
942 if (m_pszExtParamList != NULL)
943 {
e6c91aac 944 for(pItem = pEnd = m_pszExtParamList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
945 {
946 pEnd = strchr(pItem, '\n');
947 if (pEnd != NULL)
948 *pEnd = 0;
949 StrStrip(pItem);
950 if (!AddExternalParameter(pItem, FALSE))
951 nxlog_write(MSG_ADD_EXT_PARAM_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
952 }
953 free(m_pszExtParamList);
954 }
955 if (m_pszShExtParamList != NULL)
956 {
e6c91aac 957 for(pItem = pEnd = m_pszShExtParamList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
5039dede
AK
958 {
959 pEnd = strchr(pItem, '\n');
960 if (pEnd != NULL)
961 *pEnd = 0;
962 StrStrip(pItem);
963 if (!AddExternalParameter(pItem, TRUE))
964 nxlog_write(MSG_ADD_EXT_PARAM_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
965 }
966 free(m_pszShExtParamList);
967 }
968
9795113c
VK
969 // Parse external parameters providers list
970 if (m_pszParamProviderList != NULL)
971 {
972 for(pItem = pEnd = m_pszParamProviderList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
973 {
974 pEnd = strchr(pItem, '\n');
975 if (pEnd != NULL)
976 *pEnd = 0;
977 StrStrip(pItem);
978 if (!AddParametersProvider(pItem))
979 nxlog_write(MSG_ADD_PARAM_PROVIDER_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
980 }
981 free(m_pszParamProviderList);
982 }
983
204ace22
VK
984 // Parse external subagents list
985 if (m_pszExtSubagentList != NULL)
986 {
987 for(pItem = pEnd = m_pszExtSubagentList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
988 {
989 pEnd = strchr(pItem, '\n');
990 if (pEnd != NULL)
991 *pEnd = 0;
992 StrStrip(pItem);
993 if (!AddExternalSubagent(pItem))
994 nxlog_write(MSG_ADD_EXTERNAL_SUBAGENT_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
995 }
996 free(m_pszExtSubagentList);
997 }
998
5039dede
AK
999 ThreadSleep(1);
1000
1001 // If StartupDelay is greater than zero, then wait
1002 if (g_dwStartupDelay > 0)
1003 {
1004 if (g_dwFlags & AF_DAEMON)
1005 {
1006 ThreadSleep(g_dwStartupDelay);
1007 }
1008 else
1009 {
1010 DWORD i;
1011
1012 printf("XXXXXX%*s]\rWAIT [", g_dwStartupDelay, " ");
1013 fflush(stdout);
1014 for(i = 0; i < g_dwStartupDelay; i++)
1015 {
1016 ThreadSleep(1);
1017 putc('.', stdout);
1018 fflush(stdout);
1019 }
1020 printf("\n");
1021 }
1022 }
1023
9795113c
VK
1024 StartParamProvidersPoller();
1025
5039dede
AK
1026 // Agent start time
1027 g_tmAgentStartTime = time(NULL);
1028
1029 // Start network listener and session watchdog
1030 m_thListener = ThreadCreateEx(ListenerThread, 0, NULL);
1031 m_thSessionWatchdog = ThreadCreateEx(SessionWatchdog, 0, NULL);
1032 m_thTrapSender = ThreadCreateEx(TrapSender, 0, NULL);
1033
1034#if defined(_WIN32) || defined(_NETWARE)
1035 m_hCondShutdown = ConditionCreate(TRUE);
1036#endif
1037 ThreadSleep(1);
1038
9fcdfda7
VK
1039 // Start watchdog process
1040 if (g_dwFlags & AF_ENABLE_WATCHDOG)
1041 StartWatchdog();
1042
c6fbf758
VK
1043 delete g_config;
1044
5039dede
AK
1045 return TRUE;
1046}
1047
1048
1049//
1050// Shutdown routine
1051//
1052
1053void Shutdown(void)
1054{
8e954797 1055 DebugPrintf(INVALID_INDEX, 2, _T("Shutdown() called"));
9fcdfda7
VK
1056 if (g_dwFlags & AF_ENABLE_WATCHDOG)
1057 StopWatchdog();
1058
5039dede
AK
1059 // Set shutdowm flag
1060 g_dwFlags |= AF_SHUTDOWN;
1061 ShutdownTrapSender();
1062 ThreadJoin(m_thSessionWatchdog);
1063 ThreadJoin(m_thListener);
1064 ThreadJoin(m_thTrapSender);
1065
1066 UnloadAllSubAgents();
1067 nxlog_write(MSG_AGENT_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
1068 nxlog_close();
1069
1070 // Notify main thread about shutdown
1071#ifdef _WIN32
1072 ConditionSet(m_hCondShutdown);
1073#endif
1074
1075 // Remove PID file
1076#if !defined(_WIN32) && !defined(_NETWARE)
1077 remove(g_szPidFile);
1078#endif
1079}
1080
1081
1082//
1083// Common Main()
1084//
1085
1086void Main(void)
1087{
1088 nxlog_write(MSG_AGENT_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
1089
1090 if (g_dwFlags & AF_DAEMON)
1091 {
1092#if defined(_WIN32) || defined(_NETWARE)
1093 ConditionWait(m_hCondShutdown, INFINITE);
1094#else
1095 StartMainLoop(SignalHandler, NULL);
1096#endif
1097 }
1098 else
1099 {
1100#if defined(_WIN32)
1101 if (g_dwFlags & AF_HIDE_WINDOW)
1102 {
1103 HWND hWnd;
1104
1105 hWnd = GetConsoleHWND();
1106 if (hWnd != NULL)
1107 ShowWindow(hWnd, SW_HIDE);
1108 ConditionWait(m_hCondShutdown, INFINITE);
1109 ThreadSleep(1);
1110 }
1111 else
1112 {
1113 printf("Agent running. Press ESC to shutdown.\n");
1114 while(1)
1115 {
3973f74b 1116 if (_getch() == 27)
5039dede
AK
1117 break;
1118 }
1119 printf("Agent shutting down...\n");
1120 Shutdown();
1121 }
1122#elif defined(_NETWARE)
1123 printf("Agent running. Type UNLOAD NXAGENTD on the system console for shutdown.\n");
1124 ConditionWait(m_hCondShutdown, INFINITE);
1125#else
1126 printf("Agent running. Press Ctrl+C to shutdown.\n");
1127 StartMainLoop(SignalHandler, NULL);
1128 printf("\nStopping agent...\n");
1129#endif
1130 }
1131}
1132
1133
1134//
1135// Do necessary actions on agent restart
1136//
1137
1138static void DoRestartActions(DWORD dwOldPID)
1139{
1140#if defined(_WIN32)
1141 if (dwOldPID == 0)
1142 {
1143 // Service
1144 StopAgentService();
1145 WaitForService(SERVICE_STOPPED);
1146 StartAgentService();
1147 ExitProcess(0);
1148 }
1149 else
1150 {
1151 HANDLE hProcess;
1152
1153 hProcess = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, dwOldPID);
1154 if (hProcess != NULL)
1155 {
1156 if (WaitForSingleObject(hProcess, 60000) == WAIT_TIMEOUT)
1157 {
1158 TerminateProcess(hProcess, 0);
1159 }
1160 CloseHandle(hProcess);
1161 }
1162 }
1163#elif defined(_NETWARE)
1164 /* TODO: implement restart for NetWare */
1165#else
1166 int i;
1167
1168 kill(dwOldPID, SIGTERM);
1169 for(i = 0; i < 30; i++)
1170 {
1171 sleep(2);
1172 if (kill(dwOldPID, SIGCONT) == -1)
1173 break;
1174 }
1175
1176 // Kill previous instance of agent if it's still running
1177 if (i == 30)
1178 kill(dwOldPID, SIGKILL);
1179#endif
1180}
1181
1182
1183//
1184// NetWare exit handler
1185//
1186
1187#ifdef _NETWARE
1188
1189static void ExitHandler(int nSig)
1190{
1191 printf("\n*** Unloading NetXMS agent ***\n");
1192 ConditionSet(m_hCondShutdown);
1193 while(g_nThreadCount > 0)
1194 pthread_yield();
1195}
1196
1197#endif
1198
1199
1200//
1201// Create configuration file
1202//
1203
48dd3c80
VK
1204static int CreateConfig(const TCHAR *pszServer, const TCHAR *pszLogFile, const TCHAR *pszFileStore,
1205 const TCHAR *configIncludeDir, int iNumSubAgents, TCHAR **ppszSubAgentList)
5039dede
AK
1206{
1207 FILE *fp;
1208 time_t currTime;
1209 int i;
1210
1211 if (_taccess(g_szConfigFile, 0) == 0)
1212 return 0; // File already exist, we shouldn't overwrite it
1213
1214 fp = _tfopen(g_szConfigFile, _T("w"));
1215 if (fp != NULL)
1216 {
1217 currTime = time(NULL);
1218 _ftprintf(fp, _T("#\n# NetXMS agent configuration file\n# Created by agent installer at %s#\n\n"),
1219 _tctime(&currTime));
48dd3c80
VK
1220 _ftprintf(fp, _T("MasterServers = %s\nConfigIncludeDir = %s\nLogFile = %s\nFileStore = %s\n"),
1221 pszServer, configIncludeDir, pszLogFile, pszFileStore);
5039dede
AK
1222 for(i = 0; i < iNumSubAgents; i++)
1223 _ftprintf(fp, _T("SubAgent = %s\n"), ppszSubAgentList[i]);
1224 fclose(fp);
1225 }
1226 return (fp != NULL) ? 0 : 2;
1227}
1228
1229
e6c91aac
VK
1230//
1231// Init config
1232//
1233
1234static void InitConfig()
1235{
1236 g_config = new Config();
8a0dffac 1237 g_config->setTopLevelTag(_T("config"));
e6c91aac
VK
1238}
1239
1240
5039dede
AK
1241//
1242// Startup
1243//
1244
1245int main(int argc, char *argv[])
1246{
1247 int ch, iExitCode = 0, iAction = ACTION_RUN_AGENT;
1248 BOOL bRestart = FALSE;
9fcdfda7 1249 DWORD dwOldPID, dwMainPID;
bf3b7f79 1250 TCHAR *eptr;
5039dede 1251#ifdef _WIN32
bf3b7f79 1252 TCHAR szModuleName[MAX_PATH];
1621a079
VK
1253 HKEY hKey;
1254 DWORD dwSize;
1255#else
1256 char *pszEnv;
5039dede
AK
1257#endif
1258
1259 InitThreadLibrary();
9fcdfda7
VK
1260
1261#ifdef NETXMS_MEMORY_DEBUG
1262 InitMemoryDebugger();
1263#endif
5039dede
AK
1264
1265#ifdef _NETWARE
1266 g_nThreadCount++;
1267 setscreenmode(SCR_AUTOCLOSE_ON_EXIT | SCR_COLOR_ATTRS);
1268#endif
1269
1270 // Set locale to C. It shouldn't be needed, according to
1271 // documentation, but I've seen the cases when agent formats
1272 // floating point numbers by sprintf inserting comma in place
1273 // of a dot, as set by system's regional settings.
1274#ifdef _WIN32
1275 setlocale(LC_ALL, "C");
1276#endif
1277
1621a079
VK
1278 // Check for alternate config file location
1279#ifdef _WIN32
1280 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Agent"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
1281 {
1282 dwSize = MAX_PATH * sizeof(TCHAR);
1283 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)g_szConfigFile, &dwSize);
1284 RegQueryValueEx(hKey, _T("ConfigIncludeDir"), NULL, NULL, (BYTE *)g_szConfigIncludeDir, &dwSize);
1285 RegCloseKey(hKey);
1286 }
1287#else
1288 pszEnv = _tgetenv(_T("NXAGENTD_CONFIG"));
1289 if (pszEnv != NULL)
1290 nx_strncpy(g_szConfigFile, pszEnv, MAX_PATH);
1291
1292 pszEnv = _tgetenv(_T("NXAGENTD_CONFIG_D"));
1293 if (pszEnv != NULL)
1294 nx_strncpy(g_szConfigIncludeDir, pszEnv, MAX_PATH);
1295#endif
1296
5039dede
AK
1297 // Parse command line
1298 if (argc == 1)
1299 iAction = ACTION_HELP;
1300 opterr = 1;
1301 while((ch = getopt(argc, argv, VALID_OPTIONS)) != -1)
1302 {
1303 switch(ch)
1304 {
1305 case 'h': // Display help and exit
1306 iAction = ACTION_HELP;
1307 break;
1308 case 'd': // Run as daemon
1309 g_dwFlags |= AF_DAEMON;
1310 break;
1311 case 'f': // Run in foreground
1312 g_dwFlags &= ~AF_DAEMON;
1313 break;
1314 case 'D': // Turn on debug output
53c96e2d
VK
1315 g_debugLevel = strtoul(optarg, &eptr, 0);
1316 if ((*eptr != 0) || (g_debugLevel > 9))
c303351c
VK
1317 {
1318 fprintf(stderr, "Invalid debug level: %s\n", optarg);
1319 iAction = -1;
1320 iExitCode = 1;
1321 }
5039dede
AK
1322 break;
1323 case 'c': // Configuration file
1324 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
1325 break;
1326#if !defined(_WIN32) && !defined(_NETWARE)
1327 case 'p': // PID file
1328 nx_strncpy(g_szPidFile, optarg, MAX_PATH);
1329 break;
1330#endif
1331 case 'C': // Configuration check only
1332 iAction = ACTION_CHECK_CONFIG;
1333 break;
1334 case 'v': // Print version and exit
1335 printf("NetXMS Core Agent Version " AGENT_VERSION_STRING "\n");
1336 iAction = ACTION_NONE;
1337 break;
1338 case 'M':
1339 g_dwFlags |= AF_CENTRAL_CONFIG;
1340 nx_strncpy(g_szConfigServer, optarg, MAX_DB_STRING);
1341 break;
1342 case 'r':
1343 g_dwFlags |= AF_REGISTER;
1344 nx_strncpy(g_szRegistrar, optarg, MAX_DB_STRING);
1345 break;
1346 case 'P': // Platform suffix
1347 nx_strncpy(g_szPlatformSuffix, optarg, MAX_PSUFFIX_LENGTH);
1348 break;
1349 case 'X': // Agent is being restarted
1350 bRestart = TRUE;
1351 dwOldPID = strtoul(optarg, NULL, 10);
1352 break;
9fcdfda7
VK
1353 case 'W': // Watchdog process
1354 iAction = ACTION_RUN_WATCHDOG;
1355 dwMainPID = strtoul(optarg, NULL, 10);
1356 break;
5039dede
AK
1357 case 'Z': // Create configuration file
1358 iAction = ACTION_CREATE_CONFIG;
1359 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
1360 break;
1361#ifdef _WIN32
1362 case 'H': // Hide window
1363 g_dwFlags |= AF_HIDE_WINDOW;
1364 break;
175bd003
VK
1365 case 'i':
1366 g_dwFlags |= AF_INTERACTIVE_SERVICE;
1367 break;
5039dede
AK
1368 case 'I': // Install Windows service
1369 iAction = ACTION_INSTALL_SERVICE;
1370 break;
1371 case 'R': // Remove Windows service
1372 iAction = ACTION_REMOVE_SERVICE;
1373 break;
1374 case 's': // Start Windows service
1375 iAction = ACTION_START_SERVICE;
1376 break;
1377 case 'S': // Stop Windows service
1378 iAction = ACTION_STOP_SERVICE;
1379 break;
1380 case 'E': // Install Windows event source
1381 iAction = ACTION_INSTALL_EVENT_SOURCE;
1382 break;
1383 case 'U': // Remove Windows event source
1384 iAction = ACTION_REMOVE_EVENT_SOURCE;
1385 break;
9f96fc88
VK
1386 case 'e': // Event source name
1387 nx_strncpy(g_windowsEventSourceName, optarg, MAX_PATH);
1388 break;
1389 case 'n': // Service name
1390 nx_strncpy(g_windowsServiceName, optarg, MAX_PATH);
1391 break;
1392 case 'N': // Service display name
1393 nx_strncpy(g_windowsServiceDisplayName, optarg, MAX_PATH);
1394 break;
5039dede
AK
1395#endif
1396 case '?':
1397 iAction = ACTION_HELP;
1398 iExitCode = 1;
1399 break;
1400 default:
1401 break;
1402 }
1403 }
1404
1405#if !defined(_WIN32) && !defined(_NETWARE)
1406 if (!_tcscmp(g_szConfigFile, _T("{search}")))
1407 {
1408 if (access(PREFIX "/etc/nxagentd.conf", 4) == 0)
1409 {
1410 _tcscpy(g_szConfigFile, PREFIX "/etc/nxagentd.conf");
1411 }
1412 else if (access("/usr/etc/nxagentd.conf", 4) == 0)
1413 {
1414 _tcscpy(g_szConfigFile, "/usr/etc/nxagentd.conf");
1415 }
1416 else
1417 {
1418 _tcscpy(g_szConfigFile, "/etc/nxagentd.conf");
1419 }
1420 }
80ce37ac 1421 if (!_tcscmp(g_szConfigIncludeDir, _T("{search}")))
abf35d58
VK
1422 {
1423 if (access(PREFIX "/etc/nxagentd.conf.d", 4) == 0)
1424 {
80ce37ac 1425 _tcscpy(g_szConfigIncludeDir, PREFIX "/etc/nxagentd.conf.d");
abf35d58
VK
1426 }
1427 else if (access("/usr/etc/nxagentd.conf.d", 4) == 0)
1428 {
80ce37ac 1429 _tcscpy(g_szConfigIncludeDir, "/usr/etc/nxagentd.conf.d");
abf35d58
VK
1430 }
1431 else
1432 {
80ce37ac 1433 _tcscpy(g_szConfigIncludeDir, "/etc/nxagentd.conf.d");
abf35d58
VK
1434 }
1435 }
5039dede
AK
1436#endif
1437
1438 if (bRestart)
1439 DoRestartActions(dwOldPID);
1440
e6c91aac
VK
1441 InitConfig();
1442
5039dede
AK
1443 // Do requested action
1444 switch(iAction)
1445 {
1446 case ACTION_RUN_AGENT:
1447 // Set default value for session idle timeout based on
1448 // connect() timeout, if possible
1449#if HAVE_SYSCTLBYNAME && !defined(_IPSO)
1450 {
1451 LONG nVal;
1452 size_t nSize;
1453
1454 nSize = sizeof(nVal);
1455 if (sysctlbyname("net.inet.tcp.keepinit", &nVal, &nSize, NULL, 0) == 0)
1456 {
1457 g_dwIdleTimeout = nVal / 1000 + 15;
1458 }
1459 }
1460#endif
1461
1462 if (g_dwFlags & AF_CENTRAL_CONFIG)
1463 {
c303351c 1464 if (g_debugLevel > 0)
5039dede
AK
1465 printf("Downloading configuration from %s...\n", g_szConfigServer);
1466 if (DownloadConfig(g_szConfigServer))
1467 {
c303351c 1468 if (g_debugLevel > 0)
5039dede
AK
1469 printf("Configuration downloaded successfully\n");
1470 }
1471 else
1472 {
c303351c 1473 if (g_debugLevel > 0)
5039dede
AK
1474 printf("Configuration download failed\n");
1475 }
1476 }
1477
abf35d58 1478 if (g_config->loadConfig(g_szConfigFile, _T("agent")))
e6c91aac 1479 {
357a2954
AK
1480 const TCHAR *dir = g_config->getValue(_T("/agent/ConfigIncludeDir"));
1481 if (dir != NULL)
6ca3b41c
VK
1482 nx_strncpy(g_szConfigIncludeDir, dir, MAX_PATH);
1483 g_config->loadConfigDirectory(g_szConfigIncludeDir, _T("agent"));
abf35d58
VK
1484 if (g_config->parseTemplate(_T("agent"), m_cfgTemplate))
1485 {
0bbab9d3 1486 // Set exception handler
9fcdfda7 1487#ifdef _WIN32
abf35d58
VK
1488 if (g_dwFlags & AF_CATCH_EXCEPTIONS)
1489 SetExceptionHandler(SEHServiceExceptionHandler, SEHServiceExceptionDataWriter, m_szDumpDir,
bf3b7f79 1490 _T("nxagentd"), MSG_EXCEPTION, g_dwFlags & AF_WRITE_FULL_DUMP, !(g_dwFlags & AF_DAEMON));
abf35d58 1491 __try {
9fcdfda7 1492#endif
bf3b7f79
VK
1493 if ((!_tcsicmp(g_szLogFile, _T("{syslog}"))) ||
1494 (!_tcsicmp(g_szLogFile, _T("{eventlog}"))))
abf35d58 1495 g_dwFlags |= AF_USE_SYSLOG;
5039dede
AK
1496
1497#ifdef _WIN32
abf35d58
VK
1498 if (g_dwFlags & AF_DAEMON)
1499 {
1500 InitService();
1501 }
1502 else
1503 {
1504 if (Initialize())
1505 {
1506 Main();
1507 }
1508 else
1509 {
bf3b7f79 1510 ConsolePrintf(_T("Agent initialization failed\n"));
abf35d58
VK
1511 nxlog_close();
1512 iExitCode = 3;
1513 }
1514 }
5039dede
AK
1515#else /* _WIN32 */
1516#ifndef _NETWARE
abf35d58
VK
1517 if (g_dwFlags & AF_DAEMON)
1518 if (daemon(0, 0) == -1)
1519 {
1520 perror("Unable to setup itself as a daemon");
1521 iExitCode = 4;
1522 }
5039dede 1523#endif
abf35d58 1524 if (iExitCode == 0)
5039dede
AK
1525 {
1526#ifndef _NETWARE
abf35d58 1527 m_pid = getpid();
5039dede 1528#endif
abf35d58
VK
1529 if (Initialize())
1530 {
5039dede 1531#ifdef _NETWARE
abf35d58 1532 signal(SIGTERM, ExitHandler);
5039dede 1533#else
abf35d58
VK
1534 FILE *fp;
1535
1536 // Write PID file
1537 fp = fopen(g_szPidFile, "w");
1538 if (fp != NULL)
1539 {
1540 fprintf(fp, "%d", m_pid);
1541 fclose(fp);
1542 }
5039dede 1543#endif
abf35d58
VK
1544 Main();
1545 Shutdown();
1546 }
1547 else
1548 {
bf3b7f79 1549 ConsolePrintf(_T("Agent initialization failed\n"));
abf35d58
VK
1550 nxlog_close();
1551 iExitCode = 3;
1552 }
1553 }
5039dede
AK
1554#endif /* _WIN32 */
1555
1556#if defined(_WIN32) || defined(_NETWARE)
abf35d58
VK
1557 if (m_hCondShutdown != INVALID_CONDITION_HANDLE)
1558 ConditionDestroy(m_hCondShutdown);
9fcdfda7
VK
1559#endif
1560#ifdef _WIN32
abf35d58 1561 LIBNETXMS_EXCEPTION_HANDLER
5039dede 1562#endif
abf35d58
VK
1563 }
1564 else
1565 {
bf3b7f79 1566 ConsolePrintf(_T("Error parsing configuration file\n"));
abf35d58
VK
1567 iExitCode = 2;
1568 }
5039dede
AK
1569 }
1570 else
1571 {
bf3b7f79 1572 ConsolePrintf(_T("Error loading configuration file\n"));
5039dede
AK
1573 iExitCode = 2;
1574 }
1575 break;
1576 case ACTION_CHECK_CONFIG:
97a92859 1577 if (!g_config->loadIniConfig(g_szConfigFile, _T("agent")) || !g_config->parseTemplate(_T("agent"), m_cfgTemplate))
5039dede 1578 {
bf3b7f79 1579 ConsolePrintf(_T("Configuration file check failed\n"));
5039dede
AK
1580 iExitCode = 2;
1581 }
1582 break;
9fcdfda7
VK
1583 case ACTION_RUN_WATCHDOG:
1584 iExitCode = WatchdogMain(dwMainPID);
1585 break;
5039dede
AK
1586 case ACTION_CREATE_CONFIG:
1587 iExitCode = CreateConfig(CHECK_NULL(argv[optind]), CHECK_NULL(argv[optind + 1]),
48dd3c80
VK
1588 CHECK_NULL(argv[optind + 2]), CHECK_NULL(argv[optind + 3]),
1589 argc - optind - 4, &argv[optind + 4]);
5039dede
AK
1590 break;
1591#ifdef _WIN32
1592 case ACTION_INSTALL_SERVICE:
1593 GetModuleFileName(GetModuleHandle(NULL), szModuleName, MAX_PATH);
1594 InstallService(szModuleName, g_szConfigFile);
1595 break;
1596 case ACTION_REMOVE_SERVICE:
1597 RemoveService();
1598 break;
1599 case ACTION_INSTALL_EVENT_SOURCE:
1600 GetModuleFileName(GetModuleHandle(NULL), szModuleName, MAX_PATH);
1601 InstallEventSource(szModuleName);
1602 break;
1603 case ACTION_REMOVE_EVENT_SOURCE:
1604 RemoveEventSource();
1605 break;
1606 case ACTION_START_SERVICE:
1607 StartAgentService();
1608 break;
1609 case ACTION_STOP_SERVICE:
1610 StopAgentService();
1611 break;
1612#endif
1613 case ACTION_HELP:
9fcdfda7 1614 fputs(m_szHelpText, stdout);
5039dede
AK
1615 break;
1616 default:
1617 break;
1618 }
1619
1620#ifdef _NETWARE
1621 if ((iExitCode != 0) || (iAction == ACTION_NONE) ||
1622 (iAction == ACTION_CHECK_CONFIG))
1623 setscreenmode(SCR_NO_MODE);
1624 g_nThreadCount--;
1625#endif
1626
1627 return iExitCode;
1628}