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