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