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