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