agent and server can use NETXMS_HOME environment variable to locate lib directory
[public/netxms.git] / src / agent / core / subagent.cpp
1 /*
2 ** NetXMS multiplatform core agent
3 ** Copyright (C) 2003-2013 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: subagent.cpp
20 **
21 **/
22
23 #include "nxagentd.h"
24
25 #ifdef _NETWARE
26 #include <fsio.h>
27 #include <library.h>
28 #endif
29
30 /**
31 * Subagent list
32 */
33 static UINT32 m_dwNumSubAgents = 0;
34 static SUBAGENT *m_pSubAgentList = NULL;
35
36 /**
37 * Initialize subagent
38 * Note: pszEntryPoint ignored on all pltforms except NetWare
39 */
40 BOOL InitSubAgent(HMODULE hModule, const TCHAR *pszModuleName,
41 BOOL (* SubAgentRegister)(NETXMS_SUBAGENT_INFO **, Config *),
42 const TCHAR *pszEntryPoint)
43 {
44 NETXMS_SUBAGENT_INFO *pInfo;
45 BOOL bSuccess = FALSE, bInitOK;
46
47 if (SubAgentRegister(&pInfo, g_config))
48 {
49 // Check if information structure is valid
50 if (pInfo->magic == NETXMS_SUBAGENT_INFO_MAGIC)
51 {
52 UINT32 i;
53
54 // Check if subagent with given name alreay loaded
55 for(i = 0; i < m_dwNumSubAgents; i++)
56 if (!_tcsicmp(m_pSubAgentList[i].pInfo->name, pInfo->name))
57 break;
58 if (i == m_dwNumSubAgents)
59 {
60 // Initialize subagent
61 bInitOK = (pInfo->init != NULL) ? pInfo->init(g_config) : TRUE;
62 if (bInitOK)
63 {
64 // Add subagent to subagent's list
65 m_pSubAgentList = (SUBAGENT *)realloc(m_pSubAgentList,
66 sizeof(SUBAGENT) * (m_dwNumSubAgents + 1));
67 m_pSubAgentList[m_dwNumSubAgents].hModule = hModule;
68 nx_strncpy(m_pSubAgentList[m_dwNumSubAgents].szName, pszModuleName, MAX_PATH);
69 m_pSubAgentList[m_dwNumSubAgents].pInfo = pInfo;
70 #ifdef _NETWARE
71 nx_strncpy(m_pSubAgentList[m_dwNumSubAgents].szEntryPoint, pszEntryPoint, 256);
72 #endif
73 m_dwNumSubAgents++;
74
75 // Add parameters provided by this subagent to common list
76 for(i = 0; i < pInfo->numParameters; i++)
77 AddParameter(pInfo->parameters[i].name,
78 pInfo->parameters[i].handler,
79 pInfo->parameters[i].arg,
80 pInfo->parameters[i].dataType,
81 pInfo->parameters[i].description);
82
83 // Add push parameters provided by this subagent to common list
84 for(i = 0; i < pInfo->numPushParameters; i++)
85 AddPushParameter(pInfo->pushParameters[i].name,
86 pInfo->pushParameters[i].dataType,
87 pInfo->pushParameters[i].description);
88
89 // Add lists provided by this subagent to common list
90 for(i = 0; i < pInfo->numLists; i++)
91 AddList(pInfo->lists[i].name,
92 pInfo->lists[i].handler,
93 pInfo->lists[i].arg);
94
95 // Add tables provided by this subagent to common list
96 for(i = 0; i < pInfo->numTables; i++)
97 AddTable(pInfo->tables[i].name,
98 pInfo->tables[i].handler,
99 pInfo->tables[i].arg,
100 pInfo->tables[i].instanceColumns,
101 pInfo->tables[i].description);
102
103 // Add actions provided by this subagent to common list
104 for(i = 0; i < pInfo->numActions; i++)
105 AddAction(pInfo->actions[i].name,
106 AGENT_ACTION_SUBAGENT,
107 pInfo->actions[i].arg,
108 pInfo->actions[i].handler,
109 pInfo->name,
110 pInfo->actions[i].description);
111
112 nxlog_write(MSG_SUBAGENT_LOADED, EVENTLOG_INFORMATION_TYPE,
113 "s", pszModuleName);
114 bSuccess = TRUE;
115 }
116 else
117 {
118 nxlog_write(MSG_SUBAGENT_INIT_FAILED, EVENTLOG_ERROR_TYPE,
119 "s", pszModuleName);
120 DLClose(hModule);
121 }
122 }
123 else
124 {
125 nxlog_write(MSG_SUBAGENT_ALREADY_LOADED, EVENTLOG_WARNING_TYPE,
126 "ss", pInfo->name, m_pSubAgentList[i].szName);
127 // On NetWare, DLClose will unload module, and if first instance
128 // was loaded from the same module, it will be killed, so
129 // we don't call DLClose on NetWare
130 #ifndef _NETWARE
131 DLClose(hModule);
132 #endif
133 }
134 }
135 else
136 {
137 nxlog_write(MSG_SUBAGENT_BAD_MAGIC, EVENTLOG_ERROR_TYPE,
138 "s", pszModuleName);
139 DLClose(hModule);
140 }
141 }
142 else
143 {
144 nxlog_write(MSG_SUBAGENT_REGISTRATION_FAILED, EVENTLOG_ERROR_TYPE,
145 "s", pszModuleName);
146 DLClose(hModule);
147 }
148
149 return bSuccess;
150 }
151
152 /**
153 * Load subagent
154 */
155 BOOL LoadSubAgent(TCHAR *szModuleName)
156 {
157 HMODULE hModule;
158 BOOL bSuccess = FALSE;
159 TCHAR szErrorText[256];
160
161 #if !defined(_WIN32) && !defined(_NETWARE)
162 TCHAR fullName[MAX_PATH];
163
164 if (_tcschr(szModuleName, _T('/')) == NULL)
165 {
166 // Assume that subagent name without path given
167 // Try to load it from pkglibdir
168 const TCHAR *homeDir = _tgetenv(_T("NETXMS_HOME"));
169 if (homeDir != NULL)
170 {
171 _sntprintf(fullName, MAX_PATH, _T("%s/lib/netxms/%s"), homeDir, szModuleName);
172 }
173 else
174 {
175 _sntprintf(fullName, MAX_PATH, _T("%s/%s"), PKGLIBDIR, szModuleName);
176 }
177 }
178 else
179 {
180 nx_strncpy(fullName, szModuleName, MAX_PATH);
181 }
182 hModule = DLOpen(fullName, szErrorText);
183 #else
184 hModule = DLOpen(szModuleName, szErrorText);
185 #endif
186 if (hModule != NULL)
187 {
188 BOOL (* SubAgentRegister)(NETXMS_SUBAGENT_INFO **, Config *);
189
190 // Under NetWare, we have slightly different subagent
191 // initialization procedure. Because normally two NLMs
192 // cannot export symbols with identical names, we cannot
193 // simply export NxSubAgentRegister in each subagent. Instead,
194 // agent expect to find symbol called NxSubAgentRegister_<module_file_name>
195 // in every subagent. Note that module file name should
196 // be in capital letters.
197 #ifdef _NETWARE
198 char *pExt, szFileName[MAX_PATH], szEntryPoint[MAX_PATH];
199 int iElem, iFlags;
200
201 deconstruct(szModuleName, NULL, NULL, NULL, szFileName, NULL, &iElem, &iFlags);
202 pExt = strrchr(szFileName, '.');
203 if (pExt != NULL)
204 *pExt = 0;
205 strupr(szFileName);
206 sprintf(szEntryPoint, "NxSubAgentRegister_%s", szFileName);
207 SubAgentRegister = (BOOL (*)(NETXMS_SUBAGENT_INFO **, Config *))DLGetSymbolAddr(hModule, szEntryPoint, szErrorText);
208 #else
209 SubAgentRegister = (BOOL (*)(NETXMS_SUBAGENT_INFO **, Config *))DLGetSymbolAddr(hModule, "NxSubAgentRegister", szErrorText);
210 #endif
211
212 if (SubAgentRegister != NULL)
213 {
214 #ifdef _NETWARE
215 bSuccess = InitSubAgent(hModule, szModuleName, SubAgentRegister, szEntryPoint);
216 if (bSuccess)
217 setdontunloadflag(hModule);
218 #else
219 bSuccess = InitSubAgent(hModule, szModuleName, SubAgentRegister, NULL);
220 #endif
221 }
222 else
223 {
224 nxlog_write(MSG_NO_SUBAGENT_ENTRY_POINT, EVENTLOG_ERROR_TYPE, "s", szModuleName);
225 DLClose(hModule);
226 }
227 }
228 else
229 {
230 nxlog_write(MSG_SUBAGENT_LOAD_FAILED, EVENTLOG_ERROR_TYPE, "ss", szModuleName, szErrorText);
231 }
232
233 return bSuccess;
234 }
235
236 /**
237 * Unload all subagents.
238 * This function should be called on shutdown, so we don't care
239 * about deregistering parameters and so on.
240 */
241 void UnloadAllSubAgents()
242 {
243 UINT32 i;
244
245 for(i = 0; i < m_dwNumSubAgents; i++)
246 {
247 #ifdef _NETWARE
248 UnImportPublicObject(m_pSubAgentList[i].hModule, m_pSubAgentList[i].szEntryPoint);
249 cleardontunloadflag(m_pSubAgentList[i].hModule);
250 #endif
251 if (m_pSubAgentList[i].pInfo->shutdown != NULL)
252 m_pSubAgentList[i].pInfo->shutdown();
253 #ifndef _NETWARE
254 DLClose(m_pSubAgentList[i].hModule);
255 #endif
256 }
257 }
258
259 /**
260 * Enumerate loaded subagents
261 */
262 LONG H_SubAgentList(const TCHAR *cmd, const TCHAR *arg, StringList *value)
263 {
264 UINT32 i;
265 TCHAR szBuffer[MAX_PATH + 32];
266
267 for(i = 0; i < m_dwNumSubAgents; i++)
268 {
269 #ifdef __64BIT__
270 _sntprintf(szBuffer, MAX_PATH + 32, _T("%s %s 0x") UINT64X_FMT(_T("016")) _T(" %s"),
271 m_pSubAgentList[i].pInfo->name, m_pSubAgentList[i].pInfo->version,
272 CAST_FROM_POINTER(m_pSubAgentList[i].hModule, QWORD), m_pSubAgentList[i].szName);
273 #else
274 _sntprintf(szBuffer, MAX_PATH + 32, _T("%s %s 0x%08X %s"),
275 m_pSubAgentList[i].pInfo->name, m_pSubAgentList[i].pInfo->version,
276 CAST_FROM_POINTER(m_pSubAgentList[i].hModule, UINT32), m_pSubAgentList[i].szName);
277 #endif
278 value->add(szBuffer);
279 }
280 return SYSINFO_RC_SUCCESS;
281 }
282
283 /**
284 * Enumerate loaded subagents as a table
285 */
286 LONG H_SubAgentTable(const TCHAR *cmd, const TCHAR *arg, Table *value)
287 {
288 value->addColumn(_T("NAME"));
289 value->addColumn(_T("VERSION"));
290 value->addColumn(_T("FILE"));
291
292 for(UINT32 i = 0; i < m_dwNumSubAgents; i++)
293 {
294 value->addRow();
295 value->set(0, m_pSubAgentList[i].pInfo->name);
296 value->set(1, m_pSubAgentList[i].pInfo->version);
297 value->set(2, m_pSubAgentList[i].szName);
298 }
299 return SYSINFO_RC_SUCCESS;
300 }
301
302 /**
303 * Handler for Agent.IsSubagentLoaded
304 */
305 LONG H_IsSubagentLoaded(const TCHAR *pszCmd, const TCHAR *pArg, TCHAR *pValue)
306 {
307 TCHAR name[256];
308
309 AgentGetParameterArg(pszCmd, 1, name, 256);
310 int rc = 0;
311 for(UINT32 i = 0; i < m_dwNumSubAgents; i++)
312 {
313 if (!_tcsicmp(name, m_pSubAgentList[i].pInfo->name))
314 {
315 rc = 1;
316 break;
317 }
318 }
319 ret_int(pValue, rc);
320 return SYSINFO_RC_SUCCESS;
321 }
322
323 /**
324 * Process unknown command by subagents
325 */
326 BOOL ProcessCmdBySubAgent(UINT32 dwCommand, CSCPMessage *pRequest, CSCPMessage *pResponse, void *session)
327 {
328 BOOL bResult = FALSE;
329 UINT32 i;
330
331 for(i = 0; (i < m_dwNumSubAgents) && (!bResult); i++)
332 {
333 if (m_pSubAgentList[i].pInfo->commandHandler != NULL)
334 bResult = m_pSubAgentList[i].pInfo->commandHandler(dwCommand, pRequest, pResponse, session);
335 }
336 return bResult;
337 }