unfinished client side API for tabular DCI; server-side part of API finished
[public/netxms.git] / src / agent / subagents / oracle / main.cpp
CommitLineData
4d1753cd
VK
1/*\r
2** NetXMS subagent for Oracle monitoring\r
3** Copyright (C) 2009-2012 Raden Solutions\r
4**/\r
5\r
6#include "oracle_subagent.h"\r
7\r
8CONDITION g_shutdownCondition;\r
9MUTEX g_paramAccessMutex;\r
10int g_dbCount;\r
11DB_DRIVER g_driverHandle = NULL;\r
12DatabaseInfo g_dbInfo[MAX_DATABASES];\r
13DatabaseData g_dbData[MAX_DATABASES];\r
14\r
15THREAD_RESULT THREAD_CALL queryThread(void *arg);\r
16\r
17DBParameterGroup g_paramGroup[] = {\r
18 {\r
19 700, _T("Oracle.Sessions."),\r
20 _T("select ") DB_NULLARG_MAGIC _T(" ValueName, count(*) Count from v$session"),\r
21 2, { NULL }, 0\r
22 },\r
23 {\r
24 700, _T("Oracle.Cursors."),\r
25 _T("select ") DB_NULLARG_MAGIC _T(" ValueName, sum(a.value) Count from v$sesstat a, v$statname b, v$session s where a.statistic# = b.statistic# and s.sid=a.sid and b.name = 'opened cursors current'"),\r
26 2, { NULL }, 0\r
27 },\r
28 {\r
29 700, _T("Oracle.DBInfo."), // E001_DbInstanceStat\r
30 _T("select ") DB_NULLARG_MAGIC _T(" ValueName, name Name, to_char(created) CreateDate, log_mode LogMode, open_mode OpenMode from v$database"),\r
31 5, { NULL }, 0\r
32 },\r
33 {\r
34 700, _T("Oracle.Instance."),\r
35 _T("select ") DB_NULLARG_MAGIC _T(" ValueName, version Version, status Status, archiver ArchiverStatus, shutdown_pending ShutdownPending from v$instance"),\r
36 5, { NULL }, 0\r
37 },\r
38 {\r
39 1000, _T("Oracle.TableSpaces."),\r
40 _T("select d.tablespace_name ValueName, d.status Status, d.contents Type, to_char(round(used_percent,2)) UsedPct from dba_tablespaces d, dba_tablespace_usage_metrics m where d.tablespace_name=m.tablespace_name"),\r
41 3, { NULL }, 0\r
42 },\r
43 {\r
44 700, _T("Oracle.Dual."), // E077_DualExssRowStat\r
45 _T("select ") DB_NULLARG_MAGIC _T(" ValueName, decode(count(*),1,0,1) ExcessRows from dual"),\r
46 1, { NULL }, 0\r
47 },\r
48 {\r
49 700, _T("Oracle.Performance."), // E086_PhysReadsRate (HP OV gives per minute), but this query gives current count; Oracle E088_LogicReadsRate \r
50 _T("select ") DB_NULLARG_MAGIC _T(" ValueName, (select s.value PhysReads from v$sysstat s, v$statname n where n.name='physical reads' and n.statistic#=s.statistic#) PhysReads, ")\r
2acc690f
AK
51 _T("(select s.value LogicReads from v$sysstat s, v$statname n where n.name='session logical reads' and n.statistic#=s.statistic#) LogicReads, ")\r
52 _T("(select round((sum(decode(name,'consistent gets',value,0))+sum(decode(name,'db block gets',value,0))-sum(decode(name,'physical reads',value, 0)))/(sum(decode(name,'consistent gets',value,0))+sum(decode(name,'db block gets',value,0)))*100,2) from v$sysstat) CacheHitRatio ")\r
4d1753cd 53 _T("from DUAL "),\r
2acc690f 54 3, { NULL }, 0\r
4d1753cd
VK
55 },\r
56 {\r
57 700, _T("Oracle.CriticalStats."), // E007_TblSpcStatusCnt, E014_DataFStatusCnt, E016_SegmntExtendCnt, E067_RBSegmntStatCnt, E061_AutoArchvStatus, "Grid: Datafiles Need Media Recovery"\r
58 _T("select ") DB_NULLARG_MAGIC _T(" ValueName, (select count(*) TSOFF from DBA_TABLESPACES where status <> 'ONLINE') TSOffCount, ")\r
59 _T("(select count(*) DFOFF from V$DATAFILE where status not in ('ONLINE','SYSTEM')) DFOffCount, ")\r
60 _T("(select count(*) from DBA_SEGMENTS where max_extents = extents) FullSegmentsCount, ")\r
61 _T("(select count(*) from DBA_ROLLBACK_SEGS where status <> 'ONLINE') RBSegsNotOnlineCount, ")\r
62 _T("decode(sign(decode((select upper(log_mode) from v$database),'ARCHIVELOG',1,0)-")\r
63 _T("decode((select upper(value) from v$parameter where upper(name)='LOG_ARCHIVE_START'),'TRUE',1,0)),1, 1, 0) AutoArchivingOff, ")\r
64 _T("(SELECT count(file#) from v$datafile_header where recover ='YES') DatafilesNeedMediaRecovery, ")\r
65 _T("(SELECT count(*) FROM dba_jobs where NVL(failures,0) <> 0) FailedJobs ")\r
66 _T("from DUAL"), \r
67 5, { NULL }, 0\r
68 },\r
69 0\r
70};\r
71\r
72//\r
73// Handler functions\r
74//\r
75\r
76LONG getParameters(const TCHAR *parameter, const TCHAR *argument, TCHAR *value)\r
77{\r
78 LONG ret = SYSINFO_RC_UNSUPPORTED;\r
79 TCHAR dbId[MAX_STR];\r
80 TCHAR entity[MAX_STR];\r
81\r
82 // Get id of the database requested\r
83 if (!AgentGetParameterArg(parameter, 1, dbId, MAX_STR))\r
84 return ret;\r
85 if (!AgentGetParameterArg(parameter, 2, entity, MAX_STR) || entity[0] == _T('\0'))\r
86 nx_strncpy(entity, DB_NULLARG_MAGIC, MAX_STR);\r
87\r
a4be813d 88 AgentWriteDebugLog(7, _T("%s: got request for params: dbid='%s', param='%s'"), MYNAMESTR, dbId, parameter);\r
4d1753cd
VK
89\r
90 // Loop through databases and find an entry in g_dbInfo[] for this id\r
91 for (int i = 0; i <= g_dbCount; i++)\r
92 {\r
93 if (!_tcsnicmp(g_dbInfo[i].id, dbId, MAX_STR)) // found DB\r
94 {\r
6534de9b
AK
95 if (argument[0] == _T('R'))\r
96 {\r
97 ret_string(value, g_dbInfo[i].connected ? _T("YES") : _T("NO"));\r
98 ret = SYSINFO_RC_SUCCESS;\r
99 }\r
4d1753cd 100 // Loop through parameter groups and check whose prefix matches the parameter requested\r
6534de9b 101 for (int k = 0; argument[0] == _T('X') && g_paramGroup[k].prefix; k++)\r
4d1753cd
VK
102 {\r
103 if (!_tcsnicmp(g_paramGroup[k].prefix, parameter, _tcslen(g_paramGroup[k].prefix))) // found prefix\r
104 {\r
105 MutexLock(g_dbInfo[i].accessMutex);\r
106 // Loop through the values\r
a4be813d 107 AgentWriteDebugLog(9, _T("%s: valuecount %d"), MYNAMESTR, g_paramGroup[k].valueCount[i]);\r
4d1753cd
VK
108 for (int j = 0; j < g_paramGroup[k].valueCount[i]; j++)\r
109 {\r
110 StringMap* map = (g_paramGroup[k].values[i])[j].attrs;\r
111 TCHAR* name = (g_paramGroup[k].values[i])[j].name;\r
112 if (!_tcsnicmp(name, entity, MAX_STR)) // found value which matches the parameters argument\r
113 {\r
114 TCHAR key[MAX_STR];\r
115 nx_strncpy(key, parameter + _tcslen(g_paramGroup[k].prefix), MAX_STR);\r
116 TCHAR* place = _tcschr(key, _T('('));\r
117 if (place != NULL)\r
118 {\r
119 *place = _T('\0');\r
120 const TCHAR* dbval = map->get(key);\r
121 ret_string(value, dbval);\r
122 ret = SYSINFO_RC_SUCCESS;\r
123 }\r
124 break;\r
125 }\r
126 }\r
127 MutexUnlock(g_dbInfo[i].accessMutex);\r
128\r
129 break;\r
130 }\r
131 }\r
132 break;\r
133 }\r
134 }\r
135\r
136 return ret;\r
137}\r
138\r
139\r
140//\r
141// Subagent initialization\r
142//\r
143\r
144static BOOL SubAgentInit(Config *config)\r
145{\r
146 BOOL result = TRUE;\r
147 static DatabaseInfo info;\r
148 int i;\r
149 static NX_CFG_TEMPLATE configTemplate[] = \r
150 {\r
151 { _T("Id"), CT_STRING, 0, 0, MAX_STR, 0, info.id }, \r
152 { _T("Name"), CT_STRING, 0, 0, MAX_STR, 0, info.name }, \r
6534de9b 153 { _T("TnsName"), CT_STRING, 0, 0, MAX_STR, 0, info.name }, \r
4d1753cd
VK
154 { _T("UserName"), CT_STRING, 0, 0, MAX_USERNAME, 0, info.username }, \r
155 { _T("Password"), CT_STRING, 0, 0, MAX_PASSWORD, 0, info.password },\r
156 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }\r
157 };\r
158\r
159 // Init db driver\r
160#ifdef _WIN32\r
161 g_driverHandle = DBLoadDriver(_T("oracle.ddr"), NULL, TRUE, NULL, NULL);\r
162#else\r
163 g_driverHandle = DBLoadDriver(LIBDIR "/libnxddr_oracle.so", NULL, TRUE, NULL, NULL);\r
164#endif\r
165 if (g_driverHandle == NULL)\r
166 {\r
167 AgentWriteLog(EVENTLOG_ERROR_TYPE, _T("%s: failed to load db driver"), MYNAMESTR);\r
168 result = FALSE;\r
169 }\r
170\r
171 if (result)\r
172 {\r
173 g_shutdownCondition = ConditionCreate(TRUE);\r
174 }\r
175\r
04f06779
VK
176 // Load configuration from "oracle" section to allow simple configuration\r
177 // of one database without XML includes\r
178 memset(&info, 0, sizeof(info));\r
179 g_dbCount = -1;\r
180 if (config->parseTemplate(_T("ORACLE"), configTemplate))\r
181 {\r
2f67972d 182 if (info.name[0] != 0)\r
04f06779
VK
183 {\r
184 if (info.id[0] == 0)\r
2f67972d 185 _tcscpy(info.id, info.name);\r
04f06779
VK
186 memcpy(&g_dbInfo[++g_dbCount], &info, sizeof(DatabaseInfo));\r
187 g_dbInfo[g_dbCount].accessMutex = MutexCreate();\r
188 }\r
189 }\r
190\r
a4be813d
AK
191 // Load full-featured XML configuration\r
192 if (g_dbCount == -1) // Didn't load anything from the .conf file\r
4d1753cd 193 {\r
a4be813d 194 for (i = 1; result && i <= MAX_DATABASES; i++)\r
4d1753cd 195 {\r
a4be813d
AK
196 TCHAR section[MAX_STR];\r
197 memset((void*)&info, 0, sizeof(info));\r
198 _sntprintf(section, MAX_STR, _T("oracle/databases/database#%d"), i);\r
199 if ((result = config->parseTemplate(section, configTemplate)) != TRUE)\r
200 {\r
201 AgentWriteLog(EVENTLOG_ERROR_TYPE, _T("%s: error parsing configuration template"), MYNAMESTR);\r
202 return FALSE;\r
203 }\r
204 if (info.name[0] != _T('\0'))\r
205 memcpy((void*)&g_dbInfo[++g_dbCount], (void*)&info, sizeof(info));\r
206 else\r
207 continue;\r
208 if (info.username[0] == '\0' || info.password[0] == '\0')\r
209 {\r
210 AgentWriteLog(EVENTLOG_ERROR_TYPE, _T("%s: error getting username and/or password for "), MYNAMESTR);\r
211 result = FALSE;\r
212 }\r
213 if (result && (g_dbInfo[g_dbCount].accessMutex = MutexCreate()) == NULL)\r
214 {\r
215 AgentWriteLog(EVENTLOG_ERROR_TYPE, _T("%s: failed to create mutex (%d)"), MYNAMESTR, i);\r
216 result = FALSE;\r
217 }\r
4d1753cd
VK
218 }\r
219 }\r
220\r
221 // Exit if no usable configuration found\r
222 if (result && g_dbCount < 0)\r
223 {\r
224 AgentWriteLog(EVENTLOG_ERROR_TYPE, _T("%s: no databases to monitor"), MYNAMESTR);\r
225 result = FALSE;\r
226 }\r
227\r
228 // Run query thread for each database configured\r
229 for (i = 0; result && i <= g_dbCount; i++)\r
230 {\r
231 g_dbInfo[i].queryThreadHandle = ThreadCreateEx(queryThread, 0, CAST_TO_POINTER(i, void *));\r
232 }\r
233\r
234 return result;\r
235}\r
236\r
237\r
238//\r
239// Shutdown handler\r
240//\r
241\r
242static void SubAgentShutdown(void)\r
243{\r
244 AgentWriteLog(EVENTLOG_INFORMATION_TYPE, _T("%s: shutting down"), MYNAMESTR);\r
245 ConditionSet(g_shutdownCondition);\r
246 for (int i = 0; i <= g_dbCount; i++)\r
247 {\r
248 ThreadJoin(g_dbInfo[i].queryThreadHandle);\r
249 MutexDestroy(g_dbInfo[i].accessMutex);\r
250 }\r
251 ConditionDestroy(g_shutdownCondition);\r
252}\r
253\r
254//\r
255// Figure out Oracle DBMS version\r
256//\r
257\r
258static int getOracleVersion(DB_HANDLE handle) \r
259{\r
260 TCHAR versionString[32];\r
261\r
262 DB_RESULT result = DBSelect(handle,_T("select version from v$instance"));\r
263 if (result == NULL) \r
264 {\r
2f67972d 265 AgentWriteLog(EVENTLOG_WARNING_TYPE, _T("%s: query from v$instance failed"), MYNAMESTR);\r
4d1753cd
VK
266 return 700; // assume Oracle 7.0 by default\r
267 }\r
268\r
269 DBGetField(result, 0, 0, versionString, 32);\r
270 int major = 0, minor = 0;\r
271 _stscanf(versionString, _T("%d.%d"), &major, &minor);\r
272 DBFreeResult(result);\r
273\r
274 return major * 100 + minor * 10;\r
275}\r
276\r
277//\r
278// Thread for SQL queries\r
279//\r
280\r
281THREAD_RESULT THREAD_CALL queryThread(void* arg)\r
282{\r
283 int dbIndex = CAST_FROM_POINTER(arg, int);\r
284 DatabaseInfo& db = g_dbInfo[dbIndex];\r
285 const DWORD pollInterval = 60 * 1000L; // 1 minute\r
286 int waitTimeout;\r
287 QWORD startTimeMs;\r
288 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];\r
289\r
290 while (true)\r
291 {\r
6534de9b 292 db.handle = DBConnect(g_driverHandle, db.name, NULL /* db.server */, db.username, db.password, NULL, errorText);\r
a4be813d 293 DBEnableReconnect(db.handle, false);\r
4d1753cd
VK
294 if (db.handle != NULL)\r
295 {\r
a4be813d 296 AgentWriteLog(EVENTLOG_INFORMATION_TYPE, _T("%s: connected to DB '%s'"), MYNAMESTR, db.name);\r
4d1753cd
VK
297 db.connected = true;\r
298 db.version = getOracleVersion(db.handle);\r
299 }\r
300\r
301 while (db.connected)\r
302 {\r
303 startTimeMs = GetCurrentTimeMs();\r
304\r
305 // Do queries\r
306 if (!(db.connected = getParametersFromDB(dbIndex)))\r
307 {\r
308 break;\r
309 }\r
310\r
311 waitTimeout = pollInterval - DWORD(GetCurrentTimeMs() - startTimeMs);\r
312 if (ConditionWait(g_shutdownCondition, waitTimeout < 0 ? 1 : waitTimeout))\r
313 goto finish;\r
314 }\r
315\r
316 // Try to reconnect every 30 secs\r
317 if (ConditionWait(g_shutdownCondition, DWORD(30 * 1000)))\r
318 break;\r
319 }\r
320\r
321finish:\r
322 if (db.connected && db.handle != NULL)\r
323 {\r
324 DBDisconnect(db.handle);\r
325 }\r
326\r
327 return THREAD_OK;\r
328}\r
329\r
330\r
331bool getParametersFromDB( int dbIndex )\r
332{\r
333 bool ret = true;\r
334 DatabaseInfo& info = g_dbInfo[dbIndex];\r
335\r
336 if (!info.connected)\r
337 {\r
338 return false;\r
339 }\r
340\r
341 MutexLock(info.accessMutex);\r
342\r
343 for (int i = 0; g_paramGroup[i].prefix; i++)\r
344 {\r
345 AgentWriteDebugLog(7, _T("%s: got entry for '%s'"), MYNAMESTR, g_paramGroup[i].prefix);\r
346\r
347 if (g_paramGroup[i].version > info.version) // this parameter group is not supported for this DB\r
348 continue; \r
349\r
350 // Release previously allocated array of values for this group\r
2acc690f 351 for (int j = 0; g_paramGroup[i].values[dbIndex] && j < g_paramGroup[i].valueCount[dbIndex]; j++)\r
4d1753cd 352 delete (g_paramGroup[i].values[dbIndex])[j].attrs;\r
2acc690f 353 safe_free_and_null(g_paramGroup[i].values[dbIndex]);\r
4d1753cd
VK
354\r
355 DB_RESULT queryResult = DBSelect(info.handle, g_paramGroup[i].query);\r
356 if (queryResult == NULL)\r
357 {\r
358 ret = false;\r
359 break;\r
360 }\r
361\r
362 int rows = DBGetNumRows(queryResult);\r
363 g_paramGroup[i].values[dbIndex] = (DBParameter*)malloc(sizeof(DBParameter) * rows);\r
364 g_paramGroup[i].valueCount[dbIndex] = rows;\r
365 for (int j = 0; j < rows; j++)\r
366 {\r
367 TCHAR colname[MAX_STR];\r
368 DBGetField(queryResult, j, 0, (g_paramGroup[i].values[dbIndex])[j].name, MAX_STR);\r
369 (g_paramGroup[i].values[dbIndex])[j].attrs = new StringMap;\r
370 for (int k = 1; DBGetColumnName(queryResult, k, colname, MAX_STR); k++) \r
371 {\r
372 TCHAR colval[MAX_STR];\r
373 DBGetField(queryResult, j, k, colval, MAX_STR);\r
374 // AgentWriteDebugLog(9, _T("%s: getParamsFromDB: colname '%s' ::: colval '%s'"), MYNAMESTR, colname, colval);\r
375 (g_paramGroup[i].values[dbIndex])[j].attrs->set(colname, colval);\r
376 }\r
377 }\r
378\r
379 DBFreeResult(queryResult);\r
380 }\r
381\r
382 MutexUnlock(info.accessMutex);\r
383\r
384 return ret;\r
385}\r
386\r
387//\r
388// Subagent information\r
389//\r
390\r
391\r
392static NETXMS_SUBAGENT_PARAM m_parameters[] =\r
393{\r
394 { _T("Oracle.Sessions.Count(*)"), getParameters, "X", DCI_DT_INT, _T("Oracle/Sessions: Number of sessions opened") },\r
395 { _T("Oracle.Cursors.Count(*)"), getParameters, "X", DCI_DT_INT, _T("Oracle/Cursors: Current number of opened cursors systemwide") },\r
6534de9b 396 { _T("Oracle.DBInfo.IsReachable(*)"), getParameters, "R", DCI_DT_STRING, _T("Oracle/Info: Database is reachable") },\r
4d1753cd
VK
397 { _T("Oracle.DBInfo.Name(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/Info: Database name") },\r
398 { _T("Oracle.DBInfo.CreateDate(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/Info: Database creation date") },\r
399 { _T("Oracle.DBInfo.LogMode(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/Info: Database log mode") },\r
400 { _T("Oracle.DBInfo.OpenMode(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/Info: Database open mode") },\r
401 { _T("Oracle.TableSpaces.Status(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/Tablespaces: Status") },\r
402 { _T("Oracle.TableSpaces.Type(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/Tablespaces: Type") },\r
403 { _T("Oracle.TableSpaces.UsedPct(*)"), getParameters, "X", DCI_DT_INT, _T("Oracle/Tablespaces: Percentage used") },\r
404 { _T("Oracle.Instance.Version(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/Instance: DBMS Version") },\r
405 { _T("Oracle.Instance.Status(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/Instance: Status") },\r
406 { _T("Oracle.Instance.ArchiverStatus(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/Instance: Archiver status") },\r
407 { _T("Oracle.Instance.ShutdownPending(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/Instance: Is shutdown pending") },\r
408 { _T("Oracle.CriticalStats.TSOffCount(*)"), getParameters, "X", DCI_DT_INT, _T("Oracle/CriticalStats: Number of offline tablespaces") },\r
409 { _T("Oracle.CriticalStats.DFOffCount(*)"), getParameters, "X", DCI_DT_INT, _T("Oracle/CriticalStats: Number of offline datafiles") },\r
410 { _T("Oracle.CriticalStats.FullSegmentsCount(*)"), getParameters, "X", DCI_DT_INT64, _T("Oracle/CriticalStats: Number of segments that cannot extend") },\r
411 { _T("Oracle.CriticalStats.RBSegsNotOnlineCount(*)"), getParameters, "X", DCI_DT_INT64, _T("Oracle/CriticalStats: Number of rollback segments not online") },\r
412 { _T("Oracle.CriticalStats.AutoArchivingOff(*)"), getParameters, "X", DCI_DT_STRING, _T("Oracle/CriticalStats: Archive logs enabled but auto archiving off ") },\r
413 { _T("Oracle.CriticalStats.DatafilesNeedMediaRecovery(*)"), getParameters, "X", DCI_DT_INT64, _T("Oracle/CriticalStats: Number of datafiles that need media recovery") },\r
414 { _T("Oracle.CriticalStats.FailedJobs(*)"), getParameters, "X", DCI_DT_INT64, _T("Oracle/CriticalStats: Number of failed jobs") },\r
415 { _T("Oracle.Dual.ExcessRows(*)"), getParameters, "X", DCI_DT_INT64, _T("Oracle/Dual: Excessive rows") },\r
416 { _T("Oracle.Performance.PhysReads(*)"), getParameters, "X", DCI_DT_INT64, _T("Oracle/Performance: Number of physical reads") },\r
2acc690f
AK
417 { _T("Oracle.Performance.LogicReads(*)"), getParameters, "X", DCI_DT_INT64, _T("Oracle/Performance: Number of logical reads") },\r
418 { _T("Oracle.Performance.CacheHitRatio(*)"), getParameters, "X", DCI_DT_INT64, _T("Oracle/Performance: Data buffer cache hit ratio") }\r
4d1753cd
VK
419};\r
420\r
421/*\r
422static NETXMS_SUBAGENT_ENUM m_enums[] =\r
423{\r
424};\r
425*/\r
426\r
427static NETXMS_SUBAGENT_INFO m_info =\r
428{\r
429 NETXMS_SUBAGENT_INFO_MAGIC,\r
430 _T("ORACLE"), NETXMS_VERSION_STRING,\r
431 SubAgentInit, SubAgentShutdown, NULL,\r
432 sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM), m_parameters,\r
433 0, NULL,\r
434 /*sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM),\r
435 m_parameters,\r
436 sizeof(m_enums) / sizeof(NETXMS_SUBAGENT_ENUM),\r
437 m_enums,*/\r
438 0, NULL\r
439};\r
440\r
441\r
442//\r
443// Entry point for NetXMS agent\r
444//\r
445\r
446DECLARE_SUBAGENT_ENTRY_POINT(ORACLE)\r
447{\r
448 *ppInfo = &m_info;\r
449 return TRUE;\r
450}\r
451\r
452\r
453//\r
454// DLL entry point\r
455//\r
456\r
457#ifdef _WIN32\r
458\r
459BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)\r
460{\r
461 if (dwReason == DLL_PROCESS_ATTACH)\r
462 DisableThreadLibraryCalls(hInstance);\r
463 return TRUE;\r
464}\r
465\r
466#endif\r