Implemented Debug Tag functionality
[public/netxms.git] / src / agent / core / nxagentd.cpp
1 /*
2 ** NetXMS multiplatform core agent
3 ** Copyright (C) 2003-2017 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
25 #if defined(_WIN32)
26 #include <shlobj.h>
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 #ifdef _WITH_ENCRYPTION
46 #include <openssl/ssl.h>
47 #endif
48
49 #if HAVE_GRP_H
50 #include <grp.h>
51 #endif
52
53 #if HAVE_PWD_H
54 #include <pwd.h>
55 #endif
56
57 #define DEFAULT_CONFIG_SECTION _T("CORE")
58
59 /**
60 * Externals
61 */
62 THREAD_RESULT THREAD_CALL ListenerThread(void *);
63 THREAD_RESULT THREAD_CALL SessionWatchdog(void *);
64 THREAD_RESULT THREAD_CALL TrapSender(void *);
65 THREAD_RESULT THREAD_CALL MasterAgentListener(void *arg);
66 THREAD_RESULT THREAD_CALL SNMPTrapReceiver(void *);
67 THREAD_RESULT THREAD_CALL SNMPTrapSender(void *);
68 THREAD_RESULT THREAD_CALL SyslogReceiver(void *);
69 THREAD_RESULT THREAD_CALL SyslogSender(void *);
70 THREAD_RESULT THREAD_CALL TunnelManager(void *);
71
72 void ShutdownTrapSender();
73 void ShutdownSNMPTrapSender();
74
75 void ShutdownSyslogSender();
76
77 void StartLocalDataCollector();
78 void ShutdownLocalDataCollector();
79
80 void StartWatchdog();
81 void StopWatchdog();
82 int WatchdogMain(DWORD pid);
83
84 void InitSessionList();
85 void DestroySessionList();
86
87 BOOL RegisterOnServer(const TCHAR *pszServer);
88
89 void UpdatePolicyInventory();
90
91 void ParseTunnelList(TCHAR *list);
92
93 #if !defined(_WIN32)
94 void InitStaticSubagents();
95 #endif
96
97 #ifdef _WIN32
98 extern TCHAR g_windowsServiceName[];
99 extern TCHAR g_windowsServiceDisplayName[];
100 #endif
101
102 void LIBNXAGENT_EXPORTABLE InitSubAgentAPI(void (* writeLog)(int, int, const TCHAR *),
103 void (* sendTrap1)(UINT32, const TCHAR *, const char *, va_list),
104 void (* sendTrap2)(UINT32, const TCHAR *, int, TCHAR **),
105 bool (* enumerateSessions)(EnumerationCallbackResult (*)(AbstractCommSession *, void *), void*),
106 AbstractCommSession *(* findServerSession)(UINT64),
107 bool (* sendFile)(void *, UINT32, const TCHAR *, long, bool),
108 bool (* pushData)(const TCHAR *, const TCHAR *, UINT32, time_t),
109 DB_HANDLE (* getLocalDatabaseHandle)(),
110 CONDITION shutdownCondition, const TCHAR *dataDirectory);
111
112 /**
113 * Messages generated by mc.pl (for UNIX version only)
114 */
115 #ifndef _WIN32
116 extern unsigned int g_dwNumMessages;
117 extern const TCHAR *g_szMessages[];
118 #endif
119
120 /**
121 * OpenSSL APPLINK
122 */
123 #ifdef _WIN32
124 #include <openssl/applink.c>
125 #endif
126
127 /**
128 * Valid options for getopt()
129 */
130 #if defined(_WIN32)
131 #define VALID_OPTIONS "c:CdD:e:EfG:hHiIKM:n:N:P:r:RsSUvW:X:Z:"
132 #else
133 #define VALID_OPTIONS "c:CdD:fg:G:hKM:p:P:r:u:vW:X:Z:"
134 #endif
135
136 /**
137 * Actions
138 */
139 #define ACTION_NONE 0
140 #define ACTION_RUN_AGENT 1
141 #define ACTION_INSTALL_SERVICE 2
142 #define ACTION_REMOVE_SERVICE 3
143 #define ACTION_START_SERVICE 4
144 #define ACTION_STOP_SERVICE 5
145 #define ACTION_CHECK_CONFIG 6
146 #define ACTION_INSTALL_EVENT_SOURCE 7
147 #define ACTION_REMOVE_EVENT_SOURCE 8
148 #define ACTION_CREATE_CONFIG 9
149 #define ACTION_HELP 10
150 #define ACTION_RUN_WATCHDOG 11
151 #define ACTION_SHUTDOWN_EXT_AGENTS 12
152
153 /**
154 * Global variables
155 */
156 UINT32 g_dwFlags = AF_ENABLE_ACTIONS | AF_ENABLE_AUTOLOAD | AF_WRITE_FULL_DUMP;
157 UINT32 g_failFlags = 0;
158 TCHAR g_szLogFile[MAX_PATH] = AGENT_DEFAULT_LOG;
159 TCHAR g_szSharedSecret[MAX_SECRET_LENGTH] = _T("admin");
160 TCHAR g_szConfigFile[MAX_PATH] = AGENT_DEFAULT_CONFIG;
161 TCHAR g_szFileStore[MAX_PATH] = AGENT_DEFAULT_FILE_STORE;
162 TCHAR g_szDataDirectory[MAX_PATH] = AGENT_DEFAULT_DATA_DIR;
163 TCHAR g_szPlatformSuffix[MAX_PSUFFIX_LENGTH] = _T("");
164 TCHAR g_szConfigServer[MAX_DB_STRING] = _T("not_set");
165 TCHAR g_szRegistrar[MAX_DB_STRING] = _T("not_set");
166 TCHAR g_szListenAddress[MAX_PATH] = _T("*");
167 TCHAR g_szConfigIncludeDir[MAX_PATH] = AGENT_DEFAULT_CONFIG_D;
168 TCHAR g_szConfigPolicyDir[MAX_PATH] = AGENT_DEFAULT_CONFIG_D;
169 TCHAR g_szLogParserDirectory[MAX_PATH] = _T("");
170 TCHAR g_certificateDirectory[MAX_PATH] = _T("");
171 TCHAR g_masterAgent[MAX_PATH] = _T("not_set");
172 TCHAR g_szSNMPTrapListenAddress[MAX_PATH] = _T("*");
173 UINT16 g_wListenPort = AGENT_LISTEN_PORT;
174 TCHAR g_systemName[MAX_OBJECT_NAME] = _T("");
175 ObjectArray<ServerInfo> g_serverList(8, 8, true);
176 UINT32 g_execTimeout = 2000; // External process execution timeout in milliseconds
177 UINT32 g_snmpTimeout = 1000;
178 UINT16 g_snmpTrapPort = 162;
179 time_t g_tmAgentStartTime;
180 UINT32 g_dwStartupDelay = 0;
181 UINT32 g_dwMaxSessions = 0;
182 UINT32 g_longRunningQueryThreshold = 250;
183 UINT32 g_dcReconciliationBlockSize = 1024;
184 UINT32 g_dcReconciliationTimeout = 15000;
185 UINT32 g_dcMaxCollectorPoolSize = 64;
186 UINT32 g_zoneUIN = 0;
187 UINT16 g_syslogListenPort = 514;
188 #ifdef _WIN32
189 UINT16 g_sessionAgentPort = 28180;
190 #else
191 UINT16 g_sessionAgentPort = 0;
192 #endif
193 Config *g_config = NULL;
194 UINT32 g_dwIdleTimeout = 120; // Session idle timeout
195
196 #if !defined(_WIN32)
197 TCHAR g_szPidFile[MAX_PATH] = _T("/var/run/nxagentd.pid");
198 #endif
199
200 /**
201 * Static variables
202 */
203 static TCHAR *m_pszActionList = NULL;
204 static TCHAR *m_pszShellActionList = NULL;
205 static TCHAR *m_pszServerList = NULL;
206 static TCHAR *m_pszControlServerList = NULL;
207 static TCHAR *m_pszMasterServerList = NULL;
208 static TCHAR *m_pszSubagentList = NULL;
209 static TCHAR *s_externalParametersConfig = NULL;
210 static TCHAR *s_externalShellExecParametersConfig = NULL;
211 static TCHAR *s_externalParameterProvidersConfig = NULL;
212 static TCHAR *s_externalListsConfig = NULL;
213 static TCHAR *s_externalTablesConfig = NULL;
214 static TCHAR *s_externalSubAgentsList = NULL;
215 static TCHAR *s_appAgentsList = NULL;
216 static TCHAR *s_serverConnectionList = NULL;
217 static UINT32 s_enabledCiphers = 0xFFFF;
218 static THREAD s_sessionWatchdogThread = INVALID_THREAD_HANDLE;
219 static THREAD s_listenerThread = INVALID_THREAD_HANDLE;
220 static THREAD s_eventSenderThread = INVALID_THREAD_HANDLE;
221 static THREAD s_snmpTrapReceiverThread = INVALID_THREAD_HANDLE;
222 static THREAD s_snmpTrapSenderThread = INVALID_THREAD_HANDLE;
223 static THREAD s_syslogReceiverThread = INVALID_THREAD_HANDLE;
224 static THREAD s_syslogSenderThread = INVALID_THREAD_HANDLE;
225 static THREAD s_masterAgentListenerThread = INVALID_THREAD_HANDLE;
226 static THREAD s_tunnelManagerThread = INVALID_THREAD_HANDLE;
227 static TCHAR s_processToWaitFor[MAX_PATH] = _T("");
228 static TCHAR s_dumpDir[MAX_PATH] = _T("C:\\");
229 static UINT64 s_maxLogSize = 16384 * 1024;
230 static UINT32 s_logHistorySize = 4;
231 static UINT32 s_logRotationMode = NXLOG_ROTATION_BY_SIZE;
232 static TCHAR s_dailyLogFileSuffix[64] = _T("");
233 static TCHAR s_executableName[MAX_PATH];
234 static UINT32 s_debugLevel = (UINT32)NXCONFIG_UNINITIALIZED_VALUE;
235 static TCHAR *s_debugTags = NULL;
236
237 static CONDITION s_subAgentsStopCondition = INVALID_CONDITION_HANDLE;
238 #if defined(_WIN32)
239 static CONDITION s_shutdownCondition = INVALID_CONDITION_HANDLE;
240 #endif
241
242 #if !defined(_WIN32)
243 static pid_t s_pid;
244 #endif
245
246 /**
247 * Configuration file template
248 */
249 static NX_CFG_TEMPLATE m_cfgTemplate[] =
250 {
251 { _T("Action"), CT_STRING_LIST, '\n', 0, 0, 0, &m_pszActionList, NULL },
252 { _T("ActionShellExec"), CT_STRING_LIST, '\n', 0, 0, 0, &m_pszShellActionList, NULL },
253 { _T("AppAgent"), CT_STRING_LIST, '\n', 0, 0, 0, &s_appAgentsList, NULL },
254 { _T("BackgroundLogWriter"), CT_BOOLEAN, 0, 0, AF_BACKGROUND_LOG_WRITER, 0, &g_dwFlags, NULL },
255 { _T("ControlServers"), CT_STRING_LIST, ',', 0, 0, 0, &m_pszControlServerList, NULL },
256 { _T("CreateCrashDumps"), CT_BOOLEAN, 0, 0, AF_CATCH_EXCEPTIONS, 0, &g_dwFlags, NULL },
257 { _T("DataCollectionThreadPoolSize"), CT_LONG, 0, 0, 0, 0, &g_dcMaxCollectorPoolSize, NULL },
258 { _T("DataDirectory"), CT_STRING, 0, 0, MAX_PATH, 0, g_szDataDirectory, NULL },
259 { _T("DataReconciliationBlockSize"), CT_LONG, 0, 0, 0, 0, &g_dcReconciliationBlockSize, NULL },
260 { _T("DataReconciliationTimeout"), CT_LONG, 0, 0, 0, 0, &g_dcReconciliationTimeout, NULL },
261 { _T("DailyLogFileSuffix"), CT_STRING, 0, 0, 64, 0, s_dailyLogFileSuffix, NULL },
262 { _T("DebugLevel"), CT_LONG, 0, 0, 0, 0, &s_debugLevel, &s_debugLevel },
263 { _T("DebugTags"), CT_STRING_LIST, ',', 0, 0, 0, &s_debugTags, NULL },
264 { _T("DisableIPv4"), CT_BOOLEAN, 0, 0, AF_DISABLE_IPV4, 0, &g_dwFlags, NULL },
265 { _T("DisableIPv6"), CT_BOOLEAN, 0, 0, AF_DISABLE_IPV6, 0, &g_dwFlags, NULL },
266 { _T("DumpDirectory"), CT_STRING, 0, 0, MAX_PATH, 0, s_dumpDir, NULL },
267 { _T("EnableActions"), CT_BOOLEAN, 0, 0, AF_ENABLE_ACTIONS, 0, &g_dwFlags, NULL },
268 { _T("EnabledCiphers"), CT_LONG, 0, 0, 0, 0, &s_enabledCiphers, NULL },
269 { _T("EnableControlConnector"), CT_BOOLEAN, 0, 0, AF_ENABLE_CONTROL_CONNECTOR, 0, &g_dwFlags, NULL },
270 { _T("EnableProxy"), CT_BOOLEAN, 0, 0, AF_ENABLE_PROXY, 0, &g_dwFlags, NULL },
271 { _T("EnableSNMPProxy"), CT_BOOLEAN, 0, 0, AF_ENABLE_SNMP_PROXY, 0, &g_dwFlags, NULL },
272 { _T("EnableSNMPTrapProxy"), CT_BOOLEAN, 0, 0, AF_ENABLE_SNMP_TRAP_PROXY, 0, &g_dwFlags, NULL },
273 { _T("EnableSyslogProxy"), CT_BOOLEAN, 0, 0, AF_ENABLE_SYSLOG_PROXY, 0, &g_dwFlags, NULL },
274 { _T("EnableSubagentAutoload"), CT_BOOLEAN, 0, 0, AF_ENABLE_AUTOLOAD, 0, &g_dwFlags, NULL },
275 { _T("EnableWatchdog"), CT_BOOLEAN, 0, 0, AF_ENABLE_WATCHDOG, 0, &g_dwFlags, NULL },
276 { _T("EncryptedSharedSecret"), CT_STRING, 0, 0, MAX_SECRET_LENGTH, 0, g_szSharedSecret, NULL },
277 { _T("ExecTimeout"), CT_LONG, 0, 0, 0, 0, &g_execTimeout, NULL },
278 { _T("ExternalList"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalListsConfig, NULL },
279 { _T("ExternalMasterAgent"), CT_STRING, 0, 0, MAX_PATH, 0, g_masterAgent, NULL },
280 { _T("ExternalParameter"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalParametersConfig, NULL },
281 { _T("ExternalParameterShellExec"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalShellExecParametersConfig, NULL },
282 { _T("ExternalParametersProvider"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalParameterProvidersConfig, NULL },
283 { _T("ExternalSubagent"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalSubAgentsList, NULL },
284 { _T("ExternalTable"), CT_STRING_LIST, '\n', 0, 0, 0, &s_externalTablesConfig, NULL },
285 { _T("FileStore"), CT_STRING, 0, 0, MAX_PATH, 0, g_szFileStore, NULL },
286 { _T("FullCrashDumps"), CT_BOOLEAN, 0, 0, AF_WRITE_FULL_DUMP, 0, &g_dwFlags, NULL },
287 { _T("ListenAddress"), CT_STRING, 0, 0, MAX_PATH, 0, g_szListenAddress, NULL },
288 { _T("ListenPort"), CT_WORD, 0, 0, 0, 0, &g_wListenPort, NULL },
289 { _T("LogFile"), CT_STRING, 0, 0, MAX_PATH, 0, g_szLogFile, NULL },
290 { _T("LogHistorySize"), CT_LONG, 0, 0, 0, 0, &s_logHistorySize, NULL },
291 { _T("LogRotationMode"), CT_LONG, 0, 0, 0, 0, &s_logRotationMode, NULL },
292 { _T("LogUnresolvedSymbols"), CT_BOOLEAN, 0, 0, AF_LOG_UNRESOLVED_SYMBOLS, 0, &g_dwFlags, NULL },
293 { _T("LongRunningQueryThreshold"), CT_LONG, 0, 0, 0, 0, &g_longRunningQueryThreshold, NULL },
294 { _T("MasterServers"), CT_STRING_LIST, ',', 0, 0, 0, &m_pszMasterServerList, NULL },
295 { _T("MaxLogSize"), CT_SIZE_BYTES, 0, 0, 0, 0, &s_maxLogSize, NULL },
296 { _T("MaxSessions"), CT_LONG, 0, 0, 0, 0, &g_dwMaxSessions, NULL },
297 { _T("PlatformSuffix"), CT_STRING, 0, 0, MAX_PSUFFIX_LENGTH, 0, g_szPlatformSuffix, NULL },
298 { _T("RequireAuthentication"), CT_BOOLEAN, 0, 0, AF_REQUIRE_AUTH, 0, &g_dwFlags, NULL },
299 { _T("RequireEncryption"), CT_BOOLEAN, 0, 0, AF_REQUIRE_ENCRYPTION, 0, &g_dwFlags, NULL },
300 { _T("ServerConnection"), CT_STRING_LIST, '\n', 0, 0, 0, &s_serverConnectionList, NULL },
301 { _T("Servers"), CT_STRING_LIST, ',', 0, 0, 0, &m_pszServerList, NULL },
302 { _T("SessionIdleTimeout"), CT_LONG, 0, 0, 0, 0, &g_dwIdleTimeout, NULL },
303 { _T("SessionAgentPort"), CT_WORD, 0, 0, 0, 0, &g_sessionAgentPort, NULL },
304 { _T("SharedSecret"), CT_STRING, 0, 0, MAX_SECRET_LENGTH, 0, g_szSharedSecret, NULL },
305 { _T("SNMPTimeout"), CT_LONG, 0, 0, 0, 0, &g_snmpTimeout, NULL },
306 { _T("SNMPTrapListenAddress"), CT_STRING, 0, 0, MAX_PATH, 0, &g_szSNMPTrapListenAddress, NULL },
307 { _T("SNMPTrapPort"), CT_WORD, 0, 0, 0, 0, &g_snmpTrapPort, NULL },
308 { _T("StartupDelay"), CT_LONG, 0, 0, 0, 0, &g_dwStartupDelay, NULL },
309 { _T("SubAgent"), CT_STRING_LIST, '\n', 0, 0, 0, &m_pszSubagentList, NULL },
310 { _T("SyslogListenPort"), CT_WORD, 0, 0, 0, 0, &g_syslogListenPort, NULL },
311 { _T("SystemName"), CT_STRING, 0, 0, MAX_OBJECT_NAME, 0, g_systemName, NULL },
312 { _T("TimeOut"), CT_IGNORE, 0, 0, 0, 0, NULL, NULL },
313 { _T("WaitForProcess"), CT_STRING, 0, 0, MAX_PATH, 0, s_processToWaitFor, NULL },
314 { _T("ZoneId"), CT_LONG, 0, 0, 0, 0, &g_zoneUIN, NULL }, // for backward compatibility
315 { _T("ZoneUIN"), CT_LONG, 0, 0, 0, 0, &g_zoneUIN, NULL },
316 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL, NULL }
317 };
318
319 /**
320 * Help text
321 */
322 static TCHAR m_szHelpText[] =
323 _T("Usage: nxagentd [options]\n")
324 _T("Where valid options are:\n")
325 _T(" -c <file> : Use configuration file <file> (default ") AGENT_DEFAULT_CONFIG _T(")\n")
326 _T(" -C : Load configuration file, dump resulting configuration, and exit\n")
327 _T(" -d : Run as daemon/service\n")
328 _T(" -D <level> : Set debug level (0..9)\n")
329 #ifdef _WIN32
330 _T(" -e <name> : Windows event source name\n")
331 #endif
332 _T(" -f : Run in foreground\n")
333 #if !defined(_WIN32)
334 _T(" -g <gid> : Change group ID to <gid> after start\n")
335 #endif
336 _T(" -G <name> : Use alternate global section <name> in configuration file\n")
337 _T(" -h : Display help and exit\n")
338 #ifdef _WIN32
339 _T(" -H : Hide agent's window when in standalone mode\n")
340 _T(" -i : Installed Windows service must be interactive\n")
341 _T(" -I : Install Windows service\n")
342 #endif
343 _T(" -K : Shutdown all connected external sub-agents\n")
344 _T(" -M <addr> : Download config from management server <addr>\n")
345 #ifdef _WIN32
346 _T(" -n <name> : Service name\n")
347 _T(" -N <name> : Service display name\n")
348 #endif
349 #if !defined(_WIN32)
350 _T(" -p : Path to pid file (default: /var/run/nxagentd.pid)\n")
351 #endif
352 _T(" -P <text> : Set platform suffix to <text>\n")
353 _T(" -r <addr> : Register agent on management server <addr>\n")
354 #ifdef _WIN32
355 _T(" -R : Remove Windows service\n")
356 _T(" -s : Start Windows servive\n")
357 _T(" -S : Stop Windows service\n")
358 #endif
359 #if !defined(_WIN32)
360 _T(" -u <uid> : Chhange user ID to <uid> after start\n")
361 #endif
362 _T(" -v : Display version and exit\n")
363 _T("\n");
364
365 /**
366 * Server info: constructor
367 */
368 ServerInfo::ServerInfo(const TCHAR *name, bool control, bool master)
369 {
370 #ifdef UNICODE
371 m_name = MBStringFromWideString(name);
372 #else
373 m_name = strdup(name);
374 #endif
375
376 char *p = strchr(m_name, '/');
377 if (p != NULL)
378 {
379 *p = 0;
380 p++;
381 m_address = InetAddress::resolveHostName(m_name);
382 if (m_address.isValid())
383 {
384 int bits = strtol(p, NULL, 10);
385 if ((bits >= 0) && (bits <= ((m_address.getFamily() == AF_INET) ? 32 : 128)))
386 m_address.setMaskBits(bits);
387 }
388 m_redoResolve = false;
389 }
390 else
391 {
392 m_address = InetAddress::resolveHostName(m_name);
393 m_redoResolve = true;
394 }
395
396 m_control = control;
397 m_master = master;
398 m_lastResolveTime = time(NULL);
399 m_mutex = MutexCreate();
400 }
401
402 /**
403 * Server info: destructor
404 */
405 ServerInfo::~ServerInfo()
406 {
407 safe_free(m_name);
408 MutexDestroy(m_mutex);
409 }
410
411 /**
412 * Server info: resolve hostname if needed
413 */
414 void ServerInfo::resolve()
415 {
416 time_t now = time(NULL);
417 time_t age = now - m_lastResolveTime;
418 if ((age >= 3600) || ((age > 300) && !m_address.isValid()))
419 {
420 m_address = InetAddress::resolveHostName(m_name);
421 m_lastResolveTime = now;
422 }
423 }
424
425 /**
426 * Server info: match address
427 */
428 bool ServerInfo::match(const InetAddress &addr)
429 {
430 MutexLock(m_mutex);
431 if (m_redoResolve)
432 resolve();
433 bool result = m_address.isValid() ? m_address.contain(addr) : false;
434 MutexUnlock(m_mutex);
435 return result;
436 }
437
438 #ifdef _WIN32
439
440 /**
441 * Get our own console window handle (an alternative to Microsoft's GetConsoleWindow)
442 */
443 static HWND GetConsoleHWND()
444 {
445 DWORD cpid = GetCurrentProcessId();
446 HWND hWnd = NULL;
447 while(true)
448 {
449 hWnd = FindWindowEx(NULL, hWnd, _T("ConsoleWindowClass"), NULL);
450 if (hWnd == NULL)
451 break;
452
453 DWORD wpid;
454 GetWindowThreadProcessId(hWnd, &wpid);
455 if (cpid == wpid)
456 break;
457 }
458
459 return hWnd;
460 }
461
462 /**
463 * Get proc address and write log file
464 */
465 static FARPROC GetProcAddressAndLog(HMODULE hModule, LPCSTR procName)
466 {
467 FARPROC ptr;
468
469 ptr = GetProcAddress(hModule, procName);
470 if ((ptr == NULL) && (g_dwFlags & AF_LOG_UNRESOLVED_SYMBOLS))
471 nxlog_write(MSG_NO_FUNCTION, EVENTLOG_WARNING_TYPE, "s", procName);
472 return ptr;
473 }
474
475 /**
476 * Shutdown thread (created by H_RestartAgent)
477 */
478 static THREAD_RESULT THREAD_CALL ShutdownThread(void *pArg)
479 {
480 DebugPrintf(1, _T("Shutdown thread started"));
481 Shutdown();
482 ExitProcess(0);
483 return THREAD_OK; // Never reached
484 }
485
486 #endif /* _WIN32 */
487
488 /**
489 * Restart agent
490 */
491 LONG RestartAgent()
492 {
493 nxlog_debug(1, _T("RestartAgent() called"));
494
495 RestartExtSubagents();
496
497 TCHAR platformSuffixOption[MAX_PSUFFIX_LENGTH + 16];
498 if (g_szPlatformSuffix[0] != 0)
499 {
500 _sntprintf(platformSuffixOption, MAX_PSUFFIX_LENGTH + 16, _T("-P \"%s\" "), g_szPlatformSuffix);
501 }
502 else
503 {
504 platformSuffixOption[0] = 0;
505 }
506
507 const TCHAR *configSection = g_config->getAlias(_T("agent"));
508 TCHAR configSectionOption[256];
509 if ((configSection != NULL) && (*configSection != 0))
510 {
511 _sntprintf(configSectionOption, 256, _T("-G %s "), configSection);
512 }
513 else
514 {
515 configSectionOption[0] = 0;
516 }
517
518 TCHAR szCmdLine[4096];
519 #ifdef _WIN32
520 _sntprintf(szCmdLine, 4096, _T("\"%s\" -c \"%s\" -n \"%s\" -e \"%s\" %s%s%s%s%s-D %d %s%s-X %u"), s_executableName,
521 g_szConfigFile, g_windowsServiceName, g_windowsEventSourceName,
522 (g_dwFlags & AF_DAEMON) ? _T("-d ") : _T(""),
523 (g_dwFlags & AF_HIDE_WINDOW) ? _T("-H ") : _T(""),
524 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T("-M ") : _T(""),
525 (g_dwFlags & AF_CENTRAL_CONFIG) ? g_szConfigServer : _T(""),
526 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T(" ") : _T(""),
527 s_debugLevel, platformSuffixOption, configSectionOption,
528 (g_dwFlags & AF_DAEMON) ? 0 : GetCurrentProcessId());
529 DebugPrintf(1, _T("Restarting agent with command line '%s'"), szCmdLine);
530
531 DWORD dwResult;
532 STARTUPINFO si;
533 PROCESS_INFORMATION pi;
534
535 // Fill in process startup info structure
536 memset(&si, 0, sizeof(STARTUPINFO));
537 si.cb = sizeof(STARTUPINFO);
538
539 // Create new process
540 if (!CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE,
541 (g_dwFlags & AF_DAEMON) ? (CREATE_NO_WINDOW | DETACHED_PROCESS) : (CREATE_NEW_CONSOLE),
542 NULL, NULL, &si, &pi))
543 {
544 nxlog_write(MSG_CREATE_PROCESS_FAILED, EVENTLOG_ERROR_TYPE, "se", szCmdLine, GetLastError());
545 dwResult = ERR_EXEC_FAILED;
546 }
547 else
548 {
549 // Close all handles
550 CloseHandle(pi.hThread);
551 CloseHandle(pi.hProcess);
552 dwResult = ERR_SUCCESS;
553 }
554 if ((dwResult == ERR_SUCCESS) && (!(g_dwFlags & AF_DAEMON)))
555 {
556 if (g_dwFlags & AF_HIDE_WINDOW)
557 {
558 ConditionSet(s_shutdownCondition);
559 }
560 else
561 {
562 ThreadCreate(ShutdownThread, 0, NULL);
563 }
564 }
565 return dwResult;
566 #else
567 _sntprintf(szCmdLine, 4096, _T("\"%s\" -c \"%s\" %s%s%s%s-D %d %s%s-X %lu"), s_executableName,
568 g_szConfigFile, (g_dwFlags & AF_DAEMON) ? _T("-d ") : _T(""),
569 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T("-M ") : _T(""),
570 (g_dwFlags & AF_CENTRAL_CONFIG) ? g_szConfigServer : _T(""),
571 (g_dwFlags & AF_CENTRAL_CONFIG) ? _T(" ") : _T(""),
572 (int)s_debugLevel, platformSuffixOption, configSectionOption,
573 (unsigned long)s_pid);
574 DebugPrintf(1, _T("Restarting agent with command line '%s'"), szCmdLine);
575 return ExecuteCommand(szCmdLine, NULL, NULL);
576 #endif
577 }
578
579 /**
580 * Handler for Agent.Restart action
581 */
582 static LONG H_RestartAgent(const TCHAR *action, StringList *args, const TCHAR *data, AbstractCommSession *session)
583 {
584 return RestartAgent();
585 }
586
587 /**
588 * This function writes message from subagent to agent's log
589 */
590 static void WriteSubAgentMsg(int logLevel, int debugLevel, const TCHAR *pszMsg)
591 {
592 if (logLevel == EVENTLOG_DEBUG_TYPE)
593 {
594 if (debugLevel <= (int)s_debugLevel)
595 nxlog_write(MSG_DEBUG, EVENTLOG_DEBUG_TYPE, "s", pszMsg);
596 }
597 else
598 {
599 nxlog_write(MSG_SUBAGENT_MSG, logLevel, "s", pszMsg);
600 }
601 }
602
603 /**
604 * Signal handler for UNIX platforms
605 */
606 #if !defined(_WIN32)
607
608 static THREAD_RESULT THREAD_CALL SignalHandler(void *pArg)
609 {
610 sigset_t signals;
611 sigemptyset(&signals);
612 sigaddset(&signals, SIGTERM);
613 sigaddset(&signals, SIGINT);
614 sigaddset(&signals, SIGSEGV);
615 sigaddset(&signals, SIGHUP);
616 sigaddset(&signals, SIGUSR1);
617 sigaddset(&signals, SIGUSR2);
618 #if !defined(__sun) && !defined(_AIX) && !defined(__hpux)
619 sigaddset(&signals, SIGPIPE);
620 #endif
621
622 while(true)
623 {
624 int nSignal;
625 if (sigwait(&signals, &nSignal) == 0)
626 {
627 switch(nSignal)
628 {
629 case SIGTERM:
630 case SIGINT:
631 goto stop_handler;
632 case SIGSEGV:
633 abort();
634 break;
635 default:
636 break;
637 }
638 }
639 else
640 {
641 ThreadSleepMs(100);
642 }
643 }
644
645 stop_handler:
646 sigprocmask(SIG_UNBLOCK, &signals, NULL);
647 return THREAD_OK;
648 }
649
650 #endif
651
652 /**
653 * Load platform subagent
654 */
655 static void LoadPlatformSubagent()
656 {
657 #ifdef _WIN32
658 LoadSubAgent(_T("WINNT.NSM"));
659 #else
660 #if HAVE_SYS_UTSNAME_H && !defined(_STATIC_AGENT)
661 struct utsname un;
662 TCHAR szName[MAX_PATH];
663 int i;
664
665 if (uname(&un) != -1)
666 {
667 // Convert system name to lowercase
668 for(i = 0; un.sysname[i] != 0; i++)
669 un.sysname[i] = tolower(un.sysname[i]);
670 if (!strcmp(un.sysname, "hp-ux"))
671 strcpy(un.sysname, "hpux");
672 _sntprintf(szName, MAX_PATH, _T("%hs.nsm"), un.sysname);
673 LoadSubAgent(szName);
674 }
675 #endif
676 #endif
677 }
678
679 /**
680 * Send file to server (subagent API)
681 */
682 static bool SendFileToServer(void *session, UINT32 requestId, const TCHAR *file, long offset, bool allowCompression)
683 {
684 if (session == NULL)
685 return false;
686 return ((CommSession *)session)->sendFile(requestId, file, offset, allowCompression);
687 }
688
689 /**
690 * Parser server list
691 */
692 static void ParseServerList(TCHAR *serverList, bool isControl, bool isMaster)
693 {
694 TCHAR *pItem, *pEnd;
695
696 for(pItem = pEnd = serverList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
697 {
698 pEnd = _tcschr(pItem, _T(','));
699 if (pEnd != NULL)
700 *pEnd = 0;
701 StrStrip(pItem);
702
703 g_serverList.add(new ServerInfo(pItem, isControl, isMaster));
704 }
705 free(serverList);
706 }
707
708 /**
709 * Agent initialization
710 */
711 BOOL Initialize()
712 {
713 TCHAR *pItem, *pEnd;
714
715 if (s_debugLevel == (UINT32)NXCONFIG_UNINITIALIZED_VALUE)
716 s_debugLevel = 0;
717
718 // Open log file
719 if (!nxlog_open((g_dwFlags & AF_USE_SYSLOG) ? NXAGENTD_SYSLOG_NAME : g_szLogFile,
720 ((g_dwFlags & AF_USE_SYSLOG) ? NXLOG_USE_SYSLOG : 0) |
721 ((g_dwFlags & AF_BACKGROUND_LOG_WRITER) ? NXLOG_BACKGROUND_WRITER : 0) |
722 ((g_dwFlags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
723 _T("NXAGENTD.EXE"),
724 #ifdef _WIN32
725 0, NULL, MSG_DEBUG, MSG_DEBUG_TAG, MSG_SUBAGENT_MSG))
726 #else
727 g_dwNumMessages, g_szMessages, MSG_DEBUG, MSG_DEBUG_TAG, MSG_SUBAGENT_MSG))
728 #endif
729 {
730 //TODO: set flag that log have been opened with errors
731 s_debugLevel = 1;
732 g_failFlags |= FAIL_OPEN_LOG;
733 nxlog_open(NXAGENTD_SYSLOG_NAME, NXLOG_USE_SYSLOG |
734 ((g_dwFlags & AF_BACKGROUND_LOG_WRITER) ? NXLOG_BACKGROUND_WRITER : 0) |
735 ((g_dwFlags & AF_DAEMON) ? 0 : NXLOG_PRINT_TO_STDOUT),
736 _T("NXAGENTD.EXE"),
737 #ifdef _WIN32
738 0, NULL, MSG_DEBUG, MSG_DEBUG_TAG, MSG_SUBAGENT_MSG);
739 #else
740 g_dwNumMessages, g_szMessages, MSG_DEBUG, MSG_DEBUG_TAG, MSG_SUBAGENT_MSG);
741 #endif
742 _ftprintf(stderr, _T("ERROR: Cannot open log file, logs will be written to syslog with debug level 1\n"));
743
744 }
745 else
746 {
747 if (!(g_dwFlags & AF_USE_SYSLOG))
748 {
749 if (!nxlog_set_rotation_policy((int)s_logRotationMode, s_maxLogSize, (int)s_logHistorySize, s_dailyLogFileSuffix))
750 if (!(g_dwFlags & AF_DAEMON))
751 _tprintf(_T("WARNING: cannot set log rotation policy; using default values\n"));
752 }
753 }
754 nxlog_write(MSG_AGENT_VERSION, NXLOG_INFO, "s", NETXMS_BUILD_TAG);
755 nxlog_write(MSG_USE_CONFIG_D, NXLOG_INFO, "s", g_szConfigIncludeDir);
756 nxlog_write(MSG_DEBUG_LEVEL, NXLOG_INFO, "d", s_debugLevel);
757
758 int numTags = 0, lvl = 0;
759 TCHAR tagBuffer[254], lvlBuffer[2];
760 TCHAR const *ptr;
761
762 _tprintf(_T("Taglist: %s\n"), s_debugTags);
763 TCHAR **tagList = SplitString(s_debugTags, _T(','), &numTags);
764 if (tagList != NULL)
765 {
766 for(int i = 0; i < numTags; i++)
767 {
768 ptr = ExtractWord(tagList[i], tagBuffer);
769 ExtractWord(ptr, lvlBuffer);
770 lvl = _tcstol(lvlBuffer, NULL, 0);
771
772 if(lvl != 0 && tagBuffer != NULL)
773 nxlog_set_debug_level_tag(tagBuffer, lvl);
774 }
775 }
776
777 free(s_debugTags);
778 free(tagList);
779
780 nxlog_set_debug_level(s_debugLevel);
781
782 if (_tcscmp(g_masterAgent, _T("not_set")))
783 {
784 g_dwFlags |= AF_SUBAGENT_LOADER;
785 DebugPrintf(1, _T("Switched to external subagent loader mode, master agent address is %s"), g_masterAgent);
786 }
787
788 DebugPrintf(1, _T("Data directory: %s"), g_szDataDirectory);
789 CreateFolder(g_szDataDirectory);
790
791 DebugPrintf(1, _T("File store: %s"), g_szFileStore);
792 CreateFolder(g_szFileStore);
793
794 #ifndef _WIN32
795 nxlog_debug(2, _T("Effective user ID %d"), (int)geteuid());
796 nxlog_debug(2, _T("Effective group ID %d"), (int)getegid());
797 #endif
798
799 // Initialize log parser policy folder
800 TCHAR tail = g_szDataDirectory[_tcslen(g_szDataDirectory) - 1];
801 _sntprintf(g_szLogParserDirectory, MAX_PATH, _T("%s%s%s"), g_szDataDirectory,
802 ((tail != '\\') && (tail != '/')) ? FS_PATH_SEPARATOR : _T(""),
803 LOGPARSER_AP_FOLDER FS_PATH_SEPARATOR);
804 nxlog_debug(6, _T("Log parser policy directory: %s"), g_szLogParserDirectory);
805 CreateFolder(g_szLogParserDirectory);
806
807 // Initialize certificate directory
808 _sntprintf(g_certificateDirectory, MAX_PATH, _T("%s%scertificates") FS_PATH_SEPARATOR, g_szDataDirectory,
809 ((tail != '\\') && (tail != '/')) ? FS_PATH_SEPARATOR : _T(""));
810 nxlog_debug(6, _T("Certificate directory: %s"), g_certificateDirectory);
811 CreateFolder(g_certificateDirectory);
812
813 nxlog_debug(6, _T("Configuration policy directory: %s"), g_szConfigPolicyDir);
814
815 #ifdef _WIN32
816 WSADATA wsaData;
817 int wrc = WSAStartup(MAKEWORD(2, 2), &wsaData);
818 if (wrc != 0)
819 {
820 nxlog_write(MSG_WSASTARTUP_FAILED, NXLOG_ERROR, "e", wrc);
821 return FALSE;
822 }
823 #endif
824
825 // Initialize API for subagents
826 s_subAgentsStopCondition = ConditionCreate(TRUE);
827 InitSubAgentAPI(WriteSubAgentMsg, SendTrap, SendTrap, EnumerateSessions, FindServerSession,
828 SendFileToServer, PushData, GetLocalDatabaseHandle, s_subAgentsStopCondition, g_szDataDirectory);
829 nxlog_debug(1, _T("Subagent API initialized"));
830
831 // Initialize cryptografy
832 if (!InitCryptoLib(s_enabledCiphers))
833 {
834 nxlog_write(MSG_INIT_CRYPTO_FAILED, EVENTLOG_ERROR_TYPE, "e", WSAGetLastError());
835 return FALSE;
836 }
837
838 // Initialize libssl - it is not used by core agent
839 // but may be needed by some subagents. Allowing first load of libssl by
840 // subagent via dlopen() may lead to undesired side effects
841 #ifdef _WITH_ENCRYPTION
842 SSL_library_init();
843 SSL_load_error_strings();
844 #endif
845
846 DBInit(MSG_DB_LIBRARY, MSG_SQL_ERROR);
847
848 if (!OpenLocalDatabase())
849 {
850 nxlog_write(MSG_LOCAL_DB_OPEN_FAILED, NXLOG_ERROR, NULL);
851 }
852
853 // Set system name to host name if not set in config
854 if (g_systemName[0] == 0)
855 GetLocalHostName(g_systemName, MAX_OBJECT_NAME, false);
856 nxlog_debug(2, _T("Using system name \"%s\""), g_systemName);
857
858 if (!(g_dwFlags & AF_SUBAGENT_LOADER))
859 {
860 g_commThreadPool = ThreadPoolCreate(2, 32, _T("COMM"));
861 if (g_dwFlags & AF_ENABLE_SNMP_PROXY)
862 {
863 g_snmpProxyThreadPool = ThreadPoolCreate(2, 128, _T("SNMPPROXY"));
864 }
865 InitSessionList();
866
867 // Initialize built-in parameters
868 if (!InitParameterList())
869 return FALSE;
870
871 // Parse outgoing server connection (tunnel) list
872 if (s_serverConnectionList != NULL)
873 ParseTunnelList(s_serverConnectionList);
874
875 // Parse server lists
876 if (m_pszMasterServerList != NULL)
877 ParseServerList(m_pszMasterServerList, true, true);
878 if (m_pszControlServerList != NULL)
879 ParseServerList(m_pszControlServerList, true, false);
880 if (m_pszServerList != NULL)
881 ParseServerList(m_pszServerList, false, false);
882
883 // Add built-in actions
884 AddAction(_T("Agent.Restart"), AGENT_ACTION_SUBAGENT, NULL, H_RestartAgent, _T("CORE"), _T("Restart agent"));
885
886 // Load platform subagents
887 #if !defined(_WIN32)
888 InitStaticSubagents();
889 #endif
890 if (g_dwFlags & AF_ENABLE_AUTOLOAD)
891 {
892 LoadPlatformSubagent();
893 }
894 }
895
896 // Wait for external process if requested
897 if (s_processToWaitFor[0] != 0)
898 {
899 DebugPrintf(1, _T("Waiting for process %s"), s_processToWaitFor);
900 if (!WaitForProcess(s_processToWaitFor))
901 nxlog_write(MSG_WAITFORPROCESS_FAILED, EVENTLOG_ERROR_TYPE, "s", s_processToWaitFor);
902 }
903
904 // Load other subagents
905 if (m_pszSubagentList != NULL)
906 {
907 for(pItem = pEnd = m_pszSubagentList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
908 {
909 pEnd = _tcschr(pItem, _T('\n'));
910 if (pEnd != NULL)
911 *pEnd = 0;
912 Trim(pItem);
913 LoadSubAgent(pItem);
914 }
915 free(m_pszSubagentList);
916 }
917
918 // Parse action list
919 if (m_pszActionList != NULL)
920 {
921 for(pItem = pEnd = m_pszActionList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
922 {
923 pEnd = _tcschr(pItem, _T('\n'));
924 if (pEnd != NULL)
925 *pEnd = 0;
926 Trim(pItem);
927 if (!AddActionFromConfig(pItem, FALSE))
928 nxlog_write(MSG_ADD_ACTION_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
929 }
930 free(m_pszActionList);
931 }
932 if (m_pszShellActionList != NULL)
933 {
934 for(pItem = pEnd = m_pszShellActionList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
935 {
936 pEnd = _tcschr(pItem, _T('\n'));
937
938 if (pEnd != NULL)
939 *pEnd = 0;
940 Trim(pItem);
941 if (!AddActionFromConfig(pItem, TRUE))
942 nxlog_write(MSG_ADD_ACTION_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
943 }
944 free(m_pszShellActionList);
945 }
946
947 // Parse external parameters list
948 if (s_externalParametersConfig != NULL)
949 {
950 for(pItem = pEnd = s_externalParametersConfig; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
951 {
952 pEnd = _tcschr(pItem, _T('\n'));
953 if (pEnd != NULL)
954 *pEnd = 0;
955 Trim(pItem);
956 if (!AddExternalParameter(pItem, FALSE, FALSE))
957 nxlog_write(MSG_ADD_EXT_PARAM_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
958 }
959 free(s_externalParametersConfig);
960 }
961 if (s_externalShellExecParametersConfig != NULL)
962 {
963 for(pItem = pEnd = s_externalShellExecParametersConfig; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
964 {
965 pEnd = _tcschr(pItem, _T('\n'));
966 if (pEnd != NULL)
967 *pEnd = 0;
968 Trim(pItem);
969 if (!AddExternalParameter(pItem, TRUE, FALSE))
970 nxlog_write(MSG_ADD_EXT_PARAM_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
971 }
972 free(s_externalShellExecParametersConfig);
973 }
974
975 // Parse external lists
976 if (s_externalListsConfig != NULL)
977 {
978 for(pItem = pEnd = s_externalListsConfig; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
979 {
980 pEnd = _tcschr(pItem, _T('\n'));
981 if (pEnd != NULL)
982 *pEnd = 0;
983 Trim(pItem);
984 if (!AddExternalParameter(pItem, FALSE, TRUE))
985 nxlog_write(MSG_ADD_EXT_LIST_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
986 }
987 free(s_externalListsConfig);
988 }
989
990 // Parse external tables
991 if (s_externalTablesConfig != NULL)
992 {
993 for(pItem = pEnd = s_externalTablesConfig; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
994 {
995 pEnd = _tcschr(pItem, _T('\n'));
996 if (pEnd != NULL)
997 *pEnd = 0;
998 Trim(pItem);
999 if (!AddExternalTable(pItem, false))
1000 nxlog_write(MSG_ADD_EXT_TABLE_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
1001 }
1002 free(s_externalTablesConfig);
1003 }
1004
1005 // Parse external parameters providers list
1006 if (s_externalParameterProvidersConfig != NULL)
1007 {
1008 for(pItem = pEnd = s_externalParameterProvidersConfig; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
1009 {
1010 pEnd = _tcschr(pItem, _T('\n'));
1011 if (pEnd != NULL)
1012 *pEnd = 0;
1013 Trim(pItem);
1014 if (!AddParametersProvider(pItem))
1015 nxlog_write(MSG_ADD_PARAM_PROVIDER_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
1016 }
1017 free(s_externalParameterProvidersConfig);
1018 }
1019
1020 if (!(g_dwFlags & AF_SUBAGENT_LOADER))
1021 {
1022 // Parse external subagents list
1023 if (!(g_dwFlags & AF_SUBAGENT_LOADER) && (s_externalSubAgentsList != NULL))
1024 {
1025 for(pItem = pEnd = s_externalSubAgentsList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
1026 {
1027 pEnd = _tcschr(pItem, _T('\n'));
1028 if (pEnd != NULL)
1029 *pEnd = 0;
1030 Trim(pItem);
1031 if (!AddExternalSubagent(pItem))
1032 nxlog_write(MSG_ADD_EXTERNAL_SUBAGENT_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
1033 }
1034 free(s_externalSubAgentsList);
1035 }
1036
1037 // Additional external subagents implicitly defined by EXT:* config sections
1038 ObjectArray<ConfigEntry> *entries = g_config->getSubEntries(_T("/"), _T("EXT:*"));
1039 for(int i = 0; i < entries->size(); i++)
1040 {
1041 const TCHAR *name = entries->get(i)->getName() + 4;
1042 if (!AddExternalSubagent(name))
1043 nxlog_write(MSG_ADD_EXTERNAL_SUBAGENT_FAILED, EVENTLOG_WARNING_TYPE, "s", name);
1044 }
1045 delete entries;
1046
1047 // Parse application agents list
1048 if (!(g_dwFlags & AF_SUBAGENT_LOADER) && (s_appAgentsList != NULL))
1049 {
1050 for(pItem = pEnd = s_appAgentsList; pEnd != NULL && *pItem != 0; pItem = pEnd + 1)
1051 {
1052 pEnd = _tcschr(pItem, _T('\n'));
1053 if (pEnd != NULL)
1054 *pEnd = 0;
1055 Trim(pItem);
1056 RegisterApplicationAgent(pItem);
1057 }
1058 free(s_appAgentsList);
1059 }
1060 }
1061
1062 ThreadSleep(1);
1063
1064 // If StartupDelay is greater than zero, then wait
1065 if (g_dwStartupDelay > 0)
1066 {
1067 if (g_dwFlags & AF_DAEMON)
1068 {
1069 ThreadSleep(g_dwStartupDelay);
1070 }
1071 else
1072 {
1073 UINT32 i;
1074
1075 _tprintf(_T("XXXXXX%*s]\rWAIT ["), g_dwStartupDelay, _T(" "));
1076 fflush(stdout);
1077 for(i = 0; i < g_dwStartupDelay; i++)
1078 {
1079 ThreadSleep(1);
1080 _puttc(_T('.'), stdout);
1081 fflush(stdout);
1082 }
1083 _tprintf(_T("\n"));
1084 }
1085 }
1086
1087 StartParamProvidersPoller();
1088
1089 // Agent start time
1090 g_tmAgentStartTime = time(NULL);
1091
1092 s_eventSenderThread = ThreadCreateEx(TrapSender, 0, NULL);
1093
1094 if (g_dwFlags & AF_ENABLE_SNMP_TRAP_PROXY)
1095 {
1096 s_snmpTrapSenderThread = ThreadCreateEx(SNMPTrapSender, 0, NULL);
1097 s_snmpTrapReceiverThread = ThreadCreateEx(SNMPTrapReceiver, 0, NULL);
1098 }
1099
1100 if (g_dwFlags & AF_ENABLE_SYSLOG_PROXY)
1101 {
1102 s_syslogSenderThread = ThreadCreateEx(SyslogSender, 0, NULL);
1103 s_syslogReceiverThread = ThreadCreateEx(SyslogReceiver, 0, NULL);
1104 }
1105
1106 if (g_dwFlags & AF_SUBAGENT_LOADER)
1107 {
1108 s_masterAgentListenerThread = ThreadCreateEx(MasterAgentListener, 0, NULL);
1109 }
1110 else
1111 {
1112 // Start network listener and session watchdog
1113 StartLocalDataCollector();
1114 s_listenerThread = ThreadCreateEx(ListenerThread, 0, NULL);
1115 s_sessionWatchdogThread = ThreadCreateEx(SessionWatchdog, 0, NULL);
1116 StartPushConnector();
1117 StartStorageDiscoveryConnector();
1118 StartSessionAgentConnector();
1119 if (g_dwFlags & AF_ENABLE_CONTROL_CONNECTOR)
1120 {
1121 StartControlConnector();
1122 }
1123
1124 if (g_dwFlags & AF_REGISTER)
1125 {
1126 RegisterOnServer(g_szRegistrar);
1127 }
1128
1129 s_tunnelManagerThread = ThreadCreateEx(TunnelManager, 0, NULL);
1130 }
1131
1132 #if defined(_WIN32)
1133 s_shutdownCondition = ConditionCreate(TRUE);
1134 #endif
1135 ThreadSleep(1);
1136
1137 // Start watchdog process
1138 if (!(g_dwFlags & AF_SUBAGENT_LOADER))
1139 {
1140 if (g_dwFlags & AF_ENABLE_WATCHDOG)
1141 StartWatchdog();
1142 }
1143
1144 // Delete file used for upgrade if exists
1145 TCHAR upgradeFileName[MAX_PATH];
1146 ReadRegistryAsString(_T("upgrade.file"), upgradeFileName, MAX_PATH, _T(""));
1147 if(upgradeFileName[0] != 0)
1148 {
1149 _tremove(upgradeFileName);
1150 DeleteRegistryEntry(_T("upgrade.file"));
1151 }
1152
1153 //Update policy inventory according to files that exist on file system
1154 UpdatePolicyInventory();
1155
1156 return TRUE;
1157 }
1158
1159 /**
1160 * Shutdown agent
1161 */
1162 void Shutdown()
1163 {
1164 DebugPrintf(2, _T("Shutdown() called"));
1165 if (g_dwFlags & AF_ENABLE_WATCHDOG)
1166 StopWatchdog();
1167
1168 g_dwFlags |= AF_SHUTDOWN;
1169 ConditionSet(s_subAgentsStopCondition);
1170
1171 if (g_dwFlags & AF_SUBAGENT_LOADER)
1172 {
1173 // TODO: shall we inform master agent listener about shutdown?
1174 //ThreadJoin(s_masterAgentListenerThread);
1175 }
1176 else
1177 {
1178 ShutdownLocalDataCollector();
1179 ShutdownTrapSender();
1180 ThreadJoin(s_sessionWatchdogThread);
1181 ThreadJoin(s_listenerThread);
1182 ThreadJoin(s_tunnelManagerThread);
1183 }
1184 ThreadJoin(s_eventSenderThread);
1185 if (g_dwFlags & AF_ENABLE_SNMP_TRAP_PROXY)
1186 {
1187 ShutdownSNMPTrapSender();
1188 ThreadJoin(s_snmpTrapReceiverThread);
1189 ThreadJoin(s_snmpTrapSenderThread);
1190 }
1191 if (g_dwFlags & AF_ENABLE_SYSLOG_PROXY)
1192 {
1193 ShutdownSyslogSender();
1194 ThreadJoin(s_syslogReceiverThread);
1195 ThreadJoin(s_syslogSenderThread);
1196 }
1197
1198 DestroySessionList();
1199 MsgWaitQueue::shutdown();
1200
1201 if (g_dwFlags & AF_ENABLE_SNMP_PROXY)
1202 {
1203 ThreadPoolDestroy(g_snmpProxyThreadPool);
1204 }
1205 ThreadPoolDestroy(g_commThreadPool);
1206
1207 UnloadAllSubAgents();
1208 CloseLocalDatabase();
1209 nxlog_write(MSG_AGENT_STOPPED, EVENTLOG_INFORMATION_TYPE, NULL);
1210 nxlog_close();
1211
1212 delete g_config;
1213
1214 // Notify main thread about shutdown
1215 #ifdef _WIN32
1216 ConditionSet(s_shutdownCondition);
1217 #endif
1218
1219 // Remove PID file
1220 #if !defined(_WIN32)
1221 _tremove(g_szPidFile);
1222 #endif
1223 }
1224
1225 /**
1226 * Common Main()
1227 */
1228 void Main()
1229 {
1230 nxlog_write(MSG_AGENT_STARTED, NXLOG_INFO, NULL);
1231
1232 if (g_dwFlags & AF_DAEMON)
1233 {
1234 #if defined(_WIN32)
1235 ConditionWait(s_shutdownCondition, INFINITE);
1236 #else
1237 StartMainLoop(SignalHandler, NULL);
1238 #endif
1239 }
1240 else
1241 {
1242 #if defined(_WIN32)
1243 if (g_dwFlags & AF_HIDE_WINDOW)
1244 {
1245 HWND hWnd;
1246
1247 hWnd = GetConsoleHWND();
1248 if (hWnd != NULL)
1249 ShowWindow(hWnd, SW_HIDE);
1250 ConditionWait(s_shutdownCondition, INFINITE);
1251 ThreadSleep(1);
1252 }
1253 else
1254 {
1255 _tprintf(_T("Agent running. Press ESC to shutdown.\n"));
1256 while(1)
1257 {
1258 if (_getch() == 27)
1259 break;
1260 }
1261 _tprintf(_T("Agent shutting down...\n"));
1262 Shutdown();
1263 }
1264 #else
1265 _tprintf(_T("Agent running. Press Ctrl+C to shutdown.\n"));
1266 StartMainLoop(SignalHandler, NULL);
1267 _tprintf(_T("\nStopping agent...\n"));
1268 #endif
1269 }
1270 }
1271
1272 /**
1273 * Do necessary actions on agent restart
1274 */
1275 static void DoRestartActions(UINT32 dwOldPID)
1276 {
1277 #if defined(_WIN32)
1278 if (dwOldPID == 0)
1279 {
1280 // Service
1281 StopAgentService();
1282 WaitForService(SERVICE_STOPPED);
1283 StartAgentService();
1284 ExitProcess(0);
1285 }
1286 else
1287 {
1288 HANDLE hProcess;
1289
1290 hProcess = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, dwOldPID);
1291 if (hProcess != NULL)
1292 {
1293 if (WaitForSingleObject(hProcess, 60000) == WAIT_TIMEOUT)
1294 {
1295 TerminateProcess(hProcess, 0);
1296 }
1297 CloseHandle(hProcess);
1298 }
1299 }
1300 #else
1301 int i;
1302
1303 kill(dwOldPID, SIGTERM);
1304 for(i = 0; i < 30; i++)
1305 {
1306 sleep(2);
1307 if (kill(dwOldPID, SIGCONT) == -1)
1308 break;
1309 }
1310
1311 // Kill previous instance of agent if it's still running
1312 if (i == 30)
1313 kill(dwOldPID, SIGKILL);
1314 #endif
1315 }
1316
1317 /**
1318 * Create configuration file
1319 */
1320 static int CreateConfig(const char *pszServer, const char *pszLogFile, const char *pszFileStore,
1321 const char *configIncludeDir, int iNumSubAgents, char **ppszSubAgentList)
1322 {
1323 FILE *fp;
1324 time_t currTime;
1325 int i;
1326
1327 if (_taccess(g_szConfigFile, 0) == 0)
1328 return 0; // File already exist, we shouldn't overwrite it
1329
1330 fp = _tfopen(g_szConfigFile, _T("w"));
1331 if (fp != NULL)
1332 {
1333 currTime = time(NULL);
1334 _ftprintf(fp, _T("#\n# NetXMS agent configuration file\n# Created by agent installer at %s#\n\n"),
1335 _tctime(&currTime));
1336 _ftprintf(fp, _T("MasterServers = %hs\nConfigIncludeDir = %hs\nLogFile = %hs\nFileStore = %hs\n"),
1337 pszServer, configIncludeDir, pszLogFile, pszFileStore);
1338
1339 Array extSubAgents;
1340 for(i = 0; i < iNumSubAgents; i++)
1341 {
1342 if (!strnicmp(ppszSubAgentList[i], "EXT:", 4))
1343 {
1344 extSubAgents.add(ppszSubAgentList[i] + 4);
1345 }
1346 else
1347 {
1348 _ftprintf(fp, _T("SubAgent = %hs\n"), ppszSubAgentList[i]);
1349 }
1350 }
1351
1352 for(i = 0; i < extSubAgents.size(); i++)
1353 {
1354 char section[MAX_PATH];
1355 strncpy(section, (const char *)extSubAgents.get(i), MAX_PATH);
1356 char *eptr = strrchr(section, '.');
1357 if (eptr != NULL)
1358 *eptr = 0;
1359 strupr(section);
1360 _ftprintf(fp, _T("\n[EXT:%hs]\n"), section);
1361 _ftprintf(fp, _T("SubAgent = %hs\n"), (const char *)extSubAgents.get(i));
1362 }
1363
1364 fclose(fp);
1365 }
1366 return (fp != NULL) ? 0 : 2;
1367 }
1368
1369 /**
1370 * Init config
1371 */
1372 static void InitConfig(const TCHAR *configSection)
1373 {
1374 g_config = new Config();
1375 g_config->setTopLevelTag(_T("config"));
1376 g_config->setAlias(_T("agent"), configSection);
1377
1378 // Set default data directory on Windows
1379 #ifdef _WIN32
1380 if (SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, g_szDataDirectory) == S_OK)
1381 {
1382 _tcscat(g_szDataDirectory, _T("\\nxagentd"));
1383 }
1384 #endif
1385 }
1386
1387 /**
1388 * Initiate shutdown of connected external subagents
1389 */
1390 static void InitiateExtSubagentShutdown()
1391 {
1392 NXCPMessage msg;
1393 msg.setCode(CMD_SHUTDOWN);
1394 if (SendControlMessage(&msg))
1395 _tprintf(_T("Control message sent successfully to master agent\n"));
1396 else
1397 _tprintf(_T("ERROR: Unable to send control message to master agent\n"));
1398 }
1399
1400 #ifndef _WIN32
1401
1402 /**
1403 * Get user ID
1404 */
1405 static int GetUserId(const char *name)
1406 {
1407 char *eptr;
1408 int id = (int)strtol(name, &eptr, 10);
1409 if (*eptr == 0)
1410 return id;
1411
1412 #if HAVE_GETPWNAM
1413 struct passwd *p = getpwnam(name);
1414 if (p == NULL)
1415 {
1416 _tprintf(_T("Invalid user ID \"%hs\"\n"), name);
1417 return 0;
1418 }
1419 return p->pw_uid;
1420 #else
1421 _tprintf(_T("Invalid user ID \"%hs\"\n"), name);
1422 return 0;
1423 #endif
1424 }
1425
1426 /**
1427 * Get group ID
1428 */
1429 static int GetGroupId(const char *name)
1430 {
1431 char *eptr;
1432 int id = (int)strtol(name, &eptr, 10);
1433 if (*eptr == 0)
1434 return id;
1435
1436 #if HAVE_GETGRNAM
1437 struct group *g = getgrnam(name);
1438 if (g == NULL)
1439 {
1440 _tprintf(_T("Invalid group ID \"%hs\"\n"), name);
1441 return 0;
1442 }
1443 return g->gr_gid;
1444 #else
1445 _tprintf(_T("Invalid group ID \"%hs\"\n"), name);
1446 return 0;
1447 #endif
1448 }
1449
1450 #endif
1451
1452 /**
1453 * Update agent environment from config
1454 */
1455 static void UpdateEnvironment()
1456 {
1457 ObjectArray<ConfigEntry> *entrySet = g_config->getSubEntries(_T("/ENV"), _T("*"));
1458 if (entrySet == NULL)
1459 return;
1460 for(int i = 0; i < entrySet->size(); i++)
1461 {
1462 ConfigEntry *e = entrySet->get(i);
1463 size_t len = _tcslen(e->getName()) + _tcslen(e->getValue()) + 2;
1464 char *env = (char *)malloc(len);
1465 if (env != NULL)
1466 {
1467 #ifdef UNICODE
1468 char *mbName = MBStringFromWideString(e->getName());
1469 char *mbValue = MBStringFromWideString(e->getValue());
1470 snprintf(env, len, "%s=%s", mbName, mbValue);
1471 free(mbName);
1472 free(mbValue);
1473 #else
1474 sprintf(env, "%s=%s", e->getName(), e->getValue());
1475 #endif
1476 putenv(env);
1477 }
1478 }
1479 delete entrySet;
1480 }
1481
1482 /**
1483 * Application entry point
1484 */
1485 int main(int argc, char *argv[])
1486 {
1487 int ch, iExitCode = 0, iAction = ACTION_RUN_AGENT;
1488 BOOL bRestart = FALSE;
1489 UINT32 dwOldPID, dwMainPID;
1490 char *eptr;
1491 TCHAR configSection[MAX_DB_STRING] = DEFAULT_CONFIG_SECTION;
1492 #ifdef _WIN32
1493 TCHAR szModuleName[MAX_PATH];
1494 HKEY hKey;
1495 DWORD dwSize;
1496 #else
1497 TCHAR *pszEnv;
1498 int uid = 0, gid = 0;
1499 #endif
1500
1501 InitNetXMSProcess(false);
1502
1503 // Check for alternate config file location
1504 #ifdef _WIN32
1505 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Agent"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
1506 {
1507 dwSize = MAX_PATH * sizeof(TCHAR);
1508 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)g_szConfigFile, &dwSize);
1509 dwSize = MAX_PATH * sizeof(TCHAR);
1510 RegQueryValueEx(hKey, _T("ConfigIncludeDir"), NULL, NULL, (BYTE *)g_szConfigIncludeDir, &dwSize);
1511 RegCloseKey(hKey);
1512 }
1513 #else
1514 pszEnv = _tgetenv(_T("NXAGENTD_CONFIG"));
1515 if (pszEnv != NULL)
1516 nx_strncpy(g_szConfigFile, pszEnv, MAX_PATH);
1517
1518 pszEnv = _tgetenv(_T("NXAGENTD_CONFIG_D"));
1519 if (pszEnv != NULL)
1520 nx_strncpy(g_szConfigIncludeDir, pszEnv, MAX_PATH);
1521 #endif
1522
1523 // Parse command line
1524 if (argc == 1)
1525 iAction = ACTION_HELP;
1526 opterr = 1;
1527 while((ch = getopt(argc, argv, VALID_OPTIONS)) != -1)
1528 {
1529 switch(ch)
1530 {
1531 case 'c': // Configuration file
1532 #ifdef UNICODE
1533 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szConfigFile, MAX_PATH);
1534 g_szConfigFile[MAX_PATH - 1] = 0;
1535 #else
1536 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
1537 #endif
1538 break;
1539 case 'C': // Configuration check only
1540 iAction = ACTION_CHECK_CONFIG;
1541 break;
1542 case 'd': // Run as daemon
1543 g_dwFlags |= AF_DAEMON;
1544 break;
1545 case 'D': // Turn on debug output
1546 s_debugLevel = strtoul(optarg, &eptr, 0);
1547 if ((*eptr != 0) || (s_debugLevel > 9))
1548 {
1549 fprintf(stderr, "Invalid debug level: %s\n", optarg);
1550 iAction = -1;
1551 iExitCode = 1;
1552 }
1553 break;
1554 case 'f': // Run in foreground
1555 g_dwFlags &= ~AF_DAEMON;
1556 break;
1557 #ifndef _WIN32
1558 case 'g': // set group ID
1559 gid = GetGroupId(optarg);
1560 break;
1561 #endif
1562 case 'G':
1563 #ifdef UNICODE
1564 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, configSection, MAX_DB_STRING);
1565 configSection[MAX_DB_STRING - 1] = 0;
1566 #else
1567 nx_strncpy(configSection, optarg, MAX_DB_STRING);
1568 #endif
1569 break;
1570 case 'h': // Display help and exit
1571 iAction = ACTION_HELP;
1572 break;
1573 #ifdef _WIN32
1574 case 'H': // Hide window
1575 g_dwFlags |= AF_HIDE_WINDOW;
1576 break;
1577 #endif
1578 case 'K': // Shutdown external sub-agents
1579 iAction = ACTION_SHUTDOWN_EXT_AGENTS;
1580 break;
1581 #ifndef _WIN32
1582 case 'p': // PID file
1583 #ifdef UNICODE
1584 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szPidFile, MAX_PATH);
1585 g_szPidFile[MAX_PATH - 1] = 0;
1586 #else
1587 nx_strncpy(g_szPidFile, optarg, MAX_PATH);
1588 #endif
1589 break;
1590 #endif
1591 case 'M':
1592 g_dwFlags |= AF_CENTRAL_CONFIG;
1593 #ifdef UNICODE
1594 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szConfigServer, MAX_DB_STRING);
1595 g_szConfigServer[MAX_DB_STRING - 1] = 0;
1596 #else
1597 nx_strncpy(g_szConfigServer, optarg, MAX_DB_STRING);
1598 #endif
1599 break;
1600 case 'r':
1601 g_dwFlags |= AF_REGISTER;
1602 #ifdef UNICODE
1603 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szRegistrar, MAX_DB_STRING);
1604 g_szRegistrar[MAX_DB_STRING - 1] = 0;
1605 #else
1606 nx_strncpy(g_szRegistrar, optarg, MAX_DB_STRING);
1607 #endif
1608 break;
1609 case 'P': // Platform suffix
1610 #ifdef UNICODE
1611 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szPlatformSuffix, MAX_PSUFFIX_LENGTH);
1612 g_szPlatformSuffix[MAX_PSUFFIX_LENGTH - 1] = 0;
1613 #else
1614 nx_strncpy(g_szPlatformSuffix, optarg, MAX_PSUFFIX_LENGTH);
1615 #endif
1616 break;
1617 #ifndef _WIN32
1618 case 'u': // set user ID
1619 uid = GetUserId(optarg);
1620 break;
1621 #endif
1622 case 'v': // Print version and exit
1623 _tprintf(_T("NetXMS Core Agent Version ") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" (") NETXMS_BUILD_TAG _T(")") IS_UNICODE_BUILD_STRING _T("\n"));
1624 iAction = ACTION_NONE;
1625 break;
1626 case 'W': // Watchdog process
1627 iAction = ACTION_RUN_WATCHDOG;
1628 dwMainPID = strtoul(optarg, NULL, 10);
1629 break;
1630 case 'X': // Agent is being restarted
1631 bRestart = TRUE;
1632 dwOldPID = strtoul(optarg, NULL, 10);
1633 break;
1634 case 'Z': // Create configuration file
1635 iAction = ACTION_CREATE_CONFIG;
1636 #ifdef UNICODE
1637 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_szConfigFile, MAX_PATH);
1638 g_szConfigFile[MAX_PATH - 1] = 0;
1639 #else
1640 nx_strncpy(g_szConfigFile, optarg, MAX_PATH);
1641 #endif
1642 break;
1643 #ifdef _WIN32
1644 case 'i':
1645 g_dwFlags |= AF_INTERACTIVE_SERVICE;
1646 break;
1647 case 'I': // Install Windows service
1648 iAction = ACTION_INSTALL_SERVICE;
1649 break;
1650 case 'R': // Remove Windows service
1651 iAction = ACTION_REMOVE_SERVICE;
1652 break;
1653 case 's': // Start Windows service
1654 iAction = ACTION_START_SERVICE;
1655 break;
1656 case 'S': // Stop Windows service
1657 iAction = ACTION_STOP_SERVICE;
1658 break;
1659 case 'E': // Install Windows event source
1660 iAction = ACTION_INSTALL_EVENT_SOURCE;
1661 break;
1662 case 'U': // Remove Windows event source
1663 iAction = ACTION_REMOVE_EVENT_SOURCE;
1664 break;
1665 case 'e': // Event source name
1666 #ifdef UNICODE
1667 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_windowsEventSourceName, MAX_PATH);
1668 g_windowsEventSourceName[MAX_PATH - 1] = 0;
1669 #else
1670 nx_strncpy(g_windowsEventSourceName, optarg, MAX_PATH);
1671 #endif
1672 break;
1673 case 'n': // Service name
1674 #ifdef UNICODE
1675 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_windowsServiceName, MAX_PATH);
1676 g_windowsServiceName[MAX_PATH - 1] = 0;
1677 #else
1678 nx_strncpy(g_windowsServiceName, optarg, MAX_PATH);
1679 #endif
1680 break;
1681 case 'N': // Service display name
1682 #ifdef UNICODE
1683 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, g_windowsServiceDisplayName, MAX_PATH);
1684 g_windowsServiceDisplayName[MAX_PATH - 1] = 0;
1685 #else
1686 nx_strncpy(g_windowsServiceDisplayName, optarg, MAX_PATH);
1687 #endif
1688 break;
1689 #endif
1690 case '?':
1691 iAction = ACTION_HELP;
1692 iExitCode = 1;
1693 break;
1694 default:
1695 break;
1696 }
1697 }
1698
1699 #if !defined(_WIN32)
1700 if (!_tcscmp(g_szConfigFile, _T("{search}")))
1701 {
1702 TCHAR path[MAX_PATH] = _T("");
1703 const TCHAR *homeDir = _tgetenv(_T("NETXMS_HOME"));
1704 if (homeDir != NULL)
1705 {
1706 _sntprintf(path, MAX_PATH, _T("%s/etc/nxagentd.conf"), homeDir);
1707 }
1708 if ((path[0] != 0) && (_taccess(path, 4) == 0))
1709 {
1710 _tcscpy(g_szConfigFile, path);
1711 }
1712 else if (_taccess(PREFIX _T("/etc/nxagentd.conf"), 4) == 0)
1713 {
1714 _tcscpy(g_szConfigFile, PREFIX _T("/etc/nxagentd.conf"));
1715 }
1716 else if (_taccess(_T("/Database/etc/nxagentd.conf"), 4) == 0) // for ZeroShell
1717 {
1718 _tcscpy(g_szConfigFile, _T("/Database/etc/nxagentd.conf"));
1719 }
1720 else if (_taccess(_T("/usr/etc/nxagentd.conf"), 4) == 0)
1721 {
1722 _tcscpy(g_szConfigFile, _T("/usr/etc/nxagentd.conf"));
1723 }
1724 else
1725 {
1726 _tcscpy(g_szConfigFile, _T("/etc/nxagentd.conf"));
1727 }
1728 }
1729 if (!_tcscmp(g_szConfigIncludeDir, _T("{search}")))
1730 {
1731 TCHAR path[MAX_PATH] = _T("");
1732 const TCHAR *homeDir = _tgetenv(_T("NETXMS_HOME"));
1733 if (homeDir != NULL)
1734 {
1735 _sntprintf(path, MAX_PATH, _T("%s/etc/nxagentd.conf.d"), homeDir);
1736 }
1737 if ((path[0] != 0) && (_taccess(path, 4) == 0))
1738 {
1739 _tcscpy(g_szConfigIncludeDir, path);
1740 }
1741 else if (_taccess(PREFIX _T("/etc/nxagentd.conf.d"), 4) == 0)
1742 {
1743 _tcscpy(g_szConfigIncludeDir, PREFIX _T("/etc/nxagentd.conf.d"));
1744 }
1745 else if (_taccess(_T("/Database/etc/nxagentd.conf.d"), 4) == 0)
1746 {
1747 _tcscpy(g_szConfigIncludeDir, _T("/Database/etc/nxagentd.conf.d"));
1748 }
1749 else if (_taccess(_T("/usr/etc/nxagentd.conf.d"), 4) == 0)
1750 {
1751 _tcscpy(g_szConfigIncludeDir, _T("/usr/etc/nxagentd.conf.d"));
1752 }
1753 else
1754 {
1755 _tcscpy(g_szConfigIncludeDir, _T("/etc/nxagentd.conf.d"));
1756 }
1757 }
1758 #else
1759 if (!_tcscmp(g_szConfigFile, _T("{search}")))
1760 {
1761 TCHAR path[MAX_PATH];
1762 GetNetXMSDirectory(nxDirEtc, path);
1763 _tcscat(path, _T("\\nxagentd.conf"));
1764 if (_taccess(path, 4) == 0)
1765 {
1766 _tcscpy(g_szConfigFile, path);
1767 }
1768 else
1769 {
1770 _tcscpy(g_szConfigFile, _T("C:\\nxagentd.conf"));
1771 }
1772 }
1773 if (!_tcscmp(g_szConfigIncludeDir, _T("{search}")))
1774 {
1775 TCHAR path[MAX_PATH];
1776 GetNetXMSDirectory(nxDirEtc, path);
1777 _tcscat(path, _T("\\nxagentd.conf.d"));
1778 if (_taccess(path, 4) == 0)
1779 {
1780 _tcscpy(g_szConfigIncludeDir, path);
1781 }
1782 else
1783 {
1784 _tcscpy(g_szConfigIncludeDir, _T("C:\\nxagentd.conf.d"));
1785 }
1786 }
1787 #endif
1788
1789 if (!_tcscmp(g_szDataDirectory, _T("{default}")))
1790 {
1791 GetNetXMSDirectory(nxDirData, g_szDataDirectory);
1792 }
1793
1794 // Initialize config policy folder and load policy based configs
1795 nx_strncpy(g_szConfigPolicyDir, g_szDataDirectory, MAX_PATH - 16);
1796 if (g_szConfigPolicyDir[_tcslen(g_szConfigPolicyDir) - 1] != FS_PATH_SEPARATOR_CHAR)
1797 _tcscat(g_szConfigPolicyDir, FS_PATH_SEPARATOR);
1798 _tcscat(g_szConfigPolicyDir, CONFIG_AP_FOLDER FS_PATH_SEPARATOR);
1799 CreateFolder(g_szConfigPolicyDir);
1800
1801 if (bRestart)
1802 DoRestartActions(dwOldPID);
1803
1804 InitConfig(configSection);
1805
1806 // Do requested action
1807 switch(iAction)
1808 {
1809 case ACTION_RUN_AGENT:
1810 // Set default value for session idle timeout based on
1811 // connect() timeout, if possible
1812 #if HAVE_SYSCTLBYNAME && !defined(_IPSO)
1813 {
1814 LONG nVal;
1815 size_t nSize;
1816
1817 nSize = sizeof(nVal);
1818 if (sysctlbyname("net.inet.tcp.keepinit", &nVal, &nSize, NULL, 0) == 0)
1819 {
1820 g_dwIdleTimeout = nVal / 1000 + 15;
1821 }
1822 }
1823 #endif
1824
1825 if (g_dwFlags & AF_CENTRAL_CONFIG)
1826 {
1827 if (s_debugLevel > 0)
1828 _tprintf(_T("Downloading configuration from %s...\n"), g_szConfigServer);
1829 if (DownloadConfig(g_szConfigServer))
1830 {
1831 if (s_debugLevel > 0)
1832 _tprintf(_T("Configuration downloaded successfully\n"));
1833 }
1834 else
1835 {
1836 if (s_debugLevel > 0)
1837 _tprintf(_T("Configuration download failed\n"));
1838 }
1839 }
1840
1841 if (g_config->loadConfig(g_szConfigFile, DEFAULT_CONFIG_SECTION))
1842 {
1843 const TCHAR *dir = g_config->getValue(_T("/%agent/ConfigIncludeDir"));
1844 if (dir != NULL)
1845 nx_strncpy(g_szConfigIncludeDir, dir, MAX_PATH);
1846 g_config->loadConfigDirectory(g_szConfigIncludeDir, DEFAULT_CONFIG_SECTION);
1847
1848 g_config->loadConfigDirectory(g_szConfigPolicyDir, DEFAULT_CONFIG_SECTION);
1849
1850 // Check if master section starts with EXT:
1851 // If yes, switch to external subagent mode
1852 if (!_tcsnicmp(configSection, _T("EXT:"), 4))
1853 {
1854 nx_strncpy(g_masterAgent, &configSection[4], MAX_PATH);
1855 }
1856
1857 if (g_config->parseTemplate(configSection, m_cfgTemplate))
1858 {
1859 _tprintf(_T("Taglist main: %s\n"), s_debugTags);
1860 DecryptPassword(_T("netxms"), g_szSharedSecret, g_szSharedSecret, MAX_SECRET_LENGTH);
1861
1862 // try to guess executable path
1863 #ifdef _WIN32
1864 GetModuleFileName(GetModuleHandle(NULL), s_executableName, MAX_PATH);
1865 #else
1866 #ifdef UNICODE
1867 char __buffer[PATH_MAX];
1868 #else
1869 #define __buffer s_executableName
1870 #endif
1871 if (realpath(argv[0], __buffer) == NULL)
1872 {
1873 // fallback
1874 TCHAR *path = _tgetenv(_T("NETXMS_HOME"));
1875 if (path != NULL)
1876 {
1877 nx_strncpy(s_executableName, path, sizeof(s_executableName) / sizeof(s_executableName[0]));
1878 }
1879 else
1880 {
1881 nx_strncpy(s_executableName, PREFIX, sizeof(s_executableName) / sizeof(s_executableName[0]));
1882 }
1883 _tcsncat(s_executableName, _T("/bin/nxagentd"), sizeof(s_executableName) / sizeof(s_executableName[0]));
1884 }
1885 else
1886 {
1887 #ifdef UNICODE
1888 int len = strlen(__buffer);
1889 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, __buffer, len, s_executableName, MAX_PATH);
1890 #endif
1891 }
1892 #endif
1893
1894 // Set exception handler
1895 #ifdef _WIN32
1896 if (g_dwFlags & AF_CATCH_EXCEPTIONS)
1897 SetExceptionHandler(SEHServiceExceptionHandler, SEHServiceExceptionDataWriter, s_dumpDir,
1898 _T("nxagentd"), MSG_EXCEPTION, g_dwFlags & AF_WRITE_FULL_DUMP, !(g_dwFlags & AF_DAEMON));
1899 __try {
1900 #endif
1901 if ((!_tcsicmp(g_szLogFile, _T("{syslog}"))) ||
1902 (!_tcsicmp(g_szLogFile, _T("{eventlog}"))))
1903 g_dwFlags |= AF_USE_SYSLOG;
1904
1905 #ifdef _WIN32
1906 if (g_dwFlags & AF_DAEMON)
1907 {
1908 InitService();
1909 }
1910 else
1911 {
1912 if (Initialize())
1913 {
1914 Main();
1915 }
1916 else
1917 {
1918 ConsolePrintf(_T("Agent initialization failed\n"));
1919 nxlog_close();
1920 iExitCode = 3;
1921 }
1922 }
1923 #else /* _WIN32 */
1924 if (g_dwFlags & AF_DAEMON)
1925 {
1926 if (daemon(0, 0) == -1)
1927 {
1928 perror("Unable to setup itself as a daemon");
1929 iExitCode = 4;
1930 }
1931 }
1932 if (iExitCode == 0)
1933 {
1934 #ifndef _WIN32
1935 if (gid == 0)
1936 {
1937 const TCHAR *v = g_config->getValue(_T("/%agent/GroupId"));
1938 if (v != NULL)
1939 {
1940 #ifdef UNICODE
1941 char vmb[64];
1942 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, v, -1, vmb, 64, NULL, NULL);
1943 vmb[63] = 0;
1944 gid = GetGroupId(vmb);
1945 #else
1946 gid = GetGroupId(v);
1947 #endif
1948 }
1949 }
1950 if (uid == 0)
1951 {
1952 const TCHAR *v = g_config->getValue(_T("/%agent/UserId"));
1953 if (v != NULL)
1954 {
1955 #ifdef UNICODE
1956 char vmb[64];
1957 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, v, -1, vmb, 64, NULL, NULL);
1958 vmb[63] = 0;
1959 uid = GetUserId(vmb);
1960 #else
1961 uid = GetUserId(v);
1962 #endif
1963 }
1964 }
1965
1966 if (gid > 0)
1967 {
1968 if (setgid(gid) != 0)
1969 _tprintf(_T("setgid(%d) call failed (%s)\n"), gid, _tcserror(errno));
1970 }
1971 if (uid > 0)
1972 {
1973 if (setuid(uid) != 0)
1974 _tprintf(_T("setuid(%d) call failed (%s)\n"), uid, _tcserror(errno));
1975 }
1976 #endif
1977
1978 UpdateEnvironment();
1979
1980 s_pid = getpid();
1981 if (Initialize())
1982 {
1983 FILE *fp;
1984
1985 // Write PID file
1986 fp = _tfopen(g_szPidFile, _T("w"));
1987 if (fp != NULL)
1988 {
1989 _ftprintf(fp, _T("%d"), s_pid);
1990 fclose(fp);
1991 }
1992 Main();
1993 Shutdown();
1994 }
1995 else
1996 {
1997 ConsolePrintf(_T("Agent initialization failed\n"));
1998 nxlog_close();
1999 iExitCode = 3;
2000 }
2001 }
2002 #endif /* _WIN32 */
2003
2004 #if defined(_WIN32)
2005 if (s_shutdownCondition != INVALID_CONDITION_HANDLE)
2006 ConditionDestroy(s_shutdownCondition);
2007 #endif
2008 #ifdef _WIN32
2009 LIBNETXMS_EXCEPTION_HANDLER
2010 #endif
2011 }
2012 else
2013 {
2014 ConsolePrintf(_T("Error parsing configuration file\n"));
2015 iExitCode = 2;
2016 }
2017 }
2018 else
2019 {
2020 ConsolePrintf(_T("Error loading configuration file\n"));
2021 iExitCode = 2;
2022 }
2023 break;
2024 case ACTION_CHECK_CONFIG:
2025 {
2026 bool validConfig = g_config->loadConfig(g_szConfigFile, DEFAULT_CONFIG_SECTION, false);
2027 if (validConfig)
2028 {
2029 const TCHAR *dir = g_config->getValue(_T("/%agent/ConfigIncludeDir"));
2030 if (dir != NULL)
2031 {
2032 validConfig = g_config->loadConfigDirectory(dir, DEFAULT_CONFIG_SECTION, false);
2033 if (!validConfig)
2034 {
2035 ConsolePrintf(_T("Error reading additional configuration files from \"%s\"\n"), dir);
2036 }
2037 }
2038 }
2039
2040 if (validConfig)
2041 {
2042 validConfig = g_config->loadConfigDirectory(g_szConfigPolicyDir, DEFAULT_CONFIG_SECTION);
2043 if (!validConfig)
2044 {
2045 ConsolePrintf(_T("Error reading additional configuration files from \"%s\"\n"), g_szConfigPolicyDir);
2046 }
2047 }
2048
2049 if (validConfig)
2050 {
2051 g_config->print(stdout);
2052 validConfig = g_config->parseTemplate(configSection, m_cfgTemplate);
2053 }
2054
2055 if (!validConfig)
2056 {
2057 ConsolePrintf(_T("Configuration file check failed\n"));
2058 iExitCode = 2;
2059 }
2060 }
2061 break;
2062 case ACTION_RUN_WATCHDOG:
2063 iExitCode = WatchdogMain(dwMainPID);
2064 break;
2065 case ACTION_CREATE_CONFIG:
2066 iExitCode = CreateConfig(CHECK_NULL_A(argv[optind]), CHECK_NULL_A(argv[optind + 1]),
2067 CHECK_NULL_A(argv[optind + 2]), CHECK_NULL_A(argv[optind + 3]),
2068 argc - optind - 4, &argv[optind + 4]);
2069 break;
2070 case ACTION_SHUTDOWN_EXT_AGENTS:
2071 InitiateExtSubagentShutdown();
2072 iExitCode = 0;
2073 break;
2074 #ifdef _WIN32
2075 case ACTION_INSTALL_SERVICE:
2076 GetModuleFileName(GetModuleHandle(NULL), szModuleName, MAX_PATH);
2077 InstallService(szModuleName, g_szConfigFile, s_debugLevel);
2078 break;
2079 case ACTION_REMOVE_SERVICE:
2080 RemoveService();
2081 break;
2082 case ACTION_INSTALL_EVENT_SOURCE:
2083 GetModuleFileName(GetModuleHandle(NULL), szModuleName, MAX_PATH);
2084 InstallEventSource(szModuleName);
2085 break;
2086 case ACTION_REMOVE_EVENT_SOURCE:
2087 RemoveEventSource();
2088 break;
2089 case ACTION_START_SERVICE:
2090 StartAgentService();
2091 break;
2092 case ACTION_STOP_SERVICE:
2093 StopAgentService();
2094 break;
2095 #endif
2096 case ACTION_HELP:
2097 _fputts(m_szHelpText, stdout);
2098 break;
2099 default:
2100 break;
2101 }
2102
2103 return iExitCode;
2104 }