Implemented file upload to agents
[public/netxms.git] / src / agent / core / nxagentd.cpp
1 /*
2 ** NetXMS multiplatform core agent
3 ** Copyright (C) 2003, 2004 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be usefu,,
11 ** but ITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** $module: nxagentd.cpp
20 **
21 **/
22
23 #include "nxagentd.h"
24
25 #ifdef _WIN32
26 #include <conio.h>
27 #endif
28
29 #ifdef _NETWARE
30 #include <screen.h>
31 #endif
32
33
34 //
35 // Externals
36 //
37
38 THREAD_RESULT THREAD_CALL ListenerThread(void *);
39
40
41 //
42 // Valid options for getopt()
43 //
44
45 #if defined(_WIN32)
46 #define VALID_OPTIONS "c:CdDhIRsSv"
47 #elif defined(_NETWARE)
48 #define VALID_OPTIONS "c:CDhv"
49 #else
50 #define VALID_OPTIONS "c:CdDhp:v"
51 #endif
52
53
54 //
55 // Actions
56 //
57
58 #define ACTION_NONE 0
59 #define ACTION_RUN_AGENT 1
60 #define ACTION_INSTALL_SERVICE 2
61 #define ACTION_REMOVE_SERVICE 3
62 #define ACTION_START_SERVICE 4
63 #define ACTION_STOP_SERVICE 5
64 #define ACTION_CHECK_CONFIG 6
65
66
67 //
68 // Global variables
69 //
70
71 DWORD g_dwFlags = 0;
72 char g_szLogFile[MAX_PATH] = AGENT_DEFAULT_LOG;
73 char g_szSharedSecret[MAX_SECRET_LENGTH] = "admin";
74 char g_szConfigFile[MAX_PATH] = AGENT_DEFAULT_CONFIG;
75 char g_szFileStore[MAX_PATH] = AGENT_DEFAULT_FILE_STORE;
76 WORD g_wListenPort = AGENT_LISTEN_PORT;
77 SERVER_INFO g_pServerList[MAX_SERVERS];
78 DWORD g_dwServerCount = 0;
79 DWORD g_dwTimeOut = 5000; // Request timeout in milliseconds
80 time_t g_dwAgentStartTime;
81
82 #if !defined(_WIN32) && !defined(_NETWARE)
83 char g_szPidFile[MAX_PATH] = "/var/run/nxagentd.pid";
84 #endif
85
86 #ifdef _WIN32
87 DWORD (__stdcall *imp_GetGuiResources)(HANDLE, DWORD);
88 BOOL (__stdcall *imp_GetProcessIoCounters)(HANDLE, PIO_COUNTERS);
89 BOOL (__stdcall *imp_GetPerformanceInfo)(PPERFORMANCE_INFORMATION, DWORD);
90 BOOL (__stdcall *imp_GlobalMemoryStatusEx)(LPMEMORYSTATUSEX);
91 DWORD (__stdcall *imp_HrLanConnectionNameFromGuidOrPath)(LPWSTR, LPWSTR, LPWSTR, LPDWORD);
92 #endif /* _WIN32 */
93
94
95 //
96 // Static variables
97 //
98
99 static char *m_pszActionList = NULL;
100 static char *m_pszServerList = NULL;
101 static char *m_pszInstallServerList = NULL;
102 static char *m_pszSubagentList = NULL;
103 static char *m_pszExtParamList = NULL;
104 static CONDITION m_hCondShutdown = INVALID_CONDITION_HANDLE;
105 static DWORD m_dwStartupDelay = 0;
106
107
108 //
109 // Configuration file template
110 //
111
112 static NX_CFG_TEMPLATE cfgTemplate[] =
113 {
114 { "Action", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszActionList },
115 { "EnableActions", CT_BOOLEAN, 0, 0, AF_ENABLE_ACTIONS, 0, &g_dwFlags },
116 { "ExternalParameter", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszExtParamList },
117 { "FileStore", CT_STRING, 0, 0, MAX_PATH, 0, g_szFileStore },
118 { "InstallationServers", CT_STRING_LIST, ',', 0, 0, 0, &m_pszInstallServerList },
119 { "ListenPort", CT_WORD, 0, 0, 0, 0, &g_wListenPort },
120 { "LogFile", CT_STRING, 0, 0, MAX_PATH, 0, g_szLogFile },
121 { "LogUnresolvedSymbols", CT_BOOLEAN, 0, 0, AF_LOG_UNRESOLVED_SYMBOLS, 0, &g_dwFlags },
122 { "RequireAuthentication", CT_BOOLEAN, 0, 0, AF_REQUIRE_AUTH, 0, &g_dwFlags },
123 { "Servers", CT_STRING_LIST, ',', 0, 0, 0, &m_pszServerList },
124 { "SharedSecret", CT_STRING, 0, 0, MAX_SECRET_LENGTH, 0, g_szSharedSecret },
125 { "StartupDelay", CT_LONG, 0, 0, 0, 0, &m_dwStartupDelay },
126 { "SubAgent", CT_STRING_LIST, '\n', 0, 0, 0, &m_pszSubagentList },
127 { "Timeout", CT_LONG, 0, 0, 0, 0, &g_dwTimeOut },
128 { "", CT_END_OF_LIST, 0, 0, 0, 0, NULL }
129 };
130
131
132 //
133 // Help text
134 //
135
136 static char m_szHelpText[] =
137 "Usage: nxagentd [options]\n"
138 "Where valid options are:\n"
139 " -c <file> : Use configuration file <file> (default " AGENT_DEFAULT_CONFIG ")\n"
140 " -C : Check configuration file and exit\n"
141 #ifndef _NETWARE
142 " -d : Run as daemon/service\n"
143 #endif
144 " -D : Turn on debug output\n"
145 " -h : Display help and exit\n"
146 #ifdef _WIN32
147 " -I : Install Windows service\n"
148 " -R : Remove Windows service\n"
149 " -s : Start Windows servive\n"
150 " -S : Stop Windows service\n"
151 #endif
152 " -v : Display version and exit\n"
153 "\n";
154
155
156 #ifdef _WIN32
157
158 //
159 // Get proc address and write log file
160 //
161
162 static FARPROC GetProcAddressAndLog(HMODULE hModule, LPCSTR procName)
163 {
164 FARPROC ptr;
165
166 ptr = GetProcAddress(hModule, procName);
167 if ((ptr == NULL) && (g_dwFlags & AF_LOG_UNRESOLVED_SYMBOLS))
168 WriteLog(MSG_NO_FUNCTION, EVENTLOG_WARNING_TYPE, "s", procName);
169 return ptr;
170 }
171
172
173 //
174 // Import symbols
175 //
176
177 static void ImportSymbols(void)
178 {
179 HMODULE hModule;
180
181 // USER32.DLL
182 hModule = GetModuleHandle("USER32.DLL");
183 if (hModule != NULL)
184 {
185 imp_GetGuiResources = (DWORD (__stdcall *)(HANDLE, DWORD))GetProcAddressAndLog(hModule,"GetGuiResources");
186 }
187 else
188 {
189 WriteLog(MSG_NO_DLL, EVENTLOG_WARNING_TYPE, "s", "USER32.DLL");
190 }
191
192 // KERNEL32.DLL
193 hModule = GetModuleHandle("KERNEL32.DLL");
194 if (hModule != NULL)
195 {
196 imp_GetProcessIoCounters = (BOOL (__stdcall *)(HANDLE, PIO_COUNTERS))GetProcAddressAndLog(hModule,"GetProcessIoCounters");
197 imp_GlobalMemoryStatusEx = (BOOL (__stdcall *)(LPMEMORYSTATUSEX))GetProcAddressAndLog(hModule,"GlobalMemoryStatusEx");
198 }
199 else
200 {
201 WriteLog(MSG_NO_DLL, EVENTLOG_WARNING_TYPE, "s", "KERNEL32.DLL");
202 }
203
204 // PSAPI.DLL
205 hModule = GetModuleHandle("PSAPI.DLL");
206 if (hModule != NULL)
207 {
208 imp_GetPerformanceInfo = (BOOL (__stdcall *)(PPERFORMANCE_INFORMATION, DWORD))GetProcAddressAndLog(hModule,"GetPerformanceInfo");
209 }
210 else
211 {
212 WriteLog(MSG_NO_DLL, EVENTLOG_WARNING_TYPE, "s", "PSAPI.DLL");
213 }
214
215 // NETMAN.DLL
216 hModule = LoadLibrary("NETMAN.DLL");
217 if (hModule != NULL)
218 {
219 imp_HrLanConnectionNameFromGuidOrPath =
220 (DWORD (__stdcall *)(LPWSTR, LPWSTR, LPWSTR, LPDWORD))GetProcAddressAndLog(hModule,
221 "HrLanConnectionNameFromGuidOrPath");
222 }
223 else
224 {
225 WriteLog(MSG_NO_DLL, EVENTLOG_WARNING_TYPE, "s", "NETMAN.DLL");
226 }
227 }
228
229 #endif /* _WIN32 */
230
231
232 //
233 // This function writes message from subagent to agent's log
234 //
235
236 static void WriteSubAgentMsg(int iLevel, TCHAR *pszMsg)
237 {
238 WriteLog(MSG_SUBAGENT_MSG, iLevel, "s", pszMsg);
239 }
240
241
242 //
243 // Initialization routine
244 //
245
246 BOOL Initialize(void)
247 {
248 char *pItem, *pEnd;
249
250 // Open log file
251 InitLog();
252
253 #ifdef _WIN32
254 WSADATA wsaData;
255 if (WSAStartup(2, &wsaData) != 0)
256 {
257 WriteLog(MSG_WSASTARTUP_FAILED, EVENTLOG_ERROR_TYPE, "e", WSAGetLastError());
258 return FALSE;
259 }
260 #endif
261
262 // Initialize logger for subagents
263 InitSubAgentsLogger(WriteSubAgentMsg);
264
265 // Initialize built-in parameters
266 if (!InitParameterList())
267 return FALSE;
268
269 #ifdef _WIN32
270 // Dynamically import functions that may not be presented in all Windows versions
271 ImportSymbols();
272 #endif
273
274 // Parse server list
275 if (m_pszServerList != NULL)
276 {
277 for(pItem = m_pszServerList; *pItem != 0; pItem = pEnd + 1)
278 {
279 pEnd = strchr(pItem, ',');
280 if (pEnd != NULL)
281 *pEnd = 0;
282 StrStrip(pItem);
283 g_pServerList[g_dwServerCount].dwIpAddr = inet_addr(pItem);
284 if ((g_pServerList[g_dwServerCount].dwIpAddr == INADDR_NONE) ||
285 (g_pServerList[g_dwServerCount].dwIpAddr == INADDR_ANY))
286 {
287 if (!(g_dwFlags & AF_DAEMON))
288 printf("Invalid server address '%s'\n", pItem);
289 }
290 else
291 {
292 g_pServerList[g_dwServerCount].bInstallationServer = FALSE;
293 g_dwServerCount++;
294 }
295 }
296 free(m_pszServerList);
297 }
298
299 // Parse installation server list
300 if (m_pszInstallServerList != NULL)
301 {
302 DWORD i, dwAddr;
303
304 for(pItem = m_pszInstallServerList; *pItem != 0; pItem = pEnd + 1)
305 {
306 pEnd = strchr(pItem, ',');
307 if (pEnd != NULL)
308 *pEnd = 0;
309 StrStrip(pItem);
310
311 dwAddr = inet_addr(pItem);
312 if ((dwAddr == INADDR_NONE) ||
313 (dwAddr == INADDR_ANY))
314 {
315 if (!(g_dwFlags & AF_DAEMON))
316 printf("Invalid server address '%s'\n", pItem);
317 }
318 else
319 {
320 for(i = 0; i < g_dwServerCount; i++)
321 if (g_pServerList[i].dwIpAddr == dwAddr)
322 break;
323
324 if (i == g_dwServerCount)
325 {
326 g_pServerList[g_dwServerCount].dwIpAddr = dwAddr;
327 g_pServerList[g_dwServerCount].bInstallationServer = TRUE;
328 g_dwServerCount++;
329 }
330 else
331 {
332 g_pServerList[i].bInstallationServer = TRUE;
333 }
334 }
335 }
336 free(m_pszInstallServerList);
337 }
338
339 // Load subagents
340 if (m_pszSubagentList != NULL)
341 {
342 for(pItem = m_pszSubagentList; *pItem != 0; pItem = pEnd + 1)
343 {
344 pEnd = strchr(pItem, '\n');
345 if (pEnd != NULL)
346 *pEnd = 0;
347 StrStrip(pItem);
348 LoadSubAgent(pItem);
349 }
350 free(m_pszSubagentList);
351 }
352
353 // Parse action list
354 if (m_pszActionList != NULL)
355 {
356 for(pItem = m_pszActionList; *pItem != 0; pItem = pEnd + 1)
357 {
358 pEnd = strchr(pItem, '\n');
359 if (pEnd != NULL)
360 *pEnd = 0;
361 StrStrip(pItem);
362 if (!AddActionFromConfig(pItem))
363 WriteLog(MSG_ADD_ACTION_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
364 }
365 free(m_pszActionList);
366 }
367
368 // Parse external parameters list
369 if (m_pszExtParamList != NULL)
370 {
371 for(pItem = m_pszExtParamList; *pItem != 0; pItem = pEnd + 1)
372 {
373 pEnd = strchr(pItem, '\n');
374 if (pEnd != NULL)
375 *pEnd = 0;
376 StrStrip(pItem);
377 if (!AddExternalParameter(pItem))
378 WriteLog(MSG_ADD_EXT_PARAM_FAILED, EVENTLOG_WARNING_TYPE, "s", pItem);
379 }
380 free(m_pszExtParamList);
381 }
382
383 // Agent start time
384 g_dwAgentStartTime = time(NULL);
385
386 // Start network listener
387 ThreadCreate(ListenerThread, 0, NULL);
388
389 m_hCondShutdown = ConditionCreate(FALSE);
390 ThreadSleep(1);
391
392 return TRUE;
393 }
394
395
396 //
397 // Shutdown routine
398 //
399
400 void Shutdown(void)
401 {
402 // Set shutdowm flag and sleep for some time
403 // to allow other threads to finish
404 g_dwFlags |= AF_SHUTDOWN;
405 ThreadSleep(5);
406
407 UnloadAllSubAgents();
408 CloseLog();
409
410 // Notify main thread about shutdown
411 ConditionSet(m_hCondShutdown);
412
413 // Remove PID file
414 #if !defined(_WIN32) && !defined(_NETWARE)
415 remove(g_szPidFile);
416 #endif
417 }
418
419
420 //
421 // Common Main()
422 //
423
424 void Main(void)
425 {
426 if (m_dwStartupDelay > 0)
427 {
428 if (g_dwFlags & AF_DAEMON)
429 {
430 ThreadSleep(m_dwStartupDelay);
431 }
432 else
433 {
434 DWORD i;
435
436 printf("XXXXXX%*s]\rWAIT [", m_dwStartupDelay, " ");
437 fflush(stdout);
438 for(i = 0; i < m_dwStartupDelay; i++)
439 {
440 ThreadSleep(1);
441 putc('.', stdout);
442 fflush(stdout);
443 }
444 printf("\n");
445 }
446 }
447 WriteLog(MSG_AGENT_STARTED, EVENTLOG_INFORMATION_TYPE, NULL);
448
449 if (g_dwFlags & AF_DAEMON)
450 {
451 ConditionWait(m_hCondShutdown, INFINITE);
452 }
453 else
454 {
455 #if defined(_WIN32)
456 printf("Agent running. Press ESC to shutdown.\n");
457 while(1)
458 {
459 if (getch() == 27)
460 break;
461 }
462 printf("Agent shutting down...\n");
463 Shutdown();
464 #elif defined(_NETWARE)
465 printf("Agent running. Type UNLOAD NXAGENTD on the system console for shutdown.\n");
466 ConditionWait(m_hCondShutdown, INFINITE);
467 #else
468 printf("Agent running. Press Ctrl+C to shutdown.\n");
469 ConditionWait(m_hCondShutdown, INFINITE);
470 #endif
471 }
472 }
473
474
475 //
476 // Startup
477 //
478
479 int main(int argc, char *argv[])
480 {
481 int ch, iExitCode = 0, iAction = ACTION_RUN_AGENT;
482 #ifdef _WIN32
483 char szModuleName[MAX_PATH];
484 #endif
485
486 #ifdef _NETWARE
487 setscreenmode(SCR_AUTOCLOSE_ON_EXIT | SCR_COLOR_ATTRS);
488 #endif
489
490 // Parse command line
491 opterr = 1;
492 while((ch = getopt(argc, argv, VALID_OPTIONS)) != -1)
493 {
494 switch(ch)
495 {
496 case 'h': // Display help and exit
497 printf(m_szHelpText);
498 iAction = ACTION_NONE;
499 break;
500 case 'd': // Run as daemon
501 g_dwFlags |= AF_DAEMON;
502 break;
503 case 'D': // Turn on debug output
504 g_dwFlags |= AF_DEBUG;
505 break;
506 case 'c': // Configuration file
507 strncpy(g_szConfigFile, optarg, MAX_PATH - 1);
508 break;
509 #if !defined(_WIN32) && !defined(_NETWARE)
510 case 'p': // PID file
511 strncpy(g_szPidFile, optarg, MAX_PATH - 1);
512 break;
513 #endif
514 case 'C': // Configuration check only
515 iAction = ACTION_CHECK_CONFIG;
516 break;
517 case 'v': // Print version and exit
518 printf("NetXMS Core Agent Version " AGENT_VERSION_STRING "\n");
519 iAction = ACTION_NONE;
520 break;
521 #ifdef _WIN32
522 case 'I': // Install Windows service
523 iAction = ACTION_INSTALL_SERVICE;
524 break;
525 case 'R': // Remove Windows service
526 iAction = ACTION_REMOVE_SERVICE;
527 break;
528 case 's': // Start Windows service
529 iAction = ACTION_START_SERVICE;
530 break;
531 case 'S': // Stop Windows service
532 iAction = ACTION_STOP_SERVICE;
533 break;
534 #endif
535 case '?':
536 iAction = ACTION_NONE;
537 iExitCode = 1;
538 break;
539 default:
540 break;
541 }
542 }
543
544 // Do requested action
545 switch(iAction)
546 {
547 case ACTION_RUN_AGENT:
548 if (NxLoadConfig(g_szConfigFile, "", cfgTemplate, !(g_dwFlags & AF_DAEMON)) == NXCFG_ERR_OK)
549 {
550 if ((!stricmp(g_szLogFile, "{syslog}")) ||
551 (!stricmp(g_szLogFile, "{eventlog}")))
552 g_dwFlags |= AF_USE_SYSLOG;
553
554 #ifdef _WIN32
555 if (g_dwFlags & AF_DAEMON)
556 {
557 InitService();
558 }
559 else
560 {
561 if (Initialize())
562 {
563 Main();
564 }
565 else
566 {
567 ConsolePrintf("Agent initialization failed\n");
568 CloseLog();
569 iExitCode = 3;
570 }
571 }
572 #else /* _WIN32 */
573 #ifndef _NETWARE
574 if (g_dwFlags & AF_DAEMON)
575 if (daemon(0, 0) == -1)
576 {
577 perror("Unable to setup itself as a daemon");
578 iExitCode = 4;
579 }
580 #endif
581 if (iExitCode == 0)
582 {
583 if (Initialize())
584 {
585 #ifndef _NETWARE
586 FILE *fp;
587
588 // Write PID file
589 fp = fopen(g_szPidFile, "w");
590 if (fp != NULL)
591 {
592 fprintf(fp, "%d", getpid());
593 fclose(fp);
594 }
595 #endif
596 Main();
597 }
598 else
599 {
600 ConsolePrintf("Agent initialization failed\n");
601 CloseLog();
602 iExitCode = 3;
603 }
604 }
605 #endif /* _WIN32 */
606
607 if (m_hCondShutdown != INVALID_CONDITION_HANDLE)
608 ConditionDestroy(m_hCondShutdown);
609 }
610 else
611 {
612 ConsolePrintf("Error loading configuration file\n");
613 iExitCode = 2;
614 }
615 break;
616 case ACTION_CHECK_CONFIG:
617 if (NxLoadConfig(g_szConfigFile, "", cfgTemplate, !(g_dwFlags & AF_DAEMON)) != NXCFG_ERR_OK)
618 {
619 ConsolePrintf("Configuration file check failed\n");
620 iExitCode = 2;
621 }
622 break;
623 #ifdef _WIN32
624 case ACTION_INSTALL_SERVICE:
625 GetModuleFileName(GetModuleHandle(NULL), szModuleName, MAX_PATH);
626 InstallService(szModuleName, g_szConfigFile);
627 break;
628 case ACTION_REMOVE_SERVICE:
629 RemoveService();
630 break;
631 case ACTION_START_SERVICE:
632 StartAgentService();
633 break;
634 case ACTION_STOP_SERVICE:
635 StopAgentService();
636 break;
637 #endif
638 default:
639 break;
640 }
641
642 #ifdef _NETWARE
643 if ((iExitCode != 0) || (iAction == ACTION_NONE) ||
644 (iAction == ACTION_CHECK_CONFIG))
645 setscreenmode(SCR_NO_MODE);
646 #endif
647
648 return iExitCode;
649 }