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