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