mirror of
https://github.com/gnustep/libs-ec.git
synced 2025-02-16 00:21:01 +00:00
1796 lines
45 KiB
Objective-C
1796 lines
45 KiB
Objective-C
|
||
/** Enterprise Control Configuration and Logging
|
||
|
||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||
|
||
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
||
Date: Febrary 2010
|
||
Originally developed from 1996 to 2012 by Brainstorm, and donated to
|
||
the FSF.
|
||
|
||
This file is part of the GNUstep project.
|
||
|
||
This library is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU Lesser General Public
|
||
License as published by the Free Software Foundation; either
|
||
version 3 of the License, or (at your option) any later version.
|
||
|
||
This library is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
Library General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Lesser General Public
|
||
License along with this library; if not, write to the Free
|
||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
Boston, MA 02111 USA.
|
||
|
||
*/
|
||
|
||
#import <Foundation/Foundation.h>
|
||
|
||
#import "config.h"
|
||
|
||
#import "EcAlarm.h"
|
||
#import "EcAlarmDestination.h"
|
||
#import "EcAlarmSinkSNMP.h"
|
||
#import "EcProcess.h"
|
||
#import "EcLogger.h"
|
||
|
||
static EcAlarmSinkSNMP *alarmSink = nil; // The singleton
|
||
static NSLock *classLock = nil;
|
||
|
||
#if defined(WITH_NET_SNMP)
|
||
|
||
@interface EcAlarmSinkSNMP (Private)
|
||
|
||
/* Archives the SNMP data to persistent storage so we can re-load it on
|
||
* startup. Also stores a description of the active alarms for readability.
|
||
*/
|
||
- (void) _store;
|
||
|
||
/* Sends the alarm out as an SNMP trap.
|
||
* If forceClear is YES then sends the trap with a cleared severity
|
||
* irrespective of the actual severity stored in the alarm object.
|
||
*/
|
||
- (void) _trap: (EcAlarm*)alarm forceClear: (BOOL)forceClear;
|
||
|
||
@end
|
||
|
||
/* The ObjC __block define might conflict with system headers ...
|
||
* */
|
||
#if defined(__block)
|
||
#undef __block
|
||
#endif
|
||
|
||
#include <net-snmp/net-snmp-config.h>
|
||
#include <net-snmp/net-snmp-includes.h>
|
||
#include <net-snmp/agent/net-snmp-agent-includes.h>
|
||
#include <net-snmp/library/snmp_assert.h>
|
||
|
||
#include <time.h>
|
||
|
||
static NSString *persistentStore = nil;
|
||
static int32_t notificationID = 0;
|
||
static NSUInteger managedObjectsCount = 0;
|
||
static NSMutableArray *managedObjects = nil;
|
||
static NSString *alarmsOID = nil;
|
||
static NSString *objectsOID = nil;
|
||
static NSString *trapOID = nil;
|
||
|
||
static netsnmp_tdata *alarmsTable = 0;
|
||
static netsnmp_tdata *objectsTable = 0;
|
||
|
||
/* The following scalar variables are made available via SNMP OIDs.
|
||
* The agent will handle all GET and (if applicable) SET requests
|
||
* to these variables, changing the values as needed.
|
||
*/
|
||
static int32_t resyncFlag = 0; /* normally not re-syncing */
|
||
static int resyncTimer = 0; /* seconds since started */
|
||
static int32_t trapSequenceNumber = 0; /* XXX: set default value */
|
||
static int32_t pollHeartBeat = 5; /* heartbeat every 5 minutes */
|
||
|
||
/* SNMP data structure for an alarm table row entry
|
||
*/
|
||
struct alarmsTable_entry
|
||
{
|
||
/* Index
|
||
*/
|
||
int32_t notificationID;
|
||
|
||
/* Column values
|
||
*/
|
||
int32_t perceivedSeverity;
|
||
char firstEventDate[32];
|
||
size_t firstEventDate_len;
|
||
char eventDate[32];
|
||
size_t eventDate_len;
|
||
char managedObject[128];
|
||
size_t managedObject_len;
|
||
int32_t eventTypeID;
|
||
int32_t probableCauseID;
|
||
char specificProblem[256];
|
||
size_t specificProblem_len;
|
||
char proposedRepairAction[256];
|
||
size_t proposedRepairAction_len;
|
||
char additionalText[256];
|
||
size_t additionalText_len;
|
||
char trendIndicator[2];
|
||
size_t trendIndicator_len;
|
||
|
||
int valid;
|
||
};
|
||
|
||
/* SNMP data structure for a managed objects table row entry
|
||
*/
|
||
struct objectsTable_entry
|
||
{
|
||
/* Index
|
||
*/
|
||
char objectID[128];
|
||
size_t objectID_len;
|
||
|
||
int valid;
|
||
};
|
||
|
||
/*
|
||
* function declarations
|
||
*/
|
||
|
||
static BOOL
|
||
heartbeat(time_t now);
|
||
|
||
static void
|
||
init_EcAlarmSink(void);
|
||
|
||
static Netsnmp_Node_Handler alarmsTable_handler;
|
||
|
||
static Netsnmp_Node_Handler objectsTable_handler;
|
||
|
||
static netsnmp_tdata_row *
|
||
alarmsTable_createEntry(int32_t notificationID);
|
||
|
||
static int
|
||
pollHeartBeat_handler(netsnmp_mib_handler *handler,
|
||
netsnmp_handler_registration *reginfo,
|
||
netsnmp_agent_request_info *reqinfo,
|
||
netsnmp_request_info *requests);
|
||
|
||
static netsnmp_tdata_row *
|
||
objectsTable_createEntry(NSString *objectID);
|
||
|
||
/*
|
||
* column number definitions for table alarmsTable
|
||
*/
|
||
#define COLUMN_NOTIFICATIONID 1
|
||
#define COLUMN_PERCEIVEDSEVERITY 2
|
||
#define COLUMN_FIRSTEVENTDATE 3
|
||
#define COLUMN_EVENTDATE 4
|
||
#define COLUMN_MANAGEDOBJECT 5
|
||
#define COLUMN_IDEVENTTYPE 6
|
||
#define COLUMN_IDPROBABLECAUSE 7
|
||
#define COLUMN_SPECIFICPROBLEM 8
|
||
#define COLUMN_PROPOSEDREPAIRACTION 9
|
||
#define COLUMN_ADDITIONALTEXT 10
|
||
#define COLUMN_TRENDINDICATOR 11
|
||
|
||
/*
|
||
* column number definitions for table objectsTable
|
||
*/
|
||
#define COLUMN_OBJECTID 1
|
||
|
||
|
||
@interface EcAlarmSinkSNMP (SNMP)
|
||
|
||
- (BOOL) snmpClearAlarms: (NSString*)managed;
|
||
|
||
- (void) snmpHousekeeping;
|
||
|
||
@end
|
||
|
||
|
||
|
||
|
||
/* alarmTrap stuff
|
||
*/
|
||
static oid snmptrap_oid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
|
||
|
||
static oid *additionalText_oid = 0;
|
||
static size_t additionalText_len = 0;
|
||
static oid *alarmsTable_oid = 0;
|
||
static size_t alarmsTable_len = 0;
|
||
static oid *alarmTrap_oid = 0;
|
||
static size_t alarmTrap_len = 0;
|
||
static oid *eventDate_oid = 0;
|
||
static size_t eventDate_len = 0;
|
||
static oid *eventType_oid = 0;
|
||
static size_t eventType_len = 0;
|
||
static oid *firstEventDate_oid = 0;
|
||
static size_t firstEventDate_len = 0;
|
||
static oid *eventTypeID_oid = 0;
|
||
static size_t eventTypeID_len = 0;
|
||
static oid *probableCauseID_oid = 0;
|
||
static size_t probableCauseID_len = 0;
|
||
static oid *notificationID_oid = 0;
|
||
static size_t notificationID_len = 0;
|
||
static oid *objectID_oid = 0;
|
||
static size_t objectID_len = 0;
|
||
static oid *objectsTable_oid = 0;
|
||
static size_t objectsTable_len = 0;
|
||
static oid *perceivedSeverity_oid = 0;
|
||
static size_t perceivedSeverity_len = 0;
|
||
static oid *pollHeartBeat_oid = 0;
|
||
static size_t pollHeartBeat_len = 0;
|
||
static oid *proposedRepairAction_oid = 0;
|
||
static size_t proposedRepairAction_len = 0;
|
||
static oid *resyncFlag_oid = 0;
|
||
static size_t resyncFlag_len = 0;
|
||
static oid *specificProblem_oid = 0;
|
||
static size_t specificProblem_len = 0;
|
||
static oid *trapSequenceNumber_oid = 0;
|
||
static size_t trapSequenceNumber_len = 0;
|
||
static oid *trendIndicator_oid = 0;
|
||
static size_t trendIndicator_len = 0;
|
||
|
||
static int
|
||
logSNMP(int major, int minor, void* server, void* client)
|
||
{
|
||
struct snmp_log_message *slm = (struct snmp_log_message *)server;
|
||
|
||
if (nil == EcProc)
|
||
{
|
||
NSLog(@"%s", slm->msg);
|
||
}
|
||
else
|
||
{
|
||
switch (slm->priority)
|
||
{
|
||
case LOG_EMERG:
|
||
case LOG_ALERT:
|
||
case LOG_CRIT:
|
||
[[EcLogger loggerForType: LT_ALERT] log: @"%s", slm->msg];
|
||
break;
|
||
|
||
case LOG_ERR:
|
||
[[EcLogger loggerForType: LT_ERROR] log: @"%s", slm->msg];
|
||
break;
|
||
|
||
case LOG_WARNING:
|
||
case LOG_NOTICE:
|
||
case LOG_INFO:
|
||
case LOG_DEBUG:
|
||
default:
|
||
NSLog(@"%s", slm->msg); break;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static const char *
|
||
stringFromDate(NSDate *d)
|
||
{
|
||
NSCalendarDate *c;
|
||
|
||
c = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate:
|
||
[d timeIntervalSinceReferenceDate]];
|
||
[c setCalendarFormat: @"%Y%m%d %H:%M:%S"];
|
||
return [[c description] UTF8String];
|
||
}
|
||
|
||
/* Function to send heartbeat alarm iff the pollHeartBeat interval has passed.
|
||
*/
|
||
static BOOL
|
||
heartbeat(time_t now)
|
||
{
|
||
static time_t last = 0;
|
||
struct tm *t;
|
||
char timestamp[18];
|
||
netsnmp_variable_list *var_list = NULL;
|
||
const char *trapName = "HEARTBEAT TRAP";
|
||
const int32_t cause = 0;
|
||
const int32_t notification = 0;
|
||
const int32_t severity = 4;
|
||
const int32_t eventType = 2;
|
||
|
||
/* Build current timestamp and send a heartbeat
|
||
*/
|
||
now = time(0);
|
||
if (((now - last) / 60) < pollHeartBeat)
|
||
{
|
||
return NO; /* Not yet time for a heartbeat */
|
||
}
|
||
last = now;
|
||
t = gmtime(&now);
|
||
sprintf(timestamp, "%04d%02d%02d %02d:%02d:%02d",
|
||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||
t->tm_hour, t->tm_min, t->tm_sec);
|
||
|
||
/*
|
||
* Set the snmpTrapOid.0 value
|
||
*/
|
||
snmp_varlist_add_variable(&var_list,
|
||
snmptrap_oid, OID_LENGTH(snmptrap_oid),
|
||
ASN_OBJECT_ID,
|
||
(u_char*)alarmTrap_oid,
|
||
alarmTrap_len * sizeof(oid));
|
||
|
||
if (++trapSequenceNumber <= 0) trapSequenceNumber = 1;
|
||
snmp_varlist_add_variable(&var_list,
|
||
trapSequenceNumber_oid,
|
||
trapSequenceNumber_len,
|
||
ASN_INTEGER,
|
||
(u_char*)&trapSequenceNumber,
|
||
sizeof(trapSequenceNumber));
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
notificationID_oid,
|
||
notificationID_len, ASN_INTEGER,
|
||
(u_char*)¬ification, /* Special for heartbeat */
|
||
sizeof(notification));
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
perceivedSeverity_oid,
|
||
perceivedSeverity_len,
|
||
ASN_INTEGER,
|
||
(u_char*)&severity, /* warning */
|
||
sizeof(severity));
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
firstEventDate_oid,
|
||
firstEventDate_len,
|
||
ASN_OCTET_STR,
|
||
0, /* not required */
|
||
0);
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
eventDate_oid, eventDate_len,
|
||
ASN_OCTET_STR,
|
||
(u_char*)timestamp,
|
||
strlen(timestamp));
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
objectID_oid, objectID_len,
|
||
ASN_OCTET_STR,
|
||
0, /* not required */
|
||
0);
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
eventTypeID_oid, eventTypeID_len,
|
||
ASN_INTEGER,
|
||
(u_char*)&eventType, /* heartbeat */
|
||
sizeof(eventType));
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
probableCauseID_oid,
|
||
probableCauseID_len, ASN_INTEGER,
|
||
(u_char*)&cause,
|
||
sizeof(cause));
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
specificProblem_oid,
|
||
specificProblem_len,
|
||
ASN_OCTET_STR,
|
||
0, /* not required */
|
||
0);
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
proposedRepairAction_oid,
|
||
proposedRepairAction_len,
|
||
ASN_OCTET_STR,
|
||
0, /* not required */
|
||
0);
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
additionalText_oid,
|
||
additionalText_len,
|
||
ASN_OCTET_STR,
|
||
(u_char*)trapName,
|
||
strlen(trapName));
|
||
|
||
snmp_varlist_add_variable(&var_list,
|
||
trendIndicator_oid,
|
||
trendIndicator_len,
|
||
ASN_OCTET_STR,
|
||
0, /* not required */
|
||
0);
|
||
/*
|
||
* Send the trap to the list of configured destinations
|
||
* and clean up
|
||
*/
|
||
DEBUGMSGTL(("EcAlarmSinkHeartbeat", "Sending trap.\n"));
|
||
send_v2trap(var_list);
|
||
snmp_free_varbind(var_list);
|
||
return YES;
|
||
}
|
||
|
||
static void
|
||
setAlarmTableEntry(netsnmp_tdata_row *row, EcAlarm *alarm)
|
||
{
|
||
struct alarmsTable_entry *e;
|
||
const char *s;
|
||
|
||
[alarm setExtra: (void*)row];
|
||
[alarm freeze]; // Once it's in the snmp table, no more change
|
||
e = (struct alarmsTable_entry *)row->data;
|
||
|
||
e->notificationID = [alarm notificationID];
|
||
|
||
e->perceivedSeverity = [alarm perceivedSeverity];
|
||
|
||
s = stringFromDate([alarm firstEventDate]);
|
||
strcpy(e->firstEventDate, s);
|
||
e->firstEventDate_len = strlen(s);
|
||
|
||
s = stringFromDate([alarm eventDate]);
|
||
strcpy(e->eventDate, s);
|
||
e->eventDate_len = strlen(s);
|
||
|
||
s = [[alarm managedObject] UTF8String];
|
||
strcpy(e->managedObject, s);
|
||
e->managedObject_len = strlen(s);
|
||
|
||
e->eventTypeID = [alarm eventType];
|
||
|
||
e->probableCauseID = [alarm probableCause];
|
||
|
||
s = [[alarm specificProblem] UTF8String];
|
||
strcpy(e->specificProblem, s);
|
||
e->specificProblem_len = strlen(s);
|
||
|
||
s = [[alarm proposedRepairAction] UTF8String];
|
||
strcpy(e->proposedRepairAction, s);
|
||
e->proposedRepairAction_len = strlen(s);
|
||
|
||
s = [[alarm additionalText] UTF8String];
|
||
strcpy(e->additionalText, s);
|
||
e->additionalText_len = strlen(s);
|
||
|
||
e->trendIndicator[0] = [alarm trendIndicator];
|
||
if (0 == e->trendIndicator[0])
|
||
{
|
||
e->trendIndicator_len = 0;
|
||
}
|
||
else
|
||
{
|
||
e->trendIndicator_len = 1;
|
||
e->trendIndicator[1] = 0;
|
||
}
|
||
}
|
||
|
||
/* Regular timer called at one second intervals to check for updates from
|
||
* alarm sources, update SNMP tables, generate alerts, and send heartbeats
|
||
*/
|
||
static void
|
||
housekeeping(unsigned int clientreg, void *clientarg)
|
||
{
|
||
if (0 == resyncFlag)
|
||
{
|
||
resyncTimer = 0;
|
||
}
|
||
else
|
||
{
|
||
/* We automatically leave resync mode after five minutes (300 seconds)
|
||
* if resync mode has not been turned off via SNMP.
|
||
*/
|
||
if (300 <= ++resyncTimer)
|
||
{
|
||
resyncTimer = 0;
|
||
resyncFlag = 0;
|
||
}
|
||
}
|
||
[alarmSink snmpHousekeeping];
|
||
}
|
||
|
||
|
||
static void
|
||
init_EcAlarmSink(void)
|
||
{
|
||
netsnmp_handler_registration *reg;
|
||
netsnmp_table_registration_info *tinfo;
|
||
netsnmp_watcher_info *winfo;
|
||
NSString *oidString;
|
||
NSUserDefaults *defaults;
|
||
NSArray *array;
|
||
oid *oids;
|
||
int len;
|
||
int i;
|
||
|
||
defaults = [EcProc cmdDefaults];
|
||
|
||
/* First convert the trap OID from dotted integer format to an array
|
||
* of net-snmp oid values.
|
||
*/
|
||
oidString = trapOID = [[defaults stringForKey: @"TrapOID"] copy];
|
||
if (nil == oidString) oidString = trapOID = @"1.3.6.1.4.1.39543.3.0.1";
|
||
array = [oidString componentsSeparatedByString: @"."];
|
||
alarmTrap_len = [array count];
|
||
alarmTrap_oid = (oid*)malloc(sizeof(oid) * alarmTrap_len);
|
||
for (i = 0; i < alarmTrap_len; i++)
|
||
{
|
||
alarmTrap_oid[i] = [[array objectAtIndex: i] intValue];
|
||
}
|
||
|
||
/* Now use the dotted integer format 'alarms' OID as the basis to set up
|
||
* all the alarm data OIDs.
|
||
* The OID buffer needs to allow for two more OIDs after the 'alarms'
|
||
* OID because the alarmsTable (in the alarms OID) has entries in it.
|
||
*/
|
||
oidString = alarmsOID = [[defaults stringForKey: @"AlarmsOID"] copy];
|
||
if (nil == oidString) oidString = alarmsOID = @"1.3.6.1.4.1.39543.1";
|
||
array = [oidString componentsSeparatedByString: @"."];
|
||
len = [array count];
|
||
oids = (oid*)malloc(sizeof(oid) * (len + 2));
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
oids[i] = [[array objectAtIndex: i] intValue];
|
||
}
|
||
oids[len] = 0; // alarmsTable
|
||
oids[len + 1] = 0; // alarmsEntry
|
||
|
||
alarmsTable_len = len + 1;
|
||
alarmsTable_oid = (oid*)malloc(sizeof(oid) * alarmsTable_len);
|
||
memcpy(alarmsTable_oid, oids, sizeof(oid) * len + 1);
|
||
alarmsTable_oid[len] = 1;
|
||
|
||
resyncFlag_len = len + 1;
|
||
resyncFlag_oid = (oid*)malloc(sizeof(oid) * resyncFlag_len);
|
||
memcpy(resyncFlag_oid, oids, sizeof(oid) * len);
|
||
resyncFlag_oid[len] = 2;
|
||
|
||
trapSequenceNumber_len = len + 1;
|
||
trapSequenceNumber_oid = (oid*)malloc(sizeof(oid) * trapSequenceNumber_len);
|
||
memcpy(trapSequenceNumber_oid, oids, sizeof(oid) * len);
|
||
trapSequenceNumber_oid[len] = 3;
|
||
|
||
pollHeartBeat_len = len + 1;
|
||
pollHeartBeat_oid = (oid*)malloc(sizeof(oid) * pollHeartBeat_len);
|
||
memcpy(pollHeartBeat_oid, oids, sizeof(oid) * len);
|
||
pollHeartBeat_oid[len] = 4;
|
||
|
||
notificationID_len = len + 3;
|
||
notificationID_oid = (oid*)malloc(sizeof(oid) * notificationID_len);
|
||
memcpy(notificationID_oid, oids, sizeof(oid) * (len + 2));
|
||
notificationID_oid[len+2] = 1;
|
||
|
||
perceivedSeverity_len = len + 3;
|
||
perceivedSeverity_oid = (oid*)malloc(sizeof(oid) * perceivedSeverity_len);
|
||
memcpy(perceivedSeverity_oid, oids, sizeof(oid) * (len + 2));
|
||
perceivedSeverity_oid[len+2] = 2;
|
||
|
||
firstEventDate_len = len + 3;
|
||
firstEventDate_oid = (oid*)malloc(sizeof(oid) * firstEventDate_len);
|
||
memcpy(firstEventDate_oid, oids, sizeof(oid) * (len + 2));
|
||
firstEventDate_oid[len+2] = 3;
|
||
|
||
eventDate_len = len + 3;
|
||
eventDate_oid = (oid*)malloc(sizeof(oid) * eventDate_len);
|
||
memcpy(eventDate_oid, oids, sizeof(oid) * (len + 2));
|
||
eventDate_oid[len+2] = 4;
|
||
|
||
eventType_len = len + 3;
|
||
eventType_oid = (oid*)malloc(sizeof(oid) * eventType_len);
|
||
memcpy(eventType_oid, oids, sizeof(oid) * (len + 2));
|
||
eventType_oid[len+2] = 6;
|
||
|
||
probableCauseID_len = len + 3;
|
||
probableCauseID_oid = (oid*)malloc(sizeof(oid) * probableCauseID_len);
|
||
memcpy(probableCauseID_oid, oids, sizeof(oid) * (len + 2));
|
||
probableCauseID_oid[len+2] = 7;
|
||
|
||
specificProblem_len = len + 3;
|
||
specificProblem_oid = (oid*)malloc(sizeof(oid) * specificProblem_len);
|
||
memcpy(specificProblem_oid, oids, sizeof(oid) * (len + 2));
|
||
specificProblem_oid[len+2] = 8;
|
||
|
||
proposedRepairAction_len = len + 3;
|
||
proposedRepairAction_oid
|
||
= (oid*)malloc(sizeof(oid) * proposedRepairAction_len);
|
||
memcpy(proposedRepairAction_oid, oids, sizeof(oid) * (len + 2));
|
||
proposedRepairAction_oid[len+2] = 9;
|
||
|
||
additionalText_len = len + 3;
|
||
additionalText_oid = (oid*)malloc(sizeof(oid) * additionalText_len);
|
||
memcpy(additionalText_oid, oids, sizeof(oid) * (len + 2));
|
||
additionalText_oid[len+2] = 10;
|
||
|
||
trendIndicator_len = len + 3;
|
||
trendIndicator_oid = (oid*)malloc(sizeof(oid) * trendIndicator_len);
|
||
memcpy(trendIndicator_oid, oids, sizeof(oid) * (len + 2));
|
||
trendIndicator_oid[len+2] = 11;
|
||
|
||
free(oids);
|
||
|
||
oidString = objectsOID = [[defaults stringForKey: @"ObjectsOID"] copy];
|
||
if (nil == oidString) oidString = objectsOID = @"1.3.6.1.4.1.39543.2";
|
||
array = [oidString componentsSeparatedByString: @"."];
|
||
len = [array count];
|
||
objectID_len = len + 3;
|
||
objectID_oid = (oid*)malloc(sizeof(oid) * objectID_len);
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
objectID_oid[i] = [[array objectAtIndex: i] intValue];
|
||
}
|
||
objectID_oid[len] = 1; // objectsTable
|
||
objectID_oid[len+1] = 1; // objectsEntry
|
||
objectID_oid[len+2] = 1; // objectID
|
||
|
||
objectsTable_len = len + 1;
|
||
objectsTable_oid = (oid*)malloc(sizeof(oid) * objectsTable_len);
|
||
memcpy(objectsTable_oid, objectID_oid, sizeof(oid) * objectsTable_len);
|
||
|
||
/* Create the managed objects table as a read-only item for SNMP.
|
||
*/
|
||
reg = netsnmp_create_handler_registration(
|
||
"objectsTable",
|
||
objectsTable_handler,
|
||
objectsTable_oid,
|
||
objectsTable_len,
|
||
HANDLER_CAN_RONLY);
|
||
if (NULL == reg)
|
||
snmp_perror("register objectsTable");
|
||
|
||
objectsTable = netsnmp_tdata_create_table("objectsTable", 0);
|
||
if (NULL == objectsTable)
|
||
snmp_perror("create objectsTable");
|
||
|
||
tinfo = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
|
||
netsnmp_table_helper_add_indexes(tinfo,
|
||
ASN_OCTET_STR, /* index: objectID */
|
||
0);
|
||
tinfo->min_column = COLUMN_OBJECTID;
|
||
tinfo->max_column = COLUMN_OBJECTID;
|
||
if (netsnmp_tdata_register(reg, objectsTable, tinfo) != SNMPERR_SUCCESS)
|
||
snmp_perror("register objectsTable tdata");
|
||
|
||
/* Create the alarms table as a read-only item for SNMP.
|
||
*/
|
||
reg = netsnmp_create_handler_registration(
|
||
"alarmsTable",
|
||
alarmsTable_handler,
|
||
alarmsTable_oid,
|
||
alarmsTable_len,
|
||
HANDLER_CAN_RONLY);
|
||
if (NULL == reg)
|
||
snmp_perror("register alarmsTable");
|
||
|
||
alarmsTable = netsnmp_tdata_create_table("alarmsTable", 0);
|
||
if (NULL == alarmsTable)
|
||
snmp_perror("create alarmsTable");
|
||
|
||
tinfo = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
|
||
netsnmp_table_helper_add_indexes(tinfo,
|
||
ASN_INTEGER, /* index: notificationID */
|
||
0);
|
||
tinfo->min_column = COLUMN_NOTIFICATIONID;
|
||
tinfo->max_column = COLUMN_TRENDINDICATOR;
|
||
if (netsnmp_tdata_register(reg, alarmsTable, tinfo) != SNMPERR_SUCCESS)
|
||
snmp_perror("register alarmsTable tdata");
|
||
|
||
/* Register scalar watchers for each of the MIB objects.
|
||
*/
|
||
reg = netsnmp_create_handler_registration(
|
||
"resyncFlag",
|
||
NULL,
|
||
resyncFlag_oid,
|
||
resyncFlag_len,
|
||
HANDLER_CAN_RWRITE);
|
||
if (NULL == reg)
|
||
snmp_perror("register resyncFlag");
|
||
|
||
winfo = netsnmp_create_watcher_info(&resyncFlag,
|
||
sizeof(int32_t), ASN_INTEGER, WATCHER_FIXED_SIZE);
|
||
if (netsnmp_register_watched_scalar(reg, winfo) != MIB_REGISTERED_OK)
|
||
snmp_perror("register watched resyncFlag");
|
||
|
||
reg = netsnmp_create_handler_registration(
|
||
"trapSequenceNumber",
|
||
NULL,
|
||
trapSequenceNumber_oid,
|
||
trapSequenceNumber_len,
|
||
HANDLER_CAN_RONLY);
|
||
if (NULL == reg)
|
||
snmp_perror("register trapSequenceNumber");
|
||
|
||
winfo = netsnmp_create_watcher_info(&trapSequenceNumber,
|
||
sizeof(int32_t), ASN_INTEGER, WATCHER_FIXED_SIZE);
|
||
if (netsnmp_register_watched_scalar(reg, winfo) != MIB_REGISTERED_OK)
|
||
snmp_perror("register watched trapSequenceNumber");
|
||
|
||
reg = netsnmp_create_handler_registration(
|
||
"pollHeartBeat",
|
||
pollHeartBeat_handler,
|
||
pollHeartBeat_oid,
|
||
pollHeartBeat_len,
|
||
HANDLER_CAN_RWRITE);
|
||
if (NULL == reg)
|
||
snmp_perror("register pollHeartBeat");
|
||
|
||
winfo = netsnmp_create_watcher_info(&pollHeartBeat,
|
||
sizeof(int32_t), ASN_INTEGER, WATCHER_FIXED_SIZE);
|
||
if (netsnmp_register_watched_scalar(reg, winfo) != MIB_REGISTERED_OK)
|
||
snmp_perror("register watched pollHeartBeat");
|
||
|
||
/* get alarms at one second intervals to do housekeeping.
|
||
*/
|
||
if (snmp_alarm_register(1, SA_REPEAT, housekeeping, NULL) == 0)
|
||
snmp_perror("register housekeeping alarm");
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* create a new row in the table
|
||
*/
|
||
static netsnmp_tdata_row *
|
||
alarmsTable_createEntry(int32_t notificationID)
|
||
{
|
||
struct alarmsTable_entry *entry;
|
||
netsnmp_tdata_row *row;
|
||
|
||
entry = SNMP_MALLOC_TYPEDEF(struct alarmsTable_entry);
|
||
if (!entry)
|
||
{
|
||
return NULL;
|
||
}
|
||
row = netsnmp_tdata_create_row();
|
||
if (!row)
|
||
{
|
||
SNMP_FREE(entry);
|
||
return NULL;
|
||
}
|
||
row->data = entry;
|
||
entry->notificationID = notificationID;
|
||
netsnmp_tdata_row_add_index(row,
|
||
ASN_INTEGER, &(entry->notificationID), sizeof(entry->notificationID));
|
||
if (netsnmp_tdata_add_row(alarmsTable, row) != SNMPERR_SUCCESS)
|
||
snmp_perror("add alarmsTable entry");
|
||
|
||
return row;
|
||
}
|
||
|
||
|
||
static int
|
||
pollHeartBeat_handler(netsnmp_mib_handler *handler,
|
||
netsnmp_handler_registration *reginfo,
|
||
netsnmp_agent_request_info *reqinfo,
|
||
netsnmp_request_info *requests)
|
||
{
|
||
int32_t *pollHeartBeat_cache = NULL;
|
||
int32_t tmp;
|
||
|
||
DEBUGMSGTL(("EcAlarmSink", "Got pollHeartBeat_handler request:\n"));
|
||
|
||
switch (reqinfo->mode)
|
||
{
|
||
case MODE_GET:
|
||
snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
|
||
(u_char*) &pollHeartBeat, sizeof(pollHeartBeat));
|
||
break;
|
||
|
||
case MODE_SET_RESERVE1:
|
||
if (requests->requestvb->type != ASN_INTEGER)
|
||
netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE);
|
||
break;
|
||
|
||
case MODE_SET_RESERVE2:
|
||
/*
|
||
* store old info for undo later
|
||
*/
|
||
pollHeartBeat_cache = malloc(sizeof(pollHeartBeat));
|
||
memcpy(&pollHeartBeat_cache, &pollHeartBeat, sizeof(pollHeartBeat));
|
||
if (pollHeartBeat_cache == NULL)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, requests,
|
||
SNMP_ERR_RESOURCEUNAVAILABLE);
|
||
return SNMP_ERR_NOERROR;
|
||
}
|
||
netsnmp_request_add_list_data(requests,
|
||
netsnmp_create_data_list
|
||
("EcAlarmSink",
|
||
pollHeartBeat_cache, free));
|
||
break;
|
||
|
||
case MODE_SET_ACTION:
|
||
/*
|
||
* update current
|
||
*/
|
||
tmp = *(requests->requestvb->val.integer);
|
||
if (tmp > 0)
|
||
{
|
||
pollHeartBeat = tmp;
|
||
[alarmSink _store];
|
||
DEBUGMSGTL(("EcAlarmSink", "updated pollHeartBeat -> %d\n", tmp));
|
||
}
|
||
else
|
||
{
|
||
DEBUGMSGTL(("EcAlarmSink", "ignored pollHeartBeat -> %d\n", tmp));
|
||
netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGVALUE);
|
||
}
|
||
break;
|
||
|
||
case MODE_SET_UNDO:
|
||
pollHeartBeat =
|
||
*((int32_t*)netsnmp_request_get_list_data(requests, "EcAlarmSink"));
|
||
break;
|
||
|
||
case MODE_SET_COMMIT:
|
||
case MODE_SET_FREE:
|
||
/*
|
||
* nothing to do
|
||
*/
|
||
break;
|
||
}
|
||
|
||
return SNMP_ERR_NOERROR;
|
||
}
|
||
|
||
/** handles requests for the alarmsTable table */
|
||
static int
|
||
alarmsTable_handler(netsnmp_mib_handler *handler,
|
||
netsnmp_handler_registration *reginfo,
|
||
netsnmp_agent_request_info *reqinfo,
|
||
netsnmp_request_info *requests)
|
||
{
|
||
netsnmp_request_info *request;
|
||
netsnmp_table_request_info *table_info;
|
||
struct alarmsTable_entry *table_entry;
|
||
|
||
DEBUGMSGTL(("EcAlarmSink", "Got alarmsTable_handler request:\n"));
|
||
switch (reqinfo->mode)
|
||
{
|
||
/*
|
||
* Read-support (also covers GetNext requests)
|
||
*/
|
||
case MODE_GET:
|
||
for (request = requests; request; request = request->next)
|
||
{
|
||
table_entry = (struct alarmsTable_entry *)
|
||
netsnmp_tdata_extract_entry(request);
|
||
table_info = netsnmp_extract_table_info(request);
|
||
|
||
switch (table_info->colnum)
|
||
{
|
||
case COLUMN_NOTIFICATIONID:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER,
|
||
table_entry->notificationID);
|
||
break;
|
||
|
||
case COLUMN_PERCEIVEDSEVERITY:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER,
|
||
table_entry->perceivedSeverity);
|
||
break;
|
||
|
||
case COLUMN_FIRSTEVENTDATE:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
|
||
(u_char *) table_entry->
|
||
firstEventDate,
|
||
table_entry->firstEventDate_len);
|
||
break;
|
||
|
||
case COLUMN_EVENTDATE:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
|
||
(u_char *) table_entry->eventDate,
|
||
table_entry->eventDate_len);
|
||
break;
|
||
|
||
case COLUMN_MANAGEDOBJECT:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
|
||
(u_char *) table_entry->
|
||
managedObject,
|
||
table_entry->managedObject_len);
|
||
break;
|
||
|
||
case COLUMN_IDEVENTTYPE:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER,
|
||
table_entry->eventTypeID);
|
||
break;
|
||
|
||
case COLUMN_IDPROBABLECAUSE:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER,
|
||
table_entry->probableCauseID);
|
||
break;
|
||
|
||
case COLUMN_SPECIFICPROBLEM:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
|
||
(u_char *) table_entry->
|
||
specificProblem,
|
||
table_entry->specificProblem_len);
|
||
break;
|
||
|
||
case COLUMN_PROPOSEDREPAIRACTION:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
|
||
(u_char *) table_entry->
|
||
proposedRepairAction,
|
||
table_entry->
|
||
proposedRepairAction_len);
|
||
break;
|
||
|
||
case COLUMN_ADDITIONALTEXT:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
|
||
(u_char *) table_entry->
|
||
additionalText,
|
||
table_entry->additionalText_len);
|
||
break;
|
||
|
||
case COLUMN_TRENDINDICATOR:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
|
||
(u_char *) table_entry->
|
||
trendIndicator,
|
||
table_entry->trendIndicator_len);
|
||
break;
|
||
|
||
default:
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHOBJECT);
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
return SNMP_ERR_NOERROR;
|
||
}
|
||
|
||
/*
|
||
* create a new row in the table
|
||
*/
|
||
static netsnmp_tdata_row *
|
||
objectsTable_createEntry(NSString *objectID)
|
||
{
|
||
struct objectsTable_entry *entry;
|
||
netsnmp_tdata_row *row;
|
||
const char *str;
|
||
|
||
entry = SNMP_MALLOC_TYPEDEF(struct objectsTable_entry);
|
||
if (!entry)
|
||
{
|
||
return NULL;
|
||
}
|
||
row = netsnmp_tdata_create_row();
|
||
if (!row)
|
||
{
|
||
SNMP_FREE(entry);
|
||
return NULL;
|
||
}
|
||
row->data = entry;
|
||
str = [objectID UTF8String];
|
||
entry->objectID_len = strlen(str);
|
||
strcpy(entry->objectID, str);
|
||
netsnmp_tdata_row_add_index(row, ASN_OCTET_STR,
|
||
entry->objectID, entry->objectID_len);
|
||
if (netsnmp_tdata_add_row(objectsTable, row) != SNMPERR_SUCCESS)
|
||
snmp_perror("add alarmsTable entry");
|
||
|
||
return row;
|
||
}
|
||
|
||
|
||
/** handles requests for the objectsTable table */
|
||
static int
|
||
objectsTable_handler(netsnmp_mib_handler *handler,
|
||
netsnmp_handler_registration *reginfo,
|
||
netsnmp_agent_request_info *reqinfo,
|
||
netsnmp_request_info *requests)
|
||
{
|
||
netsnmp_request_info *request;
|
||
netsnmp_table_request_info *table_info;
|
||
struct objectsTable_entry *table_entry;
|
||
|
||
DEBUGMSGTL(("EcAlarmSink", "Got objectsTable_handler request:\n"));
|
||
switch (reqinfo->mode)
|
||
{
|
||
/*
|
||
* Read-support (also covers GetNext requests)
|
||
*/
|
||
case MODE_GET:
|
||
for (request = requests; request; request = request->next)
|
||
{
|
||
table_entry = (struct objectsTable_entry *)
|
||
netsnmp_tdata_extract_entry(request);
|
||
table_info = netsnmp_extract_table_info(request);
|
||
|
||
switch (table_info->colnum)
|
||
{
|
||
case COLUMN_OBJECTID:
|
||
if (!table_entry)
|
||
{
|
||
netsnmp_set_request_error(reqinfo, request,
|
||
SNMP_NOSUCHINSTANCE);
|
||
continue;
|
||
}
|
||
snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
|
||
(u_char *) table_entry->objectID,
|
||
table_entry->objectID_len);
|
||
break;
|
||
|
||
default:
|
||
netsnmp_set_request_error(
|
||
reqinfo, request, SNMP_NOSUCHOBJECT);
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
return SNMP_ERR_NOERROR;
|
||
}
|
||
|
||
|
||
|
||
|
||
@implementation EcAlarmSinkSNMP
|
||
|
||
+ (EcAlarmSinkSNMP*) alarmSinkSNMP
|
||
{
|
||
EcAlarmSinkSNMP *sink;
|
||
|
||
[classLock lock];
|
||
sink = [alarmSink retain];
|
||
[classLock unlock];
|
||
if (nil == sink)
|
||
{
|
||
sink = [self new];
|
||
}
|
||
return [sink autorelease];
|
||
}
|
||
|
||
+ (void) initialize
|
||
{
|
||
if (nil == classLock)
|
||
{
|
||
classLock = [NSLock new];
|
||
}
|
||
}
|
||
|
||
- (NSString*) description
|
||
{
|
||
return [NSString stringWithFormat:
|
||
@"%@\n AlarmsOID: %@\n ObjectsOID:%@\n TrapOID:%@\n SNMP %@",
|
||
[super description], alarmsOID, objectsOID, trapOID,
|
||
_isRunning ? @"running" : @"not running"];
|
||
}
|
||
|
||
- (id) init
|
||
{
|
||
[classLock lock];
|
||
if (nil == alarmSink)
|
||
{
|
||
alarmSink = self = [super init];
|
||
}
|
||
else
|
||
{
|
||
[self release];
|
||
self = [alarmSink retain];
|
||
}
|
||
[classLock unlock];
|
||
return self;
|
||
}
|
||
|
||
- (id) initWithHost: (NSString*)host name: (NSString*)name
|
||
{
|
||
[classLock lock];
|
||
if (nil == alarmSink)
|
||
{
|
||
alarmSink = self = [super initWithHost: host name: name];
|
||
}
|
||
else
|
||
{
|
||
[self release];
|
||
self = [alarmSink retain];
|
||
}
|
||
[classLock unlock];
|
||
return self;
|
||
}
|
||
|
||
- (void) run
|
||
{
|
||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||
NSString *p;
|
||
NSDictionary *d;
|
||
|
||
init_snmp_logging();
|
||
snmp_disable_filelog();
|
||
snmp_disable_stderrlog();
|
||
snmp_enable_calllog();
|
||
|
||
/* register the callback function to record SNMP errors */
|
||
snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING,
|
||
logSNMP, NULL);
|
||
|
||
/* Make us an agentx client.
|
||
*/
|
||
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);
|
||
|
||
/* Initialize tcpip, if necessary
|
||
*/
|
||
SOCK_STARTUP;
|
||
|
||
/* Initialize the agent library to use the standard agentx port
|
||
*/
|
||
if (nil == _host || [_host isEqual: @""] || [_host isEqual: @"*"])
|
||
{
|
||
ASSIGN(_host, @"localhost"); // Local host
|
||
}
|
||
if (nil == _name || [_name isEqual: @""] || [_name isEqual: @"*"])
|
||
{
|
||
ASSIGN(_name, @"705"); // Standard Agent-X port
|
||
}
|
||
p = [NSString stringWithFormat: @"tcp:%@:%@", _host, _name];
|
||
netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
|
||
NETSNMP_DS_AGENT_X_SOCKET, [p UTF8String]);
|
||
if (0 != init_agent("EcAlarmSink"))
|
||
{
|
||
NSLog(@"Unable to initialise EcAlarmSink as an SNMP sub-agent");
|
||
}
|
||
|
||
/* Initialize MIB code here
|
||
*/
|
||
init_EcAlarmSink();
|
||
|
||
/* Will read gnustep.conf file from standard locations (such as ~/.snmp)
|
||
*/
|
||
init_snmp("gnustep");
|
||
|
||
/* Populate tables and set scalar values from contents of files.
|
||
*/
|
||
p = [[EcProc cmdDataDirectory] stringByAppendingPathComponent: @"SNMP.plist"];
|
||
persistentStore = [p copy];
|
||
if ([[NSFileManager defaultManager] isReadableFileAtPath: p] == YES
|
||
&& (d = [NSDictionary dictionaryWithContentsOfFile: p]) != nil)
|
||
{
|
||
NSData *archive;
|
||
NSEnumerator *enumerator;
|
||
EcAlarm *alarm;
|
||
NSString *name;
|
||
|
||
/* get trap and notification numbers.
|
||
*/
|
||
trapSequenceNumber = [[d objectForKey: @"TrapSequence"] intValue];
|
||
notificationID = [[d objectForKey: @"NotificationID"] intValue];
|
||
pollHeartBeat = [[d objectForKey: @"PollheartBeat"] intValue];
|
||
if (pollHeartBeat < 1) pollHeartBeat = 5;
|
||
|
||
/* Get managed objects table and copy it into SNMP table structure.
|
||
*/
|
||
managedObjects = [[d objectForKey: @"ManagedObjects"] mutableCopy];
|
||
enumerator = [managedObjects objectEnumerator];
|
||
while (nil != (name = [enumerator nextObject]))
|
||
{
|
||
objectsTable_createEntry(name);
|
||
}
|
||
|
||
/* Get archived current alarms and add them to the active SNMP table.
|
||
*/
|
||
archive = [[d objectForKey: @"AlarmsActive"] mutableCopy];
|
||
if (nil != archive)
|
||
{
|
||
NS_DURING
|
||
{
|
||
_alarmsActive
|
||
= [[NSUnarchiver unarchiveObjectWithData: archive] retain];
|
||
if (NO == [_alarmsActive isKindOfClass: [NSMutableSet class]])
|
||
{
|
||
NSLog(@"AlarmsActive loaded as bad class: %@",
|
||
[_alarmsActive class]);
|
||
_alarmsActive = nil;
|
||
}
|
||
}
|
||
NS_HANDLER
|
||
{
|
||
_alarmsActive = nil;
|
||
NSLog(@"AlarmsActive failed to load: %@", localException);
|
||
}
|
||
NS_ENDHANDLER
|
||
}
|
||
enumerator = [_alarmsActive objectEnumerator];
|
||
while (nil != (alarm = [enumerator nextObject]))
|
||
{
|
||
static netsnmp_tdata_row *row;
|
||
|
||
row = alarmsTable_createEntry([alarm notificationID]);
|
||
setAlarmTableEntry(row, alarm);
|
||
}
|
||
}
|
||
|
||
if (nil == managedObjects)
|
||
{
|
||
managedObjects = [NSMutableArray new];
|
||
}
|
||
managedObjectsCount = [managedObjects count];
|
||
if (nil == _alarmsActive)
|
||
{
|
||
_alarmsActive = [NSMutableSet new];
|
||
}
|
||
[pool release];
|
||
pool = [NSAutoreleasePool new];
|
||
|
||
snmp_log(LOG_INFO,"EcAlarmSinkSNMP startup.\n");
|
||
|
||
_isRunning = YES;
|
||
while (YES == _isRunning)
|
||
{
|
||
agent_check_and_process(1); /* 0 == don't block */
|
||
[pool release];
|
||
pool = [NSAutoreleasePool new];
|
||
}
|
||
[pool release];
|
||
|
||
snmp_log(LOG_INFO,"EcAlarmSinkSNMP shutdown.\n");
|
||
/* at shutdown time */
|
||
snmp_shutdown("EcAlarmSink");
|
||
SOCK_CLEANUP;
|
||
}
|
||
|
||
- (void) setBackups: (NSArray*)backups
|
||
{
|
||
if (nil != backups)
|
||
{
|
||
[NSException raise: NSInvalidArgumentException
|
||
format: @"Calling -setBackups: for a sink is not allowed"];
|
||
}
|
||
}
|
||
|
||
- (id<EcAlarmDestination>) setDestination: (id<EcAlarmDestination>)destination
|
||
{
|
||
if (nil != destination)
|
||
{
|
||
[NSException raise: NSInvalidArgumentException
|
||
format: @"Calling -setDestination: for a sink is not allowed"];
|
||
}
|
||
return nil;
|
||
}
|
||
|
||
@end
|
||
|
||
@implementation EcAlarmSinkSNMP (Private)
|
||
|
||
- (void) _store
|
||
{
|
||
NSMutableDictionary *d;
|
||
|
||
[_alarmLock lock];
|
||
NS_DURING
|
||
{
|
||
d = [NSMutableDictionary dictionaryWithCapacity: 6];
|
||
[d setObject: [NSNumber numberWithInt: notificationID]
|
||
forKey: @"NotificationID"];
|
||
[d setObject: [NSNumber numberWithInt: trapSequenceNumber]
|
||
forKey: @"TrapSequence"];
|
||
[d setObject: [NSNumber numberWithInt: pollHeartBeat]
|
||
forKey: @"PollHeartBeat"];
|
||
[d setObject: managedObjects
|
||
forKey: @"ManagedObjects"];
|
||
[d setObject: [NSArchiver archivedDataWithRootObject: _alarmsActive]
|
||
forKey: @"AlarmsActive"];
|
||
[d setObject: _alarmsActive
|
||
forKey: @"AlarmsDescription"];
|
||
[d writeToFile: persistentStore atomically: YES];
|
||
[_alarmLock unlock];
|
||
}
|
||
NS_HANDLER
|
||
{
|
||
[_alarmLock unlock];
|
||
NSLog(@"Problem storing alarm data to disk ... %@", localException);
|
||
}
|
||
NS_ENDHANDLER
|
||
}
|
||
|
||
- (void) _trap: (EcAlarm*)alarm forceClear: (BOOL)forceClear
|
||
{
|
||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||
netsnmp_variable_list *var_list = NULL;
|
||
int32_t i;
|
||
const char *s;
|
||
char ti[2];
|
||
|
||
/*
|
||
* Set the snmpTrapOid.0 value
|
||
*/
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
snmptrap_oid,
|
||
OID_LENGTH(snmptrap_oid),
|
||
ASN_OBJECT_ID,
|
||
(u_char*)alarmTrap_oid,
|
||
alarmTrap_len * sizeof(oid));
|
||
|
||
/*
|
||
* Add any objects from the trap definition
|
||
*/
|
||
if (++trapSequenceNumber <= 0) trapSequenceNumber = 1;
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
trapSequenceNumber_oid,
|
||
trapSequenceNumber_len,
|
||
ASN_INTEGER,
|
||
(u_char*)&trapSequenceNumber,
|
||
sizeof(trapSequenceNumber));
|
||
|
||
i = (int32_t)[alarm notificationID];
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
notificationID_oid,
|
||
notificationID_len,
|
||
ASN_INTEGER,
|
||
(u_char*)&i, sizeof(i));
|
||
|
||
if (YES == forceClear)
|
||
{
|
||
i = EcAlarmSeverityCleared;
|
||
}
|
||
else
|
||
{
|
||
i = (int32_t)[alarm perceivedSeverity];
|
||
}
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
perceivedSeverity_oid,
|
||
perceivedSeverity_len,
|
||
ASN_INTEGER,
|
||
(u_char*)&i, sizeof(i));
|
||
|
||
s = stringFromDate([alarm firstEventDate]);
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
firstEventDate_oid,
|
||
firstEventDate_len,
|
||
ASN_OCTET_STR,
|
||
(u_char*)s, strlen(s));
|
||
|
||
s = stringFromDate([alarm eventDate]);
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
eventDate_oid,
|
||
eventDate_len,
|
||
ASN_OCTET_STR,
|
||
(u_char*)s, strlen(s));
|
||
|
||
s = [[alarm managedObject] UTF8String];
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
objectID_oid,
|
||
objectID_len,
|
||
ASN_OCTET_STR,
|
||
(u_char*)s, strlen(s));
|
||
|
||
i = (int32_t)[alarm eventType];
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
eventTypeID_oid,
|
||
eventTypeID_len,
|
||
ASN_INTEGER,
|
||
(u_char*)&i, sizeof(i));
|
||
|
||
i = (int32_t)[alarm probableCause];
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
probableCauseID_oid,
|
||
probableCauseID_len,
|
||
ASN_INTEGER,
|
||
(u_char*)&i, sizeof(i));
|
||
|
||
s = [[alarm specificProblem] UTF8String];
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
specificProblem_oid,
|
||
specificProblem_len,
|
||
ASN_OCTET_STR,
|
||
(u_char*)s, strlen(s));
|
||
|
||
s = [[alarm proposedRepairAction] UTF8String];
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
proposedRepairAction_oid,
|
||
proposedRepairAction_len,
|
||
ASN_OCTET_STR,
|
||
(u_char*)s, strlen(s));
|
||
|
||
s = [[alarm additionalText] UTF8String];
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
additionalText_oid,
|
||
additionalText_len,
|
||
ASN_OCTET_STR,
|
||
(u_char*)s, strlen(s));
|
||
|
||
ti[0] = (u_char)[alarm trendIndicator];
|
||
ti[1] = 0;
|
||
snmp_varlist_add_variable(
|
||
&var_list,
|
||
trendIndicator_oid,
|
||
trendIndicator_len,
|
||
ASN_OCTET_STR,
|
||
(u_char*)ti, strlen(ti));
|
||
|
||
/* Send the trap and clean up
|
||
*/
|
||
send_v2trap(var_list);
|
||
snmp_free_varbind(var_list);
|
||
[pool release];
|
||
}
|
||
|
||
@end
|
||
|
||
|
||
|
||
|
||
|
||
@implementation EcAlarmSinkSNMP (SNMP)
|
||
|
||
/* Call this only with the lock locked.
|
||
*/
|
||
- (BOOL) snmpClearAlarms: (NSString*)managed
|
||
{
|
||
NSEnumerator *enumerator;
|
||
EcAlarm *alarm;
|
||
BOOL changed = NO;
|
||
|
||
/* Enumerate a copy of the _alarmsActive set so that removing objects
|
||
* from the set while enumerating is safe.
|
||
*/
|
||
enumerator = [[_alarmsActive allObjects] objectEnumerator];
|
||
while (nil != (alarm = [enumerator nextObject]))
|
||
{
|
||
if ([[alarm managedObject] hasPrefix: managed])
|
||
{
|
||
netsnmp_tdata_row *row;
|
||
|
||
/* Remove from the ObjC table.
|
||
*/
|
||
[_alarmsActive removeObject: alarm];
|
||
|
||
/* Find and remove the SNMP table entry.
|
||
*/
|
||
row = (netsnmp_tdata_row*)[alarm extra];
|
||
if (0 != row)
|
||
{
|
||
struct alarmsTable_entry *entry;
|
||
|
||
entry = (struct alarmsTable_entry *)
|
||
netsnmp_tdata_remove_and_delete_row(alarmsTable,
|
||
row);
|
||
if (0 != entry)
|
||
{
|
||
SNMP_FREE(entry);
|
||
}
|
||
}
|
||
|
||
/* send the clear for the entry.
|
||
*/
|
||
[alarmSink _trap: alarm forceClear: YES];
|
||
changed = YES;
|
||
}
|
||
}
|
||
return changed;
|
||
}
|
||
|
||
- (void) snmpHousekeeping
|
||
{
|
||
time_t now;
|
||
BOOL changed = NO;
|
||
|
||
DEBUGMSGTL(("EcAlarmSinkHousekeeping", "Timer called.\n"));
|
||
|
||
[_alarmLock lock];
|
||
if (NO == _inTimeout && YES == _isRunning)
|
||
{
|
||
_inTimeout = YES;
|
||
NS_DURING
|
||
{
|
||
if (0 == resyncFlag)
|
||
{
|
||
/* Check for alarms.
|
||
*/
|
||
while ([_alarmQueue count] > 0)
|
||
{
|
||
id o = [_alarmQueue objectAtIndex: 0];
|
||
|
||
if (YES == [o isKindOfClass: [EcAlarm class]])
|
||
{
|
||
EcAlarm *next = (EcAlarm*)o;
|
||
EcAlarm *prev = [_alarmsActive member: next];
|
||
NSString *m = [next managedObject];
|
||
|
||
if (nil == prev)
|
||
{
|
||
[next setFirstEventDate: [next eventDate]];
|
||
}
|
||
else
|
||
{
|
||
[next setFirstEventDate: [prev firstEventDate]];
|
||
}
|
||
|
||
if ([next perceivedSeverity] == EcAlarmSeverityCleared)
|
||
{
|
||
if (nil != prev)
|
||
{
|
||
netsnmp_tdata_row *row;
|
||
|
||
/* Remove from the ObjC table.
|
||
*/
|
||
[prev retain];
|
||
[_alarmsActive removeObject: prev];
|
||
|
||
/* Find and remove the SNMP table entry.
|
||
*/
|
||
row = (netsnmp_tdata_row*)[prev extra];
|
||
if (0 != row)
|
||
{
|
||
struct alarmsTable_entry *entry;
|
||
|
||
entry = (struct alarmsTable_entry *)
|
||
netsnmp_tdata_remove_and_delete_row(
|
||
alarmsTable, row);
|
||
if (0 != entry)
|
||
{
|
||
SNMP_FREE(entry);
|
||
}
|
||
}
|
||
|
||
/* send the clear for the entry.
|
||
*/
|
||
[next setNotificationID: [prev notificationID]];
|
||
[alarmSink _trap: next forceClear: NO];
|
||
[self alarmFwd: next];
|
||
[prev release];
|
||
changed = YES;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Register any new managed object.
|
||
*/
|
||
if (NO == [managedObjects containsObject: m])
|
||
{
|
||
objectsTable_createEntry(m);
|
||
[managedObjects addObject: m];
|
||
managedObjectsCount = [managedObjects count];
|
||
[self domanageFwd: m];
|
||
changed = YES;
|
||
}
|
||
|
||
if ((nil == prev) || ([next perceivedSeverity]
|
||
!= [prev perceivedSeverity]))
|
||
{
|
||
netsnmp_tdata_row *row;
|
||
|
||
if (nil == prev)
|
||
{
|
||
/* Add and send the new alarm
|
||
*/
|
||
if (++notificationID <= 0) notificationID = 1;
|
||
[next setNotificationID: notificationID];
|
||
row = alarmsTable_createEntry(
|
||
[next notificationID]);
|
||
}
|
||
else
|
||
{
|
||
prev = [[prev retain] autorelease];
|
||
[_alarmsActive removeObject: prev];
|
||
row = (netsnmp_tdata_row*)[prev extra];
|
||
/* send the clear for the entry.
|
||
*/
|
||
[next setNotificationID:
|
||
[prev notificationID]];
|
||
[alarmSink _trap: prev forceClear: YES];
|
||
}
|
||
|
||
/* copy new version of data into row
|
||
* and send new severity trap.
|
||
*/
|
||
setAlarmTableEntry(row, next);
|
||
[_alarmsActive addObject: next];
|
||
[alarmSink _trap: next forceClear: NO];
|
||
[self alarmFwd: next];
|
||
changed = YES;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
NSString *s = [o description];
|
||
|
||
if (YES == [s hasPrefix: @"domanage "])
|
||
{
|
||
NSString *m = [s substringFromIndex: 9];
|
||
|
||
changed = [self snmpClearAlarms: m];
|
||
if (NO == [managedObjects containsObject: m])
|
||
{
|
||
objectsTable_createEntry(m);
|
||
[managedObjects addObject: m];
|
||
managedObjectsCount = [managedObjects count];
|
||
changed = YES;
|
||
}
|
||
}
|
||
else if (YES == [s hasPrefix: @"unmanage "])
|
||
{
|
||
NSString *m = [s substringFromIndex: 9];
|
||
|
||
if (YES == [managedObjects containsObject: m])
|
||
{
|
||
const char *str;
|
||
netsnmp_tdata_row *row;
|
||
|
||
changed = YES;
|
||
[self snmpClearAlarms: m];
|
||
str = [m UTF8String];
|
||
row = netsnmp_tdata_row_first(objectsTable);
|
||
while (0 != row)
|
||
{
|
||
struct objectsTable_entry *entry;
|
||
|
||
entry = (struct objectsTable_entry *)row->data;
|
||
if (0 == strcmp(entry->objectID, str))
|
||
{
|
||
netsnmp_tdata_remove_and_delete_row
|
||
(objectsTable, row);
|
||
SNMP_FREE(entry);
|
||
break;
|
||
}
|
||
row = netsnmp_tdata_row_next(objectsTable, row);
|
||
}
|
||
[managedObjects removeObject: m];
|
||
if (YES == [m hasSuffix: @"_"])
|
||
{
|
||
NSEnumerator *e;
|
||
NSString *s;
|
||
|
||
e = [[[managedObjects copy] autorelease]
|
||
objectEnumerator];
|
||
while (nil != (s = [e nextObject]))
|
||
{
|
||
if (YES == [s hasPrefix: m])
|
||
{
|
||
[managedObjects removeObject: s];
|
||
}
|
||
}
|
||
}
|
||
managedObjectsCount = [managedObjects count];
|
||
}
|
||
}
|
||
else
|
||
{
|
||
NSLog(@"ERROR ... unexpected command '%@'", s);
|
||
}
|
||
}
|
||
[_alarmQueue removeObjectAtIndex: 0];
|
||
[_alarmLock unlock];
|
||
[_alarmLock lock];
|
||
}
|
||
}
|
||
}
|
||
NS_HANDLER
|
||
{
|
||
NSLog(@"Problem in housekeeping timeout: %@", localException);
|
||
}
|
||
NS_ENDHANDLER
|
||
_inTimeout = NO;
|
||
}
|
||
if (YES == _shouldStop)
|
||
{
|
||
_isRunning = NO;
|
||
}
|
||
[_alarmLock unlock];
|
||
now = time(0);
|
||
if (YES == heartbeat(now))
|
||
{
|
||
changed = YES;
|
||
}
|
||
if (YES == changed)
|
||
{
|
||
[alarmSink _store];
|
||
}
|
||
}
|
||
|
||
@end
|
||
|
||
#else
|
||
|
||
@implementation EcAlarmSinkSNMP
|
||
|
||
+ (EcAlarmSinkSNMP*) alarmSinkSNMP
|
||
{
|
||
EcAlarmSinkSNMP *sink;
|
||
|
||
[classLock lock];
|
||
sink = [alarmSink retain];
|
||
[classLock unlock];
|
||
if (nil == sink)
|
||
{
|
||
sink = [self new];
|
||
}
|
||
return [sink autorelease];
|
||
}
|
||
|
||
+ (void) initialize
|
||
{
|
||
if (nil == classLock)
|
||
{
|
||
classLock = [NSLock new];
|
||
}
|
||
}
|
||
|
||
- (id) initWithHost: (NSString*)host name: (NSString*)name
|
||
{
|
||
[classLock lock];
|
||
if (nil == alarmSink)
|
||
{
|
||
alarmSink = self = [super initWithHost: host name: name];
|
||
}
|
||
else
|
||
{
|
||
[self release];
|
||
self = [alarmSink retain];
|
||
}
|
||
[classLock unlock];
|
||
return self;
|
||
}
|
||
|
||
@end
|
||
|
||
#endif
|