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