mirror of
https://github.com/gnustep/libs-ec.git
synced 2025-02-19 10:01:24 +00:00
Enterprise Control/Configuration/Logging package ... preliminary check in.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/devmodules/dev-libs/ec@34775 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
commit
acbe80c889
38 changed files with 26394 additions and 0 deletions
34
Client.h
Normal file
34
Client.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
|
||||
@interface ClientInfo : NSObject
|
||||
{
|
||||
id<CmdClient> theServer;
|
||||
id obj;
|
||||
NSString *name;
|
||||
NSDate *lastUnanswered; /* Last unanswered ping. */
|
||||
unsigned fwdSequence; /* Last ping sent TO client. */
|
||||
unsigned revSequence; /* Last gnip sent BY client. */
|
||||
NSMutableSet *files; /* Want update info for these. */
|
||||
NSData *config; /* Config info for client. */
|
||||
BOOL pingOk;
|
||||
BOOL transient;
|
||||
BOOL unregistered;
|
||||
}
|
||||
- (NSComparisonResult) compare: (ClientInfo*)other;
|
||||
- (NSData*) config;
|
||||
- (NSMutableSet*) files;
|
||||
- (BOOL) gnip: (unsigned)seq;
|
||||
- (id) initFor: (id)obj
|
||||
name: (NSString*)n
|
||||
with: (id<CmdClient>)svr;
|
||||
- (NSDate*) lastUnanswered;
|
||||
- (NSString*) name;
|
||||
- (id) obj;
|
||||
- (void) ping;
|
||||
- (void) setConfig: (NSData*)c;
|
||||
- (void) setName: (NSString*)n;
|
||||
- (void) setObj: (id)o;
|
||||
- (void) setTransient: (BOOL)flag;
|
||||
- (void) setUnregistered: (BOOL)flag;
|
||||
- (BOOL) transient;
|
||||
@end
|
||||
|
170
Client.m
Normal file
170
Client.m
Normal file
|
@ -0,0 +1,170 @@
|
|||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "EcProcess.h"
|
||||
#import "Client.h"
|
||||
|
||||
|
||||
@implementation ClientInfo
|
||||
|
||||
- (NSComparisonResult) compare: (ClientInfo*)other
|
||||
{
|
||||
return [name compare: [other name]];
|
||||
}
|
||||
|
||||
- (NSData*) config
|
||||
{
|
||||
return config;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
NSConnection *c = [obj connectionForProxy];
|
||||
|
||||
if (c != nil)
|
||||
{
|
||||
RETAIN(c);
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
removeObserver: theServer
|
||||
name: NSConnectionDidDieNotification
|
||||
object: (id)c];
|
||||
if (unregistered == NO && [c isValid] == YES)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
[obj cmdQuit: 0];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"cmdQuit: to %@ - %@", name, localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
RELEASE(c);
|
||||
}
|
||||
DESTROY(lastUnanswered);
|
||||
DESTROY(config);
|
||||
DESTROY(files);
|
||||
DESTROY(name);
|
||||
DESTROY(obj);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSMutableSet*) files
|
||||
{
|
||||
return files;
|
||||
}
|
||||
|
||||
- (BOOL) gnip: (unsigned)s
|
||||
{
|
||||
if (s != revSequence + 1 && revSequence != 0)
|
||||
{
|
||||
NSLog(@"Gnip from %@ seq: %u when expecting %u", name, s, revSequence);
|
||||
if (s == 0)
|
||||
{
|
||||
fwdSequence = 0; // Reset
|
||||
}
|
||||
}
|
||||
revSequence = s;
|
||||
if (revSequence == fwdSequence)
|
||||
{
|
||||
DESTROY(lastUnanswered);
|
||||
return YES; /* up to date */
|
||||
}
|
||||
else
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (id) initFor: (id)o
|
||||
name: (NSString*)n
|
||||
with: (id<CmdClient>)s
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
theServer = s;
|
||||
files = [NSMutableSet new];
|
||||
[self setObj: o];
|
||||
[self setName: n];
|
||||
pingOk = [o respondsToSelector: @selector(cmdPing:sequence:extra:)];
|
||||
if (pingOk == NO)
|
||||
NSLog(@"Warning - %@ is an old server ... it can't be pinged", n);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDate*) lastUnanswered
|
||||
{
|
||||
return lastUnanswered;
|
||||
}
|
||||
|
||||
- (NSString*) name
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
- (id) obj
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
- (void) ping
|
||||
{
|
||||
if (pingOk == NO)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (fwdSequence == revSequence)
|
||||
{
|
||||
lastUnanswered = [[NSDate date] retain];
|
||||
NS_DURING
|
||||
{
|
||||
[obj cmdPing: theServer sequence: ++fwdSequence extra: nil];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Ping to %@ - %@", name, localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Ping to %@ when one is already in progress.", name);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setConfig: (NSData*)c
|
||||
{
|
||||
ASSIGN(config, c);
|
||||
}
|
||||
|
||||
- (void) setName: (NSString*)n
|
||||
{
|
||||
ASSIGN(name, n);
|
||||
}
|
||||
|
||||
- (void) setObj: (id)o
|
||||
{
|
||||
ASSIGN(obj, o);
|
||||
}
|
||||
|
||||
- (void) setTransient: (BOOL)flag
|
||||
{
|
||||
transient = flag;
|
||||
}
|
||||
|
||||
- (void) setUnregistered: (BOOL)flag
|
||||
{
|
||||
unregistered = flag;
|
||||
}
|
||||
|
||||
- (BOOL) transient
|
||||
{
|
||||
return transient;
|
||||
}
|
||||
@end
|
||||
|
||||
|
900
Console.m
Normal file
900
Console.m
Normal file
|
@ -0,0 +1,900 @@
|
|||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include "EcProcess.h"
|
||||
#include "NSFileHandle+Printf.h"
|
||||
|
||||
#include <sys/signal.h>
|
||||
|
||||
#if WITH_READLINE
|
||||
# include <stdlib.h>
|
||||
# include <readline/readline.h>
|
||||
# include <readline/history.h>
|
||||
#endif
|
||||
|
||||
static BOOL commandIsRepeat (NSString *string)
|
||||
{
|
||||
switch ([string length])
|
||||
{
|
||||
case 1: return [string isEqualToString: @"r"];
|
||||
case 2: return [string isEqualToString: @"re"];
|
||||
case 3: return [string isEqualToString: @"rep"];
|
||||
case 4: return [string isEqualToString: @"repe"];
|
||||
case 5: return [string isEqualToString: @"repea"];
|
||||
case 6: return [string isEqualToString: @"repeat"];
|
||||
default: return NO;
|
||||
}
|
||||
}
|
||||
|
||||
@interface Console : EcProcess <RunLoopEvents, Console>
|
||||
{
|
||||
NSFileHandle *ichan;
|
||||
NSFileHandle *ochan;
|
||||
NSMutableData *data;
|
||||
NSTimer *timer;
|
||||
NSString *local;
|
||||
NSString *host;
|
||||
NSString *name;
|
||||
NSString *user;
|
||||
NSString *pass;
|
||||
NSString *rnam;
|
||||
id server;
|
||||
int pos;
|
||||
}
|
||||
- (void) connectionBecameInvalid: (NSNotification*)notification;
|
||||
#if !WITH_READLINE
|
||||
- (void) didRead: (NSNotification*)notification;
|
||||
#endif
|
||||
- (void) didWrite: (NSNotification*)notification;
|
||||
- (void) doCommand: (NSMutableArray*)words;
|
||||
- (void) timedOut;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
@implementation Console
|
||||
|
||||
- (BOOL) cmdIsClient
|
||||
{
|
||||
return NO; // Not a client of the Command server
|
||||
}
|
||||
|
||||
- (void) cmdQuit: (int)sig
|
||||
{
|
||||
[ochan puts: @"\nExiting\n"];
|
||||
|
||||
if (server)
|
||||
{
|
||||
id con = [(NSDistantObject*)server connectionForProxy];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: self
|
||||
name: NSConnectionDidDieNotification
|
||||
object: con];
|
||||
NS_DURING
|
||||
{
|
||||
[server unregister: self];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Exception unregistering from Control: %@", localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
[con invalidate];
|
||||
DESTROY(server);
|
||||
}
|
||||
|
||||
if (ichan)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: self
|
||||
name: NSFileHandleReadCompletionNotification
|
||||
object: (id)ichan];
|
||||
if ([ichan fileDescriptor] >= 0)
|
||||
{
|
||||
[ichan closeFile];
|
||||
}
|
||||
[ichan release];
|
||||
ichan = nil;
|
||||
}
|
||||
if (ochan)
|
||||
{
|
||||
if ([ochan fileDescriptor] >= 0)
|
||||
{
|
||||
[ochan closeFile];
|
||||
}
|
||||
[ochan release];
|
||||
ochan = nil;
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
- (void) connectionBecameInvalid: (NSNotification*)notification
|
||||
{
|
||||
id conn = [notification object];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: self
|
||||
name: NSConnectionDidDieNotification
|
||||
object: conn];
|
||||
if ([conn isKindOfClass: [NSConnection class]])
|
||||
{
|
||||
if (server && [(NSDistantObject*)server connectionForProxy] == conn)
|
||||
{
|
||||
[server release];
|
||||
server = nil;
|
||||
NSLog(@"Lost connection to Control server.");
|
||||
[self cmdQuit: 0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[rnam release];
|
||||
[user release];
|
||||
[pass release];
|
||||
[host release];
|
||||
[name release];
|
||||
[local release];
|
||||
[data release];
|
||||
if (timer)
|
||||
{
|
||||
[timer invalidate];
|
||||
timer = nil;
|
||||
}
|
||||
[self cmdQuit: 0];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) receivedEvent: (void *)descriptorData
|
||||
type: (RunLoopEventType)type
|
||||
extra: (void*)extra
|
||||
forMode: (NSString*)mode
|
||||
{
|
||||
#if WITH_READLINE
|
||||
rl_callback_read_char();
|
||||
#else
|
||||
NSLog(@"ERROR: this should not get called w/o readline: %s",
|
||||
__PRETTY_FUNCTION__);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)processReadLine:(NSString *)_line
|
||||
{
|
||||
NSAutoreleasePool *arp;
|
||||
NSMutableArray *words;
|
||||
NSEnumerator *wordsEnum;
|
||||
NSString *word;
|
||||
|
||||
if (_line == nil)
|
||||
{
|
||||
[self cmdQuit: 0];
|
||||
}
|
||||
|
||||
if ([_line length] == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
arp = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
/* setup word array */
|
||||
|
||||
words = [NSMutableArray arrayWithCapacity:16];
|
||||
wordsEnum = [[_line componentsSeparatedByString:@" "] objectEnumerator];
|
||||
while ((word = [wordsEnum nextObject]) != nil)
|
||||
[words addObject:[word stringByTrimmingSpaces]];
|
||||
|
||||
/* invoke server */
|
||||
|
||||
[self doCommand:words];
|
||||
|
||||
[arp release];
|
||||
}
|
||||
|
||||
#if WITH_READLINE
|
||||
|
||||
- (char **)complete:(const char *)_text range:(NSRange)_range
|
||||
{
|
||||
return NULL; /* no completion yet */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !WITH_READLINE
|
||||
- (void) didRead: (NSNotification*)notification
|
||||
{
|
||||
NSDictionary *userInfo = [notification userInfo];
|
||||
NSData *d;
|
||||
|
||||
d = [[userInfo objectForKey: NSFileHandleNotificationDataItem] retain];
|
||||
|
||||
if (d == nil || [d length] == 0)
|
||||
{
|
||||
if (d != nil)
|
||||
{
|
||||
[d release];
|
||||
}
|
||||
[self cmdQuit: 0];
|
||||
}
|
||||
else
|
||||
{
|
||||
char *bytes;
|
||||
int len;
|
||||
int eol;
|
||||
int done = 0;
|
||||
|
||||
[data appendData: d];
|
||||
[d release];
|
||||
bytes = (char*)[data mutableBytes];
|
||||
len = [data length];
|
||||
|
||||
for (eol = 0; eol < len; eol++)
|
||||
{
|
||||
if (bytes[eol] == '\r' || bytes[eol] == '\n')
|
||||
{
|
||||
char *word = bytes;
|
||||
char *end = word;
|
||||
NSMutableArray *a;
|
||||
|
||||
a = [NSMutableArray arrayWithCapacity: 1];
|
||||
|
||||
bytes[eol++] = '\0';
|
||||
while (eol < len && isspace(bytes[eol]))
|
||||
{
|
||||
eol++;
|
||||
}
|
||||
done = eol;
|
||||
|
||||
while (end && *end)
|
||||
{
|
||||
word = end;
|
||||
while (*word && isspace(*word))
|
||||
{
|
||||
word++;
|
||||
}
|
||||
end = word;
|
||||
|
||||
if (*word == '"' || *word == '\'')
|
||||
{
|
||||
char term = *word;
|
||||
char *ptr;
|
||||
|
||||
end = ++word;
|
||||
ptr = end;
|
||||
|
||||
while (*end)
|
||||
{
|
||||
if (*end == term)
|
||||
{
|
||||
end++;
|
||||
break;
|
||||
}
|
||||
if (*end == '\\')
|
||||
{
|
||||
end++;
|
||||
switch (*end)
|
||||
{
|
||||
case '\\': *ptr++ = '\\'; break;
|
||||
case 'b': *ptr++ = '\b'; break;
|
||||
case 'f': *ptr++ = '\f'; break;
|
||||
case 'n': *ptr++ = '\n'; break;
|
||||
case 'r': *ptr++ = '\r'; break;
|
||||
case 't': *ptr++ = '\t'; break;
|
||||
case 'x':
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (isxdigit(end[1]))
|
||||
{
|
||||
end++;
|
||||
val <<= 4;
|
||||
if (islower(*end))
|
||||
{
|
||||
val += *end - 'a' + 10;
|
||||
}
|
||||
else if (isupper(*end))
|
||||
{
|
||||
val += *end - 'A' + 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
val += *end - '0';
|
||||
}
|
||||
}
|
||||
if (isxdigit(end[1]))
|
||||
{
|
||||
end++;
|
||||
val <<= 4;
|
||||
if (islower(*end))
|
||||
{
|
||||
val += *end - 'a' + 10;
|
||||
}
|
||||
else if (isupper(*end))
|
||||
{
|
||||
val += *end - 'A' + 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
val += *end - '0';
|
||||
}
|
||||
}
|
||||
*ptr++ = val;
|
||||
}
|
||||
break;
|
||||
|
||||
case '0':
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (isdigit(end[1]))
|
||||
{
|
||||
end++;
|
||||
val <<= 3;
|
||||
val += *end - '0';
|
||||
}
|
||||
if (isdigit(end[1]))
|
||||
{
|
||||
end++;
|
||||
val <<= 3;
|
||||
val += *end - '0';
|
||||
}
|
||||
if (isdigit(end[1]))
|
||||
{
|
||||
end++;
|
||||
val <<= 3;
|
||||
val += *end - '0';
|
||||
}
|
||||
*ptr++ = val;
|
||||
}
|
||||
break;
|
||||
|
||||
default: *ptr++ = *end; break;
|
||||
}
|
||||
if (*end)
|
||||
{
|
||||
end++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*ptr++ = *end++;
|
||||
}
|
||||
}
|
||||
*ptr = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
while (*end && !isspace(*end))
|
||||
{
|
||||
if (isupper(*end))
|
||||
{
|
||||
*end = tolower(*end);
|
||||
}
|
||||
end++;
|
||||
}
|
||||
}
|
||||
|
||||
if (*end)
|
||||
{
|
||||
*end++ = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
end = 0;
|
||||
}
|
||||
|
||||
if (word && *word)
|
||||
{
|
||||
[a addObject: [NSString stringWithCString: word]];
|
||||
}
|
||||
}
|
||||
|
||||
[self doCommand: a];
|
||||
if (ichan == nil)
|
||||
{
|
||||
return; /* Quit while doing command. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (done > 0)
|
||||
{
|
||||
memcpy(bytes, &bytes[done], len - done);
|
||||
[data setLength: len - done];
|
||||
}
|
||||
|
||||
[ichan readInBackgroundAndNotify]; /* Need more data. */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void) didWrite: (NSNotification*)notification
|
||||
{
|
||||
NSDictionary *userInfo = [notification userInfo];
|
||||
NSString *e;
|
||||
|
||||
e = [userInfo objectForKey: GSFileHandleNotificationError];
|
||||
if (e)
|
||||
{
|
||||
[self cmdQuit: 0];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) doCommand: (NSMutableArray*)words
|
||||
{
|
||||
NSString *cmd;
|
||||
static NSArray *pastWords = nil;
|
||||
|
||||
if (words == nil || [words count] == 0)
|
||||
{
|
||||
cmd = @"";
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = [words objectAtIndex: 0];
|
||||
}
|
||||
if ([cmd isEqual: @"quit"] && [words count] == 1)
|
||||
{
|
||||
[self cmdQuit: 0];
|
||||
return;
|
||||
}
|
||||
|
||||
#if !WITH_READLINE
|
||||
if (user == nil)
|
||||
{
|
||||
if ([cmd length] > 0)
|
||||
{
|
||||
user = [cmd retain];
|
||||
[ochan puts: @"Enter your password: "];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ochan puts: @"Enter your username: "];
|
||||
}
|
||||
}
|
||||
else if (pass == nil)
|
||||
{
|
||||
if ([cmd length] > 0)
|
||||
{
|
||||
pass = [cmd retain];
|
||||
server = [NSConnection rootProxyForConnectionWithRegisteredName: name
|
||||
host: host
|
||||
usingNameServer: [NSSocketPortNameServer sharedInstance]];
|
||||
if (server == nil)
|
||||
{
|
||||
if ([host isEqual: @"*"])
|
||||
{
|
||||
[ochan printf: @"Unable to connect to %@ on any host.\n",
|
||||
name];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ochan printf: @"Unable to connect to %@ on %@.\n",
|
||||
name, host];
|
||||
}
|
||||
[self cmdQuit: 0];
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *reject;
|
||||
|
||||
[rnam release];
|
||||
rnam = [NSString stringWithFormat: @"%@:%@", user, local];
|
||||
[rnam retain];
|
||||
|
||||
[server retain];
|
||||
[server setProtocolForProxy: @protocol(Control)];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver: self
|
||||
selector: @selector(connectionBecameInvalid:)
|
||||
name: NSConnectionDidDieNotification
|
||||
object: (id)[server connectionForProxy]];
|
||||
[[server connectionForProxy] setDelegate: self];
|
||||
|
||||
reject = [server registerConsole: self
|
||||
name: rnam
|
||||
pass: pass];
|
||||
|
||||
if (reject == nil)
|
||||
{
|
||||
[words removeAllObjects];
|
||||
[words addObject: @"list"];
|
||||
[words addObject: @"consoles"];
|
||||
[server command: [NSPropertyListSerialization
|
||||
dataFromPropertyList: words
|
||||
format: NSPropertyListBinaryFormat_v1_0
|
||||
errorDescription: 0] from: rnam];
|
||||
}
|
||||
else
|
||||
{
|
||||
[pass release];
|
||||
pass = nil;
|
||||
[user release];
|
||||
user = nil;
|
||||
[ochan puts: [NSString stringWithFormat:
|
||||
@"Connection attempt rejected - %@\n", reject]];
|
||||
[ochan puts: @"Enter your username: "];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[ochan puts: @"Enter your password: "];
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /* WITH_READLINE */
|
||||
if (commandIsRepeat(cmd))
|
||||
{
|
||||
if (pastWords == nil)
|
||||
{
|
||||
[ochan puts: @"No command to repeat.\n"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ochan printf: @"Repeating command `%@' -\n",
|
||||
[pastWords componentsJoinedByString: @" "]];
|
||||
[server command: [NSPropertyListSerialization
|
||||
dataFromPropertyList: pastWords
|
||||
format: NSPropertyListBinaryFormat_v1_0
|
||||
errorDescription: 0] from: rnam];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSIGN(pastWords, words);
|
||||
[server command: [NSPropertyListSerialization
|
||||
dataFromPropertyList: words
|
||||
format: NSPropertyListBinaryFormat_v1_0
|
||||
errorDescription: 0] from: rnam];
|
||||
}
|
||||
}
|
||||
|
||||
#define add(C) { if (op - out >= len - 1) { int i = op - out; len += 128; [d setLength: len]; out = [d mutableBytes]; op = &out[i];} *op++ = C; }
|
||||
|
||||
- (oneway void) information: (NSString*)s
|
||||
{
|
||||
int ilen = [s cStringLength] + 1;
|
||||
int len = (ilen + 4) * 2;
|
||||
char buf[ilen];
|
||||
const char *ip = buf;
|
||||
NSMutableData *d = [NSMutableData dataWithCapacity: len];
|
||||
char *out = [d mutableBytes];
|
||||
char *op = out;
|
||||
char c;
|
||||
|
||||
[s getCString: buf];
|
||||
buf[ilen-1] = '\0';
|
||||
[d setLength: len];
|
||||
if (pos)
|
||||
{
|
||||
add('\n');
|
||||
pos = 0;
|
||||
}
|
||||
while (*ip)
|
||||
{
|
||||
switch (*ip)
|
||||
{
|
||||
case '\r':
|
||||
case '\n':
|
||||
pos = 0;
|
||||
add(*ip);
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
if (pos >= 72)
|
||||
{
|
||||
pos = 0;
|
||||
add('\n');
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
add(' ');
|
||||
pos++;
|
||||
}
|
||||
while (pos % 8);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
c = *ip;
|
||||
if (c < ' ')
|
||||
{
|
||||
c = ' ';
|
||||
}
|
||||
if (c > 126)
|
||||
{
|
||||
c = ' ';
|
||||
}
|
||||
if (c == ' ')
|
||||
{
|
||||
int l = 0;
|
||||
|
||||
while (ip[l] && isspace(ip[l])) l++;
|
||||
while (ip[l] && !isspace(ip[l])) l++;
|
||||
l--;
|
||||
if ((pos + l) >= 80 && l < 80)
|
||||
{
|
||||
add('\n');
|
||||
pos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
add(' ');
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
add(c);
|
||||
pos++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ip++;
|
||||
}
|
||||
[d setLength: op - out];
|
||||
[ochan writeData: d];
|
||||
}
|
||||
|
||||
#define NUMARGS 3
|
||||
- (id) init
|
||||
{
|
||||
NSDictionary *appDefaults;
|
||||
id objects[NUMARGS];
|
||||
id keys[NUMARGS];
|
||||
|
||||
keys[0] = @"BSEffectiveUser";
|
||||
objects[0] = @"";
|
||||
keys[1] = @"BSDaemon";
|
||||
objects[1] = @"NO"; /* Never run as daemon */
|
||||
keys[2] = @"BSNoDaemon";
|
||||
objects[2] = @"YES"; /* Never run as daemon */
|
||||
|
||||
appDefaults = [NSDictionary dictionaryWithObjects: objects
|
||||
forKeys: keys
|
||||
count: NUMARGS];
|
||||
self = [super initWithDefaults: appDefaults];
|
||||
if (self)
|
||||
{
|
||||
NSUserDefaults *defs = [self cmdDefaults];
|
||||
|
||||
local = [[[NSHost currentHost] name] retain];
|
||||
name = [defs stringForKey: @"ControlName"];
|
||||
if (name == nil)
|
||||
{
|
||||
name = @"Control";
|
||||
}
|
||||
host = [defs stringForKey: @"ControlHost"];
|
||||
if (host == nil)
|
||||
{
|
||||
host = @"*";
|
||||
}
|
||||
if ([host length] == 0)
|
||||
{
|
||||
host = local;
|
||||
}
|
||||
[host retain];
|
||||
rnam = [[NSString stringWithFormat: @"%@:%@", user, local] retain];
|
||||
|
||||
data = [[NSMutableData alloc] init];
|
||||
ichan = [[NSFileHandle fileHandleWithStandardInput] retain];
|
||||
ochan = [[NSFileHandle fileHandleWithStandardOutput] retain];
|
||||
|
||||
#if !WITH_READLINE
|
||||
[[NSNotificationCenter defaultCenter] addObserver: self
|
||||
selector: @selector(didRead:)
|
||||
name: NSFileHandleReadCompletionNotification
|
||||
object: (id)ichan];
|
||||
[ochan puts: @"Enter your username: "];
|
||||
[ichan readInBackgroundAndNotify];
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) timedOut
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* readline handling */
|
||||
|
||||
#if WITH_READLINE
|
||||
|
||||
/* readline callbacks have no context, so we need this one ... */
|
||||
static Console *rlConsole = nil;
|
||||
|
||||
static void readlineReadALine(char *_line)
|
||||
{
|
||||
NSString *s = _line ? [[NSString alloc] initWithCString:_line] : nil;
|
||||
|
||||
[rlConsole processReadLine:s];
|
||||
|
||||
if (_line != NULL && strlen(_line) > 0)
|
||||
{
|
||||
add_history(_line);
|
||||
}
|
||||
|
||||
DESTROY(s);
|
||||
}
|
||||
|
||||
static char **consoleCompleter(const char *text, int start, int end)
|
||||
{
|
||||
return [rlConsole complete:text range:NSMakeRange(start, end - start)];
|
||||
}
|
||||
|
||||
- (void)activateReadline {
|
||||
rlConsole = self;
|
||||
|
||||
/* setup readline */
|
||||
|
||||
rl_readline_name = "Console";
|
||||
rl_attempted_completion_function = consoleCompleter;
|
||||
|
||||
rl_callback_handler_install("" /* prompt */, readlineReadALine);
|
||||
atexit(rl_callback_handler_remove);
|
||||
|
||||
/* register in runloop */
|
||||
|
||||
[[NSRunLoop currentRunLoop] addEvent:(void*)(uintptr_t)0 type:ET_RDESC
|
||||
watcher:self forMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
- (void)deactivateReadline
|
||||
{
|
||||
[[NSRunLoop currentRunLoop] removeEvent:(void*)(uintptr_t)0 type:ET_RDESC
|
||||
forMode:NSDefaultRunLoopMode all:YES];
|
||||
|
||||
rl_callback_handler_remove();
|
||||
rlConsole = nil;
|
||||
}
|
||||
|
||||
- (void) setupConnectionr
|
||||
{
|
||||
while (self->server == nil)
|
||||
{
|
||||
NSString *reject;
|
||||
char lUser[128], *llUser;
|
||||
|
||||
/* read username */
|
||||
|
||||
printf("Login: ");
|
||||
fflush(stdout);
|
||||
|
||||
llUser = fgets(lUser, sizeof(lUser), stdin);
|
||||
if (llUser == NULL || strlen(llUser) == 0)
|
||||
{
|
||||
/* user pressed ctrl-D, just exit */
|
||||
exit(0);
|
||||
}
|
||||
|
||||
llUser[strlen(llUser) - 1] = '\0';
|
||||
if (strlen(llUser) < 1)
|
||||
{
|
||||
/* user just pressed enter, retry */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read password (glibc documentation says not to use getpass?) */
|
||||
|
||||
const char *lPassword = getpass("Password: ");
|
||||
if (lPassword == NULL) {
|
||||
NSLog(@"Could not read password: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/* setup connection to server */
|
||||
|
||||
self->server =
|
||||
[[NSConnection rootProxyForConnectionWithRegisteredName: name
|
||||
host: host
|
||||
usingNameServer:
|
||||
[NSSocketPortNameServer sharedInstance]] retain];
|
||||
if (self->server == nil)
|
||||
{
|
||||
if ([host isEqual: @"*"])
|
||||
{
|
||||
[ochan printf: @"Unable to connect to %@ on any host.\n",
|
||||
name];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ochan printf: @"Unable to connect to %@ on %@.\n",
|
||||
name, host];
|
||||
}
|
||||
|
||||
[self cmdQuit: 0];
|
||||
return;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver: self
|
||||
selector: @selector(connectionBecameInvalid:)
|
||||
name: NSConnectionDidDieNotification
|
||||
object: (id)[self->server connectionForProxy]];
|
||||
[server setProtocolForProxy: @protocol(Control)];
|
||||
[[server connectionForProxy] setDelegate: self];
|
||||
|
||||
/* attempt login */
|
||||
|
||||
self->user = [[NSString alloc] initWithCString:lUser];
|
||||
self->pass = [[NSString alloc] initWithCString:lPassword];
|
||||
self->rnam =
|
||||
[[NSString alloc] initWithFormat:@"%@:%@", self->user, self->local];
|
||||
|
||||
reject = [self->server registerConsole:self
|
||||
name:self->rnam pass:self->pass];
|
||||
|
||||
/* connection failed */
|
||||
|
||||
if (reject != nil) {
|
||||
[ochan puts:
|
||||
[NSString stringWithFormat:
|
||||
@"Connection attempt rejected - %@\n", reject]];
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
removeObserver:self
|
||||
name: NSConnectionDidDieNotification
|
||||
object: (id)[self->server connectionForProxy]];
|
||||
|
||||
DESTROY(self->pass);
|
||||
DESTROY(self->user);
|
||||
DESTROY(self->rnam);
|
||||
DESTROY(self->server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* WITH_READLINE */
|
||||
|
||||
@end
|
||||
|
||||
|
||||
void
|
||||
inner_main()
|
||||
{
|
||||
extern NSString *cmdVersion(NSString*);
|
||||
Console *console;
|
||||
NSAutoreleasePool *arp;
|
||||
|
||||
arp = [[NSAutoreleasePool alloc] init];
|
||||
cmdVersion(@"$Date: 2012-02-13 08:11:49 +0000 (Mon, 13 Feb 2012) $ $Revision: 65934 $");
|
||||
|
||||
console = [Console new];
|
||||
[arp release];
|
||||
|
||||
arp = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
#if WITH_READLINE
|
||||
[console setupConnection];
|
||||
[console activateReadline];
|
||||
#endif
|
||||
|
||||
/* Let standard signals simply kill the Console
|
||||
*/
|
||||
signal(SIGHUP, SIG_DFL);
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
|
||||
[[NSRunLoop currentRunLoop] run];
|
||||
|
||||
#if WITH_READLINE
|
||||
[console deactivateReadline];
|
||||
#endif
|
||||
|
||||
[console release];
|
||||
[arp release];
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
inner_main();
|
||||
return 0;
|
||||
}
|
589
EcAlarm.h
Normal file
589
EcAlarm.h
Normal file
|
@ -0,0 +1,589 @@
|
|||
#ifndef _ECALARM_H
|
||||
#define _ECALARM_H
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class NSCoder;
|
||||
@class NSDate;
|
||||
@class NSString;
|
||||
|
||||
/**
|
||||
* The EcAlarmEventType enumeration defines the different types of
|
||||
* alarm we support.<br />
|
||||
* The enumerated values MUST be matched by those in your SNMP MIB if
|
||||
* you wish to have your software interact with SNMP tools.<br />
|
||||
* NB. EcAlarmEventTypeUnknown must <em>NOT</em> be used in an alarm ...
|
||||
* it is employed solely as a marker for the case where a lookup of
|
||||
* type from probable cause can not determine a specific event type.
|
||||
* <deflist>
|
||||
* <term>EcAlarmEventTypeUnknown</term>
|
||||
* <desc>Not used</desc>
|
||||
* <term>EcAlarmEventTypeCommunications</term>
|
||||
* <desc>A communications/networking/protocol issue</desc>
|
||||
* <term>EcAlarmEventTypeEnvironmental</term>
|
||||
* <desc>An external environmental issue (eg building on fire)</desc>
|
||||
* <term>EcAlarmEventTypeEquipment</term>
|
||||
* <desc>A hardware problem (eg disk failure)</desc>
|
||||
* <term>EcAlarmEventTypeProcessingError</term>
|
||||
* <desc>A software problem (a bug or misconfiguration)</desc>
|
||||
* <term>EcAlarmEventTypeQualityOfService</term>
|
||||
* <desc>A system not running as well as expected ... eg. overloaded</desc>
|
||||
* </deflist>
|
||||
*/
|
||||
typedef enum {
|
||||
EcAlarmEventTypeUnknown = 0,
|
||||
EcAlarmEventTypeCommunications = 2,
|
||||
EcAlarmEventTypeEnvironmental = 3,
|
||||
EcAlarmEventTypeEquipment = 4,
|
||||
EcAlarmEventTypeProcessingError = 10,
|
||||
EcAlarmEventTypeQualityOfService = 11,
|
||||
} EcAlarmEventType;
|
||||
|
||||
/** The EcAlarmProbableCause enumeration defines the probable causes of
|
||||
* alarms produced by the system.<br />
|
||||
* These are taken from the CCITT X.733 specification with the numeric
|
||||
* values from the CCITT X.721 specification.<br />
|
||||
* Enumeration values include a comment to specify which
|
||||
* EcAlarmEventType they apply to.
|
||||
* <deflist>
|
||||
* <term>EcAlarmProbableCauseUnknown</term>
|
||||
* <desc>Category: Any</desc>
|
||||
* <term>EcAlarmAdapterError</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmApplicationSubsystemFailure</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* <term>EcAlarmBandwidthReduced</term>
|
||||
* <desc>Category: QoS</desc>
|
||||
* <term>EcAlarmCallEstablishmentError</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmCommunicationsProtocolError</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmCommunicationsSubsystemFailure</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmConfigurationOrCustomizationError</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* <term>EcAlarmCongestion</term>
|
||||
* <desc>Category: QoS</desc>
|
||||
* <term>EcAlarmCorruptData</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* <term>EcAlarmCpuCyclesLimitExceeded</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* <term>EcAlarmDataSetOrModemError</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmDegradedSignal</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmDTE_DCEInterfaceError</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmEnclosureDoorOpen</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmEquipmentMalfunction</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmExcessiveVibration</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmFileError</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* <term>EcAlarmFireDetected</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmFloodDetected</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmFramingError</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmHeatingOrVentilationOrCoolingSystemProblem</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmHumidityUnacceptable</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmInputOutputDeviceError</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmInputDeviceError</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmLANError</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmLeakDetected</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmLocalNodeTransmissionError</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmLossOfFrame</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmLossOfSignal</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmMaterialSupplyExhausted</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmMultiplexerProblem</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmOutOfMemory</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* <term>EcAlarmOuputDeviceError</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmPerformanceDegraded</term>
|
||||
* <desc>Category: QoS</desc>
|
||||
* <term>EcAlarmPowerProblem</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmPressureUnacceptable</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmProcessorProblem</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmPumpFailure</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmQueueSizeExceeded</term>
|
||||
* <desc>Category: QoS</desc>
|
||||
* <term>EcAlarmReceiveFailure</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmReceiverFailure</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmRemoteNodeTransmissionError</term>
|
||||
* <desc>Category: Communications</desc>
|
||||
* <term>EcAlarmResourceAtOrNearingCapacity</term>
|
||||
* <desc>Category: QoS</desc>
|
||||
* <term>EcAlarmResponseTimeExcessive</term>
|
||||
* <desc>Category: QoS</desc>
|
||||
* <term>EcAlarmRetransmissionRateExcessive</term>
|
||||
* <desc>Category: QoS</desc>
|
||||
* <term>EcAlarmSoftwareProgramAbnormallyTerminated</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* <term>EcAlarmSoftwareProgramError</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* <term>EcAlarmStorageCapacityProblem</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* <term>EcAlarmTemperatureUnacceptable</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmThresholdCrossed</term>
|
||||
* <desc>Category: QoS</desc>
|
||||
* <term>EcAlarmTimingProblem</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmToxicLeakDetected</term>
|
||||
* <desc>Category: Environmental</desc>
|
||||
* <term>EcAlarmTransmitFailure</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmTransmitterFailure</term>
|
||||
* <desc>Category: Equipment</desc>
|
||||
* <term>EcAlarmUnderlyingResourceUnavailable</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* <term>EcAlarmVersionMismatch</term>
|
||||
* <desc>Category: Processing</desc>
|
||||
* </deflist>
|
||||
*/
|
||||
typedef enum {
|
||||
EcAlarmProbableCauseUnknown = 0, // Any
|
||||
|
||||
EcAlarmAdapterError = 1, // Equipment
|
||||
EcAlarmApplicationSubsystemFailure = 2, // Processing
|
||||
EcAlarmBandwidthReduced = 3, // QoS
|
||||
EcAlarmCallEstablishmentError = 4, // Communications
|
||||
EcAlarmCommunicationsProtocolError = 5, // Communications
|
||||
EcAlarmCommunicationsSubsystemFailure = 6, // Communications
|
||||
EcAlarmConfigurationOrCustomizationError = 7, // Processing
|
||||
EcAlarmCongestion = 8, // QoS
|
||||
EcAlarmCorruptData = 9, // Processing
|
||||
EcAlarmCpuCyclesLimitExceeded = 10, // Processing
|
||||
EcAlarmDataSetOrModemError = 11, // Equipment
|
||||
EcAlarmDegradedSignal = 12, // Communications
|
||||
EcAlarmDTE_DCEInterfaceError = 13, // Communications
|
||||
EcAlarmEnclosureDoorOpen = 14, // Environmental
|
||||
EcAlarmEquipmentMalfunction = 15, // Equipment
|
||||
EcAlarmExcessiveVibration = 16, // Environmental
|
||||
EcAlarmFileError = 17, // Processing
|
||||
EcAlarmFireDetected = 18, // Environmental
|
||||
EcAlarmFloodDetected = 19, // Environmental
|
||||
EcAlarmFramingError = 20, // Communications
|
||||
EcAlarmHeatingOrVentilationOrCoolingSystemProblem = 21, // Environmental
|
||||
EcAlarmHumidityUnacceptable = 22, // Environmental
|
||||
EcAlarmInputOutputDeviceError = 23, // Equipment
|
||||
EcAlarmInputDeviceError = 24, // Equipment
|
||||
EcAlarmLANError = 25, // Communications
|
||||
EcAlarmLeakDetected = 26, // Environmental
|
||||
EcAlarmLocalNodeTransmissionError = 27, // Communications
|
||||
EcAlarmLossOfFrame = 28, // Communications
|
||||
EcAlarmLossOfSignal = 29, // Communications
|
||||
EcAlarmMaterialSupplyExhausted = 30, // Environmental
|
||||
EcAlarmMultiplexerProblem = 31, // Equipment
|
||||
EcAlarmOutOfMemory = 32, // Processing
|
||||
EcAlarmOuputDeviceError = 33, // Equipment
|
||||
EcAlarmPerformanceDegraded = 34, // QoS
|
||||
EcAlarmPowerProblem = 35, // Equipment
|
||||
EcAlarmPressureUnacceptable = 36, // Environmental
|
||||
EcAlarmProcessorProblem = 37, // Equipment
|
||||
EcAlarmPumpFailure = 38, // Environmental
|
||||
EcAlarmQueueSizeExceeded = 39, // QoS
|
||||
EcAlarmReceiveFailure = 40, // Equipment
|
||||
EcAlarmReceiverFailure = 41, // Equipment
|
||||
EcAlarmRemoteNodeTransmissionError = 42, // Communications
|
||||
EcAlarmResourceAtOrNearingCapacity = 43, // QoS
|
||||
EcAlarmResponseTimeExcessive = 44, // QoS
|
||||
EcAlarmRetransmissionRateExcessive = 45, // QoS
|
||||
EcAlarmSoftwareProgramAbnormallyTerminated = 47, // Processing
|
||||
EcAlarmSoftwareProgramError = 48, // Processing
|
||||
EcAlarmStorageCapacityProblem = 49, // Processing
|
||||
EcAlarmTemperatureUnacceptable = 50, // Environmental
|
||||
EcAlarmThresholdCrossed = 51, // QoS
|
||||
EcAlarmTimingProblem = 52, // Equipment
|
||||
EcAlarmToxicLeakDetected = 53, // Environmental
|
||||
EcAlarmTransmitFailure = 54, // Equipment
|
||||
EcAlarmTransmitterFailure = 55, // Equipment
|
||||
EcAlarmUnderlyingResourceUnavailable = 56, // Processing
|
||||
EcAlarmVersionMismatch = 57, // Processing
|
||||
|
||||
} EcAlarmProbableCause;
|
||||
|
||||
/** The EcAlarmSeverity enumeration defines the 'perceived severities' of
|
||||
* alarms produced by the system.<br />
|
||||
* The enumerated values MUST be matched by those in your SNMP MIB if
|
||||
* you wish to have your software interact with SNMP tools.<br />
|
||||
* NB. The use of EcAlarmSeverityIndeterminate should be avoided.
|
||||
* <deflist>
|
||||
* <term>EcAlarmSeverityIndeterminate</term><desc>Do not use</desc>
|
||||
* <term>EcAlarmSeverityCritical</term>
|
||||
* <desc>Immediate intervention required to restore service</desc>
|
||||
* <term>EcAlarmSeverityMajor</term>
|
||||
* <desc>Severe but partial system failure or a problem which
|
||||
* might recover without intervention</desc>
|
||||
* <term>EcAlarmSeverityMinor</term>
|
||||
* <desc>A failure, but one which is likely to recover or which is
|
||||
* probably not urgent</desc>
|
||||
* <term>EcAlarmSeverityWarning</term>
|
||||
* <desc>An unusual event which may not indicate any problem, but
|
||||
* which ought to be looked into</desc>
|
||||
* <term>EcAlarmSeverityCleared</term>
|
||||
* <desc>This indicates the resolution of an earlier issue</desc>
|
||||
* </deflist>
|
||||
*/
|
||||
typedef enum {
|
||||
EcAlarmSeverityIndeterminate = 0,
|
||||
EcAlarmSeverityCritical = 1,
|
||||
EcAlarmSeverityMajor = 2,
|
||||
EcAlarmSeverityMinor = 3,
|
||||
EcAlarmSeverityWarning = 4,
|
||||
EcAlarmSeverityCleared = 5
|
||||
} EcAlarmSeverity;
|
||||
|
||||
/** The EcAlarmTrend enumeration defines the severity trend of alarms
|
||||
* produced by the system.<br />
|
||||
* The enumerated values MUST be matched by those in your SNMP MIB if
|
||||
* you wish to have your software interact with SNMP tools.<br />
|
||||
* <deflist>
|
||||
* <term>EcAlarmTrendNone</term>
|
||||
* <desc>This is not a change in severity of an earlier alarm</desc>
|
||||
* <term>EcAlarmTrendUp</term>
|
||||
* <desc>This is a more severe version of an earlier alarm</desc>
|
||||
* <term>EcAlarmTrendDown</term>
|
||||
* <desc>This is a less severe version of an earlier alarm</desc>
|
||||
* </deflist>
|
||||
*/
|
||||
typedef enum {
|
||||
EcAlarmTrendNone = 0,
|
||||
EcAlarmTrendUp = '+',
|
||||
EcAlarmTrendDown = '-',
|
||||
} EcAlarmTrend;
|
||||
|
||||
/** This function builds a managed object name from host, process,
|
||||
* and component.<br />
|
||||
* The host part may be nil ... for the current host.<br />
|
||||
* The process part may be nil ... for the current process.<br />
|
||||
* The component may well be nil if the alert applies to the process as
|
||||
* a whole rather than to a particular component. This field is typically
|
||||
* used to identify a particular network connection etc within a process.<br />
|
||||
* This parses the process and separates out any instance ID (trailing
|
||||
* hyphen and string of digits). It then builds the managed object from
|
||||
* four parts (host, process, instance, component) separated by underscores.
|
||||
* Any underscores in the arguments are replaced by hyphens.<br />
|
||||
* NB. The total length must not exceed 127 ASCII characters.
|
||||
*/
|
||||
NSString *
|
||||
EcMakeManagedObject(NSString *host, NSString *process, NSString *component);
|
||||
|
||||
/** <p>The EcAlarm class encapsulates an alarm to be sent out to a monitoring
|
||||
* system. It's designed to work cleanly with industry standard SNMP
|
||||
* alarm monitoring systems. For more information on how the SNMP
|
||||
* operation works, see the [EcAlarmSinkSNMP] class documentation.
|
||||
* </p>
|
||||
* <p>Instances are created and sent to a central coordination point
|
||||
* where checks are performed to see if there is an existing alarm for
|
||||
* the same issue. If the incoming alarm does not change the severity
|
||||
* of an existing alarm, it is ignored, otherwise it may be passed on to
|
||||
* an external monitoring system. The central coordination system is
|
||||
* responsible for ensuring that alarms for the same issue are updated
|
||||
* to contain the first event date, notification ID and a trend indicator.
|
||||
* </p>
|
||||
*/
|
||||
@interface EcAlarm : NSObject <NSCoding,NSCopying>
|
||||
{
|
||||
NSString *_managedObject;
|
||||
NSDate *_eventDate;
|
||||
NSDate *_firstEventDate;
|
||||
EcAlarmEventType _eventType;
|
||||
EcAlarmSeverity _perceivedSeverity;
|
||||
EcAlarmProbableCause _probableCause;
|
||||
NSString *_specificProblem;
|
||||
NSString *_proposedRepairAction;
|
||||
NSString *_additionalText;
|
||||
EcAlarmTrend _trendIndicator;
|
||||
int _notificationID;
|
||||
void *_extra;
|
||||
BOOL _frozen;
|
||||
}
|
||||
|
||||
/** Creates and returns an autoreleased instance by calling the
|
||||
* designated initialiser with all the supplied arguments.
|
||||
*/
|
||||
+ (EcAlarm*) alarmForManagedObject: (NSString*)managedObject
|
||||
at: (NSDate*)eventDate
|
||||
withEventType: (EcAlarmEventType)eventType
|
||||
probableCause: (EcAlarmProbableCause)probableCause
|
||||
specificProblem: (NSString*)specificProblem
|
||||
perceivedSeverity: (EcAlarmSeverity)perceivedSeverity
|
||||
proposedRepairAction: (NSString*)proposedRepairAction
|
||||
additionalText: (NSString*)additionalText;
|
||||
|
||||
/** This method provides a mapping from the probable cause of an event to
|
||||
* the event type.<br />
|
||||
* The method is called during initialisation of an alarm instance (except
|
||||
* where the probable cause is EcAlarmProbableCauseUnknown) to check that the
|
||||
* supplied arguments are consistent. If a subclass extends the possible
|
||||
* probable cause values, it must also override this method to handle those
|
||||
* new values by returning a known event type.
|
||||
*/
|
||||
+ (EcAlarmEventType) eventTypeFromProbableCause: (EcAlarmProbableCause)value;
|
||||
|
||||
/** Provides a human readable string representation of an event type.<br />
|
||||
* This method is called during initialisation of an alarm instance to check
|
||||
* that the supplied event type is legal.<br />
|
||||
* Returns nil if the value is unknown.<br />
|
||||
*/
|
||||
+ (NSString*) stringFromEventType: (EcAlarmEventType)value;
|
||||
|
||||
/** Provides a human readable string representation of a probable cause.<br />
|
||||
* Returns nil if the value is unknown.
|
||||
*/
|
||||
+ (NSString*) stringFromProbableCause: (EcAlarmProbableCause)value;
|
||||
|
||||
/** Provides a human readable string representation of a severity.<br />
|
||||
* Returns nil if the value is unknown.
|
||||
*/
|
||||
+ (NSString*) stringFromSeverity: (EcAlarmSeverity)value;
|
||||
|
||||
/** Provides a human readable string representation of a trend.<br />
|
||||
* Returns nil if the value is unknown.
|
||||
*/
|
||||
+ (NSString*) stringFromTrend: (EcAlarmTrend)value;
|
||||
|
||||
|
||||
/** This is the supplementary text (optional) which may be provided with
|
||||
* and alarm an an aid to the human operator for the monitoring system.
|
||||
*/
|
||||
- (NSString*) additionalText;
|
||||
|
||||
/** Compares the other object with the receiver for sorting/ordering.<br />
|
||||
* If both objects have a notificationID set then the result of the
|
||||
* numeric comparison of those IDs is used.<br />
|
||||
* Otherwise the result of the comparison orders the objects by
|
||||
* managedObject, eventType, probableCause, and specificProblem.
|
||||
*/
|
||||
- (NSComparisonResult) compare: (EcAlarm*)other;
|
||||
|
||||
/** Returns an autoreleased copy of the receiver with the same notificationID
|
||||
* but with a perceivedSeverity set to be EcAlarmSeverityCleared ... this may
|
||||
* be used to clear the alarm represented by the receiver.
|
||||
*/
|
||||
- (EcAlarm*) clear;
|
||||
|
||||
/** EcAlarm objects may be copied. This method is provided to implement
|
||||
* the NSCopying protocol.<br />
|
||||
* A copy of an object does <em>not</em> copy any value provided by the
|
||||
* -setExtra: method.
|
||||
*/
|
||||
- (id) copyWithZone: (NSZone*)aZone;
|
||||
|
||||
/** Deallocates the receiver.
|
||||
*/
|
||||
- (void) dealloc;
|
||||
|
||||
/** EcAlarm objects may be passed over the distributed objects system or
|
||||
* archived. This method is provided to implement the NSCoding protocol.<br />
|
||||
* An encoded copy of an object does <em>not</em> copy any value provided
|
||||
* by the -setExtra: method.
|
||||
*/
|
||||
- (void) encodeWithCoder: (NSCoder*)aCoder;
|
||||
|
||||
/** Returns the timestamp of the event which generated the alarm.
|
||||
*/
|
||||
- (NSDate*) eventDate;
|
||||
|
||||
/** This method returns the type for event which generated the alarm.
|
||||
*/
|
||||
- (EcAlarmEventType) eventType;
|
||||
|
||||
/** Returns any extra information stored by the -setExtra: method.<br />
|
||||
*/
|
||||
- (void*) extra;
|
||||
|
||||
/** If this alarm is known to be represent an event updating the status of
|
||||
* an existing alarm, this method returns the date of the initial event.
|
||||
* otherwise it returns nil.
|
||||
*/
|
||||
- (NSDate*) firstEventDate;
|
||||
|
||||
/** Freeze the state of the alarm ... no more calls to setters are permitted.
|
||||
*/
|
||||
- (void) freeze;
|
||||
|
||||
/** Returns the hash of the receiver ... which is also the hash of its
|
||||
* managedObject.
|
||||
*/
|
||||
- (NSUInteger) hash;
|
||||
|
||||
/** <init/>
|
||||
* Initialises the receiver as an alarm for a particular event.<br />
|
||||
* The eventDate argument may be nil if the alarm should use the current
|
||||
* timestamp.<br />
|
||||
* The managedObject, eventType, probableCause, and specificProblem
|
||||
* arguments uniquely identify the issue for which an alarm is being
|
||||
* produced.<br />
|
||||
* The perceivedSeverity indicates the importance of the problem, with a
|
||||
* value of EcAlarmSeverityCleared indicating that the problem is over.<br />
|
||||
* A proposedRepairAction is mandatory (unless the severity is cleared)
|
||||
* to provide the human operator with some sort of hint about how they
|
||||
* should resolve the issue.
|
||||
*/
|
||||
- (id) initForManagedObject: (NSString*)managedObject
|
||||
at: (NSDate*)eventDate
|
||||
withEventType: (EcAlarmEventType)eventType
|
||||
probableCause: (EcAlarmProbableCause)probableCause
|
||||
specificProblem: (NSString*)specificProblem
|
||||
perceivedSeverity: (EcAlarmSeverity)perceivedSeverity
|
||||
proposedRepairAction: (NSString*)proposedRepairAction
|
||||
additionalText: (NSString*)additionalText;
|
||||
|
||||
/** EcAlarm objects may be passed over the distributed objects system or
|
||||
* archived. This method is provided to implement the NSCoding protocol.
|
||||
*/
|
||||
- (id) initWithCoder: (NSCoder*)aCoder;
|
||||
|
||||
/** Returns a flag indicating whether the receiver is equal to the other
|
||||
* object. To be considered equal either:<br />
|
||||
* The two objects must have equal managedObject values and equal (non-zero)
|
||||
* notificationID values or<br />
|
||||
* the two objects must have equal managedObject values,
|
||||
* equal eventType values, equal probableCause values,
|
||||
* and equal specificProblem values.<br />
|
||||
* NB. you must not set two alarm instances to have the same notificationID
|
||||
* values if they are not considered equal using the other criteria.
|
||||
*/
|
||||
- (BOOL) isEqual: (id)other;
|
||||
|
||||
/** Returns the managedObject value set when the receiver was initialised.
|
||||
*/
|
||||
- (NSString*) managedObject;
|
||||
|
||||
/** Returns the component of the managed object (if any).
|
||||
*/
|
||||
- (NSString*) moComponent;
|
||||
|
||||
/** Returns the host of the managed object.
|
||||
*/
|
||||
- (NSString*) moHost;
|
||||
|
||||
/** Returns the instance of the managed object (if any).
|
||||
*/
|
||||
- (NSString*) moInstance;
|
||||
|
||||
/** Returns the process name of the managed object.
|
||||
*/
|
||||
- (NSString*) moProcess;
|
||||
|
||||
/** Returns zero or the notificationID value most recently set by
|
||||
* the -setNotificationID: method.
|
||||
*/
|
||||
- (int) notificationID;
|
||||
|
||||
/** Returns the perceivedSeverity set when the receiver was initialised.
|
||||
*/
|
||||
- (EcAlarmSeverity) perceivedSeverity;
|
||||
|
||||
/** Returns the proposedRepairAction set when the receiver was initialised.
|
||||
*/
|
||||
- (NSString*) proposedRepairAction;
|
||||
|
||||
/** Returns the probableCause set when the receiver was initialised.
|
||||
*/
|
||||
- (EcAlarmProbableCause) probableCause;
|
||||
|
||||
/** Sets extra data for the current instance.<br />
|
||||
* Extra data is not copied, archived, or transferred over DO, it is available
|
||||
* only in the exact instance of the class in which it was set.
|
||||
*/
|
||||
- (void) setExtra: (void*)extra;
|
||||
|
||||
/** Sets the first event date for the receiver.<br />
|
||||
* You should not normally call this as it is reserved for use by code
|
||||
* which has matched the receiver to an existing alarm.
|
||||
*/
|
||||
- (void) setFirstEventDate: (NSDate*)firstEventDate;
|
||||
|
||||
/** Sets the notification ID for the receiver.<br />
|
||||
* You should not normally call this as it is reserved for use by code
|
||||
* which has matched the receiver to an existing alarm.<br />
|
||||
* In particular, two instances should not be set to have the same non-zero
|
||||
* notificationID unless they are equal according to other criteria of
|
||||
* equality (ie have the same managedObject, eventType, probableCause,
|
||||
* and specificProblem values).
|
||||
*/
|
||||
- (void) setNotificationID: (int)notificationID;
|
||||
|
||||
/** Sets the trend indicator for the receiver.<br />
|
||||
* You should not normally call this as it is reserved for use by code
|
||||
* which has matched the receiver to an existing alarm.
|
||||
*/
|
||||
- (void) setTrendIndicator: (EcAlarmTrend)trendIndicator;
|
||||
|
||||
/** Returns the specificProblem set when the receiver was initialised.
|
||||
*/
|
||||
- (NSString*) specificProblem;
|
||||
|
||||
/** Returns the value most recently set by the -setTrendIndicator: method
|
||||
* (or EcAlarmTrendNone if that method has not been called).<br />
|
||||
* This tells you whether the receiver represents an increase in severity
|
||||
* of an issue, or a decrease in severity (or no change).
|
||||
*/
|
||||
- (EcAlarmTrend) trendIndicator;
|
||||
|
||||
@end
|
||||
|
||||
@interface EcAlarm (Convenience)
|
||||
|
||||
/** Generates an alarm to clears an alarm previously generated.<br />
|
||||
* The componentName may be nil for a process-wide alarm.<br />
|
||||
* The probableCause must NOT be unknown ... it is used to infer
|
||||
* the event type.<br />
|
||||
* The specificProblem must be identical to the value supplied in the
|
||||
* original alarm that this is intended to clear.
|
||||
*/
|
||||
+ (EcAlarm*) clear: (NSString*)componentName
|
||||
cause: (EcAlarmProbableCause)probableCause
|
||||
problem: (NSString*)specificProblem;
|
||||
|
||||
|
||||
/** Generates a new alarm event wuth minimal parameters.<br />
|
||||
* The componentName may be nil for a process-wide alarm.<br />
|
||||
* The probablCause must NOT be unknown ... it is used to infer
|
||||
* the event type.<br />
|
||||
* The specificProblem is used to identify the event for which the
|
||||
* alarm is raised.<br />
|
||||
* The perceivedSeverity must be one of EcAlarmSeverityWarning,
|
||||
* EcAlarmSeverityMinor, EcAlarmSeverityMajor or EcAlarmSeverityCritical.<br />
|
||||
* The proposedRepairAction must contain information sufficient
|
||||
* for any person receiving notification of the alarm to be able
|
||||
* to deal with it. The action is a format string, optionally
|
||||
* followed by any number of arguments to be incorporated into
|
||||
* the repair action. NB. The resulting proposed repair action
|
||||
* must be no more than 255 bytes in length when converted to
|
||||
* UTF-8 data.
|
||||
*/
|
||||
+ (EcAlarm*) raise: (NSString*)componentName
|
||||
cause: (EcAlarmProbableCause)probableCause
|
||||
problem: (NSString*)specificProblem
|
||||
severity: (EcAlarmSeverity)perceivedSeverity
|
||||
action: (NSString*)proposedRepairAction, ...;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
879
EcAlarm.m
Normal file
879
EcAlarm.m
Normal file
|
@ -0,0 +1,879 @@
|
|||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCoder.h>
|
||||
#import <Foundation/NSDate.h>
|
||||
#import <Foundation/NSException.h>
|
||||
#import <Foundation/NSHost.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import "EcProcess.h"
|
||||
#import "EcAlarm.h"
|
||||
|
||||
@class NSPortCoder;
|
||||
|
||||
NSString *
|
||||
EcMakeManagedObject(NSString *host, NSString *process, NSString *component)
|
||||
{
|
||||
NSString *instance = @"";
|
||||
NSRange r;
|
||||
|
||||
if (nil == host)
|
||||
{
|
||||
host = [[NSHost currentHost] name];
|
||||
}
|
||||
else
|
||||
{
|
||||
// No underscores permitted.
|
||||
host = [host stringByReplacingString: @"_" withString: @"-"];
|
||||
}
|
||||
|
||||
if (nil == process)
|
||||
{
|
||||
process = [EcProc cmdName];
|
||||
}
|
||||
|
||||
/* Extract the instance number from the process name (the instance would
|
||||
* be a string of digits after a hyphen) if it's there.
|
||||
*/
|
||||
r = [process rangeOfString: @"-"
|
||||
options: NSCaseInsensitiveSearch|NSBackwardsSearch];
|
||||
if (r.length > 0)
|
||||
{
|
||||
NSString *s = [process substringFromIndex: NSMaxRange(r)];
|
||||
unsigned l = [s length];
|
||||
|
||||
if (l > 0)
|
||||
{
|
||||
while (l-- > 0)
|
||||
{
|
||||
unichar c = [s characterAtIndex: l];
|
||||
|
||||
if (c < '0' || c > '9')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (0 == l)
|
||||
{
|
||||
instance = s;
|
||||
process = [process substringToIndex: r.location];
|
||||
}
|
||||
}
|
||||
}
|
||||
// No underscores permitted.
|
||||
process = [process stringByReplacingString: @"_" withString: @"-"];
|
||||
|
||||
if (nil == component)
|
||||
{
|
||||
component = @"";
|
||||
}
|
||||
else
|
||||
{
|
||||
// No underscores permitted.
|
||||
component = [component stringByReplacingString: @"_" withString: @"-"];
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat: @"%@_%@_%@_%@",
|
||||
host, process, instance, component];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@implementation EcAlarm
|
||||
|
||||
+ (EcAlarm*) alarmForManagedObject: (NSString*)managedObject
|
||||
at: (NSDate*)eventDate
|
||||
withEventType: (EcAlarmEventType)eventType
|
||||
probableCause: (EcAlarmProbableCause)probableCause
|
||||
specificProblem: (NSString*)specificProblem
|
||||
perceivedSeverity: (EcAlarmSeverity)perceivedSeverity
|
||||
proposedRepairAction: (NSString*)proposedRepairAction
|
||||
additionalText: (NSString*)additionalText
|
||||
{
|
||||
EcAlarm *a = [self alloc];
|
||||
|
||||
a = [a initForManagedObject: managedObject
|
||||
at: eventDate
|
||||
withEventType: eventType
|
||||
probableCause: probableCause
|
||||
specificProblem: specificProblem
|
||||
perceivedSeverity: perceivedSeverity
|
||||
proposedRepairAction: proposedRepairAction
|
||||
additionalText: additionalText];
|
||||
return [a autorelease];
|
||||
}
|
||||
|
||||
+ (EcAlarmEventType) eventTypeFromProbableCause: (EcAlarmProbableCause)value
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case EcAlarmProbableCauseUnknown: // Don't call with this value
|
||||
return EcAlarmEventTypeUnknown;
|
||||
|
||||
case EcAlarmCallEstablishmentError:
|
||||
case EcAlarmCommunicationsProtocolError:
|
||||
case EcAlarmCommunicationsSubsystemFailure:
|
||||
case EcAlarmDegradedSignal:
|
||||
case EcAlarmDTE_DCEInterfaceError:
|
||||
case EcAlarmFramingError:
|
||||
case EcAlarmLANError:
|
||||
case EcAlarmLocalNodeTransmissionError:
|
||||
case EcAlarmLossOfFrame:
|
||||
case EcAlarmLossOfSignal:
|
||||
case EcAlarmRemoteNodeTransmissionError:
|
||||
return EcAlarmEventTypeCommunications;
|
||||
|
||||
case EcAlarmEnclosureDoorOpen:
|
||||
case EcAlarmExcessiveVibration:
|
||||
case EcAlarmFireDetected:
|
||||
case EcAlarmFloodDetected:
|
||||
case EcAlarmHeatingOrVentilationOrCoolingSystemProblem:
|
||||
case EcAlarmHumidityUnacceptable:
|
||||
case EcAlarmLeakDetected:
|
||||
case EcAlarmMaterialSupplyExhausted:
|
||||
case EcAlarmPressureUnacceptable:
|
||||
case EcAlarmPumpFailure:
|
||||
case EcAlarmTemperatureUnacceptable:
|
||||
case EcAlarmToxicLeakDetected:
|
||||
return EcAlarmEventTypeEnvironmental;
|
||||
|
||||
case EcAlarmAdapterError:
|
||||
case EcAlarmDataSetOrModemError:
|
||||
case EcAlarmEquipmentMalfunction:
|
||||
case EcAlarmInputDeviceError:
|
||||
case EcAlarmInputOutputDeviceError:
|
||||
case EcAlarmMultiplexerProblem:
|
||||
case EcAlarmOuputDeviceError:
|
||||
case EcAlarmPowerProblem:
|
||||
case EcAlarmProcessorProblem:
|
||||
case EcAlarmReceiveFailure:
|
||||
case EcAlarmReceiverFailure:
|
||||
case EcAlarmTimingProblem:
|
||||
case EcAlarmTransmitFailure:
|
||||
case EcAlarmTransmitterFailure:
|
||||
return EcAlarmEventTypeEquipment;
|
||||
|
||||
case EcAlarmApplicationSubsystemFailure:
|
||||
case EcAlarmConfigurationOrCustomizationError:
|
||||
case EcAlarmCorruptData:
|
||||
case EcAlarmCpuCyclesLimitExceeded:
|
||||
case EcAlarmFileError:
|
||||
case EcAlarmOutOfMemory:
|
||||
case EcAlarmSoftwareProgramAbnormallyTerminated:
|
||||
case EcAlarmSoftwareProgramError:
|
||||
case EcAlarmStorageCapacityProblem:
|
||||
case EcAlarmUnderlyingResourceUnavailable:
|
||||
case EcAlarmVersionMismatch:
|
||||
return EcAlarmEventTypeProcessingError;
|
||||
|
||||
case EcAlarmBandwidthReduced:
|
||||
case EcAlarmCongestion:
|
||||
case EcAlarmPerformanceDegraded:
|
||||
case EcAlarmQueueSizeExceeded:
|
||||
case EcAlarmResourceAtOrNearingCapacity:
|
||||
case EcAlarmResponseTimeExcessive:
|
||||
case EcAlarmRetransmissionRateExcessive:
|
||||
case EcAlarmThresholdCrossed:
|
||||
return EcAlarmEventTypeQualityOfService;
|
||||
}
|
||||
return EcAlarmEventTypeUnknown;
|
||||
}
|
||||
|
||||
+ (NSString*) stringFromEventType: (EcAlarmEventType)value
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case EcAlarmEventTypeUnknown: // Not legal
|
||||
return nil;
|
||||
case EcAlarmEventTypeCommunications:
|
||||
return @"EcAlarmEventTypeCommunications";
|
||||
case EcAlarmEventTypeEnvironmental:
|
||||
return @"EcAlarmEventTypeEnvironmental";
|
||||
case EcAlarmEventTypeEquipment:
|
||||
return @"EcAlarmEventTypeEquipment";
|
||||
case EcAlarmEventTypeProcessingError:
|
||||
return @"EcAlarmEventTypeProcessingError";
|
||||
case EcAlarmEventTypeQualityOfService:
|
||||
return @"EcAlarmEventTypeQualityOfService";
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSString*) stringFromProbableCause: (EcAlarmProbableCause)value
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case EcAlarmProbableCauseUnknown:
|
||||
return @"EcAlarmProbableCauseUnknown";
|
||||
case EcAlarmAdapterError:
|
||||
return @"adapterError";
|
||||
case EcAlarmApplicationSubsystemFailure:
|
||||
return @"applicationSubsystemFailure";
|
||||
case EcAlarmBandwidthReduced:
|
||||
return @"bandwidthReduced";
|
||||
case EcAlarmCallEstablishmentError:
|
||||
return @"callEstablishmentError";
|
||||
case EcAlarmCommunicationsProtocolError:
|
||||
return @"communicationsProtocolError";
|
||||
case EcAlarmCommunicationsSubsystemFailure:
|
||||
return @"communicationsSubsystemFailure";
|
||||
case EcAlarmConfigurationOrCustomizationError:
|
||||
return @"configurationOrCustomizationError";
|
||||
case EcAlarmCongestion:
|
||||
return @"congestion";
|
||||
case EcAlarmCorruptData:
|
||||
return @"corruptData";
|
||||
case EcAlarmCpuCyclesLimitExceeded:
|
||||
return @"cpuCyclesLimitExceeded";
|
||||
case EcAlarmDataSetOrModemError:
|
||||
return @"dataSetOrModemError";
|
||||
case EcAlarmDegradedSignal:
|
||||
return @"degradedSignal";
|
||||
case EcAlarmDTE_DCEInterfaceError:
|
||||
return @"dTE-DCEInterfaceError";
|
||||
case EcAlarmEnclosureDoorOpen:
|
||||
return @"enclosureDoorOpen";
|
||||
case EcAlarmEquipmentMalfunction:
|
||||
return @"equipmentMalfunction";
|
||||
case EcAlarmExcessiveVibration:
|
||||
return @"excessiveVibration";
|
||||
case EcAlarmFileError:
|
||||
return @"fileError";
|
||||
case EcAlarmFireDetected:
|
||||
return @"fireDetected";
|
||||
case EcAlarmFloodDetected:
|
||||
return @"floodDetected";
|
||||
case EcAlarmFramingError:
|
||||
return @"framingError";
|
||||
case EcAlarmHeatingOrVentilationOrCoolingSystemProblem:
|
||||
return @"heatingOrVentilationOrCoolingSystemProblem";
|
||||
case EcAlarmHumidityUnacceptable:
|
||||
return @"humidityUnacceptable";
|
||||
case EcAlarmInputOutputDeviceError:
|
||||
return @"inputOutputDeviceError";
|
||||
case EcAlarmInputDeviceError:
|
||||
return @"inputDeviceError";
|
||||
case EcAlarmLANError:
|
||||
return @"lANError";
|
||||
case EcAlarmLeakDetected:
|
||||
return @"leakDetected";
|
||||
case EcAlarmLocalNodeTransmissionError:
|
||||
return @"localNodeTransmissionError";
|
||||
case EcAlarmLossOfFrame:
|
||||
return @"lossOfFrame";
|
||||
case EcAlarmLossOfSignal:
|
||||
return @"lossOfSignal";
|
||||
case EcAlarmMaterialSupplyExhausted:
|
||||
return @"materialSupplyExhausted";
|
||||
case EcAlarmMultiplexerProblem:
|
||||
return @"multiplexerProblem";
|
||||
case EcAlarmOutOfMemory:
|
||||
return @"outOfMemory";
|
||||
case EcAlarmOuputDeviceError:
|
||||
return @"ouputDeviceError";
|
||||
case EcAlarmPerformanceDegraded:
|
||||
return @"performanceDegraded";
|
||||
case EcAlarmPowerProblem:
|
||||
return @"powerProblem";
|
||||
case EcAlarmPressureUnacceptable:
|
||||
return @"pressureUnacceptable";
|
||||
case EcAlarmProcessorProblem:
|
||||
return @"processorProblem";
|
||||
case EcAlarmPumpFailure:
|
||||
return @"pumpFailure";
|
||||
case EcAlarmQueueSizeExceeded:
|
||||
return @"queueSizeExceeded";
|
||||
case EcAlarmReceiveFailure:
|
||||
return @"receiveFailure";
|
||||
case EcAlarmReceiverFailure:
|
||||
return @"receiverFailure";
|
||||
case EcAlarmRemoteNodeTransmissionError:
|
||||
return @"remoteNodeTransmissionError";
|
||||
case EcAlarmResourceAtOrNearingCapacity:
|
||||
return @"resourceAtOrNearingCapacity";
|
||||
case EcAlarmResponseTimeExcessive:
|
||||
return @"responseTimeExcessive";
|
||||
case EcAlarmRetransmissionRateExcessive:
|
||||
return @"retransmissionRateExcessive";
|
||||
case EcAlarmSoftwareProgramAbnormallyTerminated:
|
||||
return @"softwareProgramAbnormallyTerminated";
|
||||
case EcAlarmSoftwareProgramError:
|
||||
return @"softwareProgramError";
|
||||
case EcAlarmStorageCapacityProblem:
|
||||
return @"storageCapacityProblem";
|
||||
case EcAlarmTemperatureUnacceptable:
|
||||
return @"temperatureUnacceptable";
|
||||
case EcAlarmThresholdCrossed:
|
||||
return @"thresholdCrossed";
|
||||
case EcAlarmTimingProblem:
|
||||
return @"timingProblem";
|
||||
case EcAlarmToxicLeakDetected:
|
||||
return @"toxicLeakDetected";
|
||||
case EcAlarmTransmitFailure:
|
||||
return @"transmitFailure";
|
||||
case EcAlarmTransmitterFailure:
|
||||
return @"transmitterFailure";
|
||||
case EcAlarmUnderlyingResourceUnavailable:
|
||||
return @"underlyingResourceUnavailable";
|
||||
case EcAlarmVersionMismatch:
|
||||
return @"versionMismatch";
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSString*) stringFromSeverity: (EcAlarmSeverity)value
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case EcAlarmSeverityIndeterminate: return @"EcAlarmSeverityIndeterminate";
|
||||
case EcAlarmSeverityCritical: return @"EcAlarmSeverityCritical";
|
||||
case EcAlarmSeverityMajor: return @"EcAlarmSeverityMajor";
|
||||
case EcAlarmSeverityMinor: return @"EcAlarmSeverityMinor";
|
||||
case EcAlarmSeverityWarning: return @"EcAlarmSeverityWarning";
|
||||
case EcAlarmSeverityCleared: return @"EcAlarmSeverityCleared";
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSString*) stringFromTrend: (EcAlarmTrend)value
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case EcAlarmTrendNone: return @"EcAlarmTrendNone";
|
||||
case EcAlarmTrendUp: return @"EcAlarmTrendUp";
|
||||
case EcAlarmTrendDown: return @"EcAlarmTrendDown";
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString*) additionalText
|
||||
{
|
||||
return _additionalText;
|
||||
}
|
||||
|
||||
- (Class) classForCoder
|
||||
{
|
||||
return [EcAlarm class];
|
||||
}
|
||||
|
||||
- (EcAlarm*) clear
|
||||
{
|
||||
EcAlarm *c = [self copy];
|
||||
|
||||
c->_perceivedSeverity = EcAlarmSeverityCleared;
|
||||
c->_notificationID = _notificationID;
|
||||
return [c autorelease];
|
||||
}
|
||||
|
||||
- (NSComparisonResult) compare: (EcAlarm*)other
|
||||
{
|
||||
int oNotificationID;
|
||||
NSString *sStr;
|
||||
NSString *oStr;
|
||||
|
||||
if (NO == [other isKindOfClass: [EcAlarm class]])
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"[%@-%@] argument is not an EcAlarm",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
||||
}
|
||||
oNotificationID = [other notificationID];
|
||||
if (_notificationID > 0 && oNotificationID > 0)
|
||||
{
|
||||
if (_notificationID < oNotificationID)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
if (_notificationID > oNotificationID)
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
return NSOrderedSame;
|
||||
}
|
||||
sStr = [NSString stringWithFormat: @"%@ %d %d %@",
|
||||
[self managedObject],
|
||||
[self eventType],
|
||||
[self probableCause],
|
||||
[self specificProblem]];
|
||||
oStr = [NSString stringWithFormat: @"%@ %d %d %@",
|
||||
[other managedObject],
|
||||
[other eventType],
|
||||
[other probableCause],
|
||||
[other specificProblem]];
|
||||
return [sStr compare: oStr];
|
||||
}
|
||||
|
||||
- (id) copyWithZone: (NSZone*)aZone
|
||||
{
|
||||
EcAlarm *c = [[self class] allocWithZone: aZone];
|
||||
|
||||
c = [c initForManagedObject: _managedObject
|
||||
at: _eventDate
|
||||
withEventType: _eventType
|
||||
probableCause: _probableCause
|
||||
specificProblem: _specificProblem
|
||||
perceivedSeverity: _perceivedSeverity
|
||||
proposedRepairAction: _proposedRepairAction
|
||||
additionalText: _additionalText];
|
||||
if (nil != c)
|
||||
{
|
||||
c->_firstEventDate = [_firstEventDate copyWithZone: aZone];
|
||||
c->_notificationID = _notificationID;
|
||||
c->_trendIndicator = _trendIndicator;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
DESTROY(_managedObject);
|
||||
DESTROY(_eventDate);
|
||||
DESTROY(_firstEventDate);
|
||||
DESTROY(_specificProblem);
|
||||
DESTROY(_proposedRepairAction);
|
||||
DESTROY(_additionalText);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString*) description
|
||||
{
|
||||
Class c = [self class];
|
||||
|
||||
return [NSString stringWithFormat:
|
||||
@"Alarm %-8d %@ %@ %@ %@ %@ at %@(%@) %@ %@ %@",
|
||||
_notificationID,
|
||||
_managedObject,
|
||||
[c stringFromEventType: _eventType],
|
||||
[c stringFromProbableCause: _probableCause],
|
||||
[c stringFromSeverity: _perceivedSeverity],
|
||||
[c stringFromTrend: _trendIndicator],
|
||||
_eventDate,
|
||||
_firstEventDate,
|
||||
_specificProblem,
|
||||
_proposedRepairAction,
|
||||
_additionalText];
|
||||
}
|
||||
|
||||
- (void) encodeWithCoder: (NSCoder*)aCoder
|
||||
{
|
||||
[aCoder encodeValuesOfObjCTypes: "iiiii@@@@@@",
|
||||
&_eventType,
|
||||
&_notificationID,
|
||||
&_perceivedSeverity,
|
||||
&_probableCause,
|
||||
&_trendIndicator,
|
||||
&_managedObject,
|
||||
&_eventDate,
|
||||
&_firstEventDate,
|
||||
&_specificProblem,
|
||||
&_proposedRepairAction,
|
||||
&_additionalText];
|
||||
}
|
||||
|
||||
- (NSDate*) eventDate
|
||||
{
|
||||
return _eventDate;
|
||||
}
|
||||
|
||||
- (EcAlarmEventType) eventType
|
||||
{
|
||||
return _eventType;
|
||||
}
|
||||
|
||||
- (void *) extra
|
||||
{
|
||||
return _extra;
|
||||
}
|
||||
|
||||
- (NSDate*) firstEventDate
|
||||
{
|
||||
return _firstEventDate;
|
||||
}
|
||||
|
||||
- (void) freeze
|
||||
{
|
||||
/* NB this value must NOT be archived ... because when we restore from
|
||||
* archive we will want to store a pointer in _extra.
|
||||
*/
|
||||
_frozen = YES;
|
||||
}
|
||||
|
||||
- (NSUInteger) hash
|
||||
{
|
||||
return [_managedObject hash];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"Called -init on EcAlarm"];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id) initForManagedObject: (NSString*)managedObject
|
||||
at: (NSDate*)eventDate
|
||||
withEventType: (EcAlarmEventType)eventType
|
||||
probableCause: (EcAlarmProbableCause)probableCause
|
||||
specificProblem: (NSString*)specificProblem
|
||||
perceivedSeverity: (EcAlarmSeverity)perceivedSeverity
|
||||
proposedRepairAction: (NSString*)proposedRepairAction
|
||||
additionalText: (NSString*)additionalText
|
||||
{
|
||||
Class c = [self class];
|
||||
|
||||
if (nil == managedObject)
|
||||
{
|
||||
managedObject = EcMakeManagedObject(nil, nil, nil);
|
||||
}
|
||||
if (4 != [[managedObject componentsSeparatedByString: @"_"] count])
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"bad managed object (%@)", managedObject];
|
||||
}
|
||||
if (127 < strlen([managedObject UTF8String]))
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"managed object too long (over 127 bytes): (%@)",
|
||||
managedObject];
|
||||
}
|
||||
if (0 == [specificProblem length])
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"empty specific problem"];
|
||||
}
|
||||
if (255 < strlen([specificProblem UTF8String]))
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"specific problem too long (over 255 bytes): (%@)",
|
||||
specificProblem];
|
||||
}
|
||||
if (0 == [proposedRepairAction length])
|
||||
{
|
||||
if (EcAlarmSeverityCleared == perceivedSeverity)
|
||||
{
|
||||
/* We don't need a proposed repair action for a clear.
|
||||
*/
|
||||
proposedRepairAction = @"";
|
||||
}
|
||||
else
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"empty proposed repair action"];
|
||||
}
|
||||
}
|
||||
if (255 < strlen([proposedRepairAction UTF8String]))
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"proposed repair action too long (over 255 bytes): (%@)",
|
||||
proposedRepairAction];
|
||||
}
|
||||
if (nil == eventDate)
|
||||
{
|
||||
eventDate = [NSDate date];
|
||||
}
|
||||
if (nil == additionalText)
|
||||
{
|
||||
additionalText = @"";
|
||||
}
|
||||
if (255 < strlen([additionalText UTF8String]))
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"additional text too long (over 255 bytes): (%@)",
|
||||
additionalText];
|
||||
}
|
||||
if (nil == [c stringFromEventType: eventType])
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"bad event type (%d)", eventType];
|
||||
}
|
||||
if (nil == [c stringFromSeverity: perceivedSeverity])
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"bad severity (%d)", perceivedSeverity];
|
||||
}
|
||||
if (nil == [c stringFromProbableCause: probableCause])
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"bad severity (%d)", probableCause];
|
||||
}
|
||||
/* Anything other than an unknown probable cause must correspond to a
|
||||
* known event type, but an unknown probable cause may match any event.
|
||||
*/
|
||||
if (EcAlarmProbableCauseUnknown != probableCause)
|
||||
{
|
||||
_eventType = [c eventTypeFromProbableCause: probableCause];
|
||||
if (_eventType != eventType)
|
||||
{
|
||||
[self release];
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"missmatch of event type and probable cause"];
|
||||
}
|
||||
}
|
||||
|
||||
if (nil != (self = [super init]))
|
||||
{
|
||||
_notificationID = 0;
|
||||
_eventType = eventType;
|
||||
_perceivedSeverity = perceivedSeverity;
|
||||
_probableCause = probableCause;
|
||||
_trendIndicator = 0;
|
||||
ASSIGNCOPY(_managedObject, managedObject);
|
||||
ASSIGNCOPY(_eventDate, eventDate);
|
||||
ASSIGNCOPY(_specificProblem, specificProblem);
|
||||
ASSIGNCOPY(_proposedRepairAction, proposedRepairAction);
|
||||
ASSIGNCOPY(_additionalText, additionalText);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) initWithCoder: (NSCoder*)aCoder
|
||||
{
|
||||
[aCoder decodeValuesOfObjCTypes: "iiiii@@@@@@",
|
||||
&_eventType,
|
||||
&_notificationID,
|
||||
&_perceivedSeverity,
|
||||
&_probableCause,
|
||||
&_trendIndicator,
|
||||
&_managedObject,
|
||||
&_eventDate,
|
||||
&_firstEventDate,
|
||||
&_specificProblem,
|
||||
&_proposedRepairAction,
|
||||
&_additionalText];
|
||||
return self;
|
||||
}
|
||||
|
||||
/* Return YES if the two instances are equal according to the correlation
|
||||
* rules, NO otherwise.
|
||||
*/
|
||||
- (BOOL) isEqual: (id)other
|
||||
{
|
||||
if (other == self)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
if (NO == [other isKindOfClass: [EcAlarm class]])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
if (NO == [[other managedObject] isEqual: _managedObject])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
if (_notificationID > 0 && [other notificationID] == _notificationID)
|
||||
{
|
||||
/* We have a notificationID set ... if both have the same notification
|
||||
* ID then they are the same.
|
||||
*/
|
||||
return YES;
|
||||
}
|
||||
|
||||
/* The correlation rule normally is:
|
||||
* Same managed object
|
||||
* same event type
|
||||
* same probable cause
|
||||
* same specific problem
|
||||
*/
|
||||
if ([other eventType] == _eventType
|
||||
&& [other probableCause] == _probableCause
|
||||
&& [[other specificProblem] isEqual: _specificProblem])
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString*) managedObject
|
||||
{
|
||||
return _managedObject;
|
||||
}
|
||||
|
||||
- (NSString*) moComponent
|
||||
{
|
||||
NSArray *s = [_managedObject componentsSeparatedByString: @"_"];
|
||||
|
||||
return [s objectAtIndex: 3];
|
||||
}
|
||||
|
||||
- (NSString*) moHost
|
||||
{
|
||||
NSArray *s = [_managedObject componentsSeparatedByString: @"_"];
|
||||
|
||||
return [s objectAtIndex: 0];
|
||||
}
|
||||
|
||||
- (NSString*) moInstance
|
||||
{
|
||||
NSArray *s = [_managedObject componentsSeparatedByString: @"_"];
|
||||
|
||||
return [s objectAtIndex: 2];
|
||||
}
|
||||
|
||||
- (NSString*) moProcess
|
||||
{
|
||||
NSArray *s = [_managedObject componentsSeparatedByString: @"_"];
|
||||
|
||||
return [s objectAtIndex: 1];
|
||||
}
|
||||
|
||||
- (int) notificationID
|
||||
{
|
||||
return _notificationID;
|
||||
}
|
||||
|
||||
- (EcAlarmSeverity) perceivedSeverity
|
||||
{
|
||||
return _perceivedSeverity;
|
||||
}
|
||||
|
||||
- (NSString*) proposedRepairAction
|
||||
{
|
||||
return _proposedRepairAction;
|
||||
}
|
||||
|
||||
- (EcAlarmProbableCause) probableCause
|
||||
{
|
||||
return _probableCause;
|
||||
}
|
||||
|
||||
- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) setExtra: (void*)extra
|
||||
{
|
||||
if (YES == _frozen)
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"[%@-%@] called for frozen instance",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
||||
}
|
||||
_extra = extra;
|
||||
}
|
||||
|
||||
- (void) setFirstEventDate: (NSDate*)firstEventDate
|
||||
{
|
||||
if (nil != firstEventDate
|
||||
&& NO == [firstEventDate isKindOfClass: [NSDate class]])
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"[%@-%@] bad argument '%@'",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
|
||||
firstEventDate];
|
||||
}
|
||||
if (YES == _frozen)
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"[%@-%@] called for frozen instance",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
||||
}
|
||||
ASSIGNCOPY(_firstEventDate, firstEventDate);
|
||||
}
|
||||
|
||||
- (void) setNotificationID: (int)notificationID
|
||||
{
|
||||
if (YES == _frozen)
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"[%@-%@] called for frozen instance",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
||||
}
|
||||
_notificationID = notificationID;
|
||||
}
|
||||
|
||||
- (void) setTrendIndicator: (EcAlarmTrend)trendIndicator
|
||||
{
|
||||
if (nil == [[self class] stringFromTrend: trendIndicator])
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"[%@-%@] bad argument '%d'",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
|
||||
trendIndicator];
|
||||
}
|
||||
if (YES == _frozen)
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"[%@-%@] called for frozen instance",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
||||
}
|
||||
_trendIndicator = trendIndicator;
|
||||
}
|
||||
|
||||
- (NSString*) specificProblem
|
||||
{
|
||||
return _specificProblem;
|
||||
}
|
||||
|
||||
- (EcAlarmTrend) trendIndicator
|
||||
{
|
||||
return _trendIndicator;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation EcAlarm (Convenience)
|
||||
|
||||
+ (EcAlarm*) clear: (NSString*)componentName
|
||||
cause: (EcAlarmProbableCause)probableCause
|
||||
problem: (NSString*)specificProblem
|
||||
{
|
||||
NSString *managedObject;
|
||||
EcAlarmEventType eventType;
|
||||
|
||||
managedObject = EcMakeManagedObject(nil, nil, componentName);
|
||||
eventType = [self eventTypeFromProbableCause: probableCause];
|
||||
return [self alarmForManagedObject: managedObject
|
||||
at: nil
|
||||
withEventType: eventType
|
||||
probableCause: probableCause
|
||||
specificProblem: specificProblem
|
||||
perceivedSeverity: EcAlarmSeverityCleared
|
||||
proposedRepairAction: nil
|
||||
additionalText: nil];
|
||||
}
|
||||
|
||||
+ (EcAlarm*) raise: (NSString*)componentName
|
||||
cause: (EcAlarmProbableCause)probableCause
|
||||
problem: (NSString*)specificProblem
|
||||
severity: (EcAlarmSeverity)perceivedSeverity
|
||||
action: (NSString*)proposedRepairAction,...
|
||||
{
|
||||
NSString *managedObject;
|
||||
EcAlarmEventType eventType;
|
||||
NSString *mesg;
|
||||
va_list ap;
|
||||
|
||||
NSAssert(EcAlarmSeverityCleared != perceivedSeverity,
|
||||
NSInvalidArgumentException);
|
||||
|
||||
managedObject = EcMakeManagedObject(nil, nil, componentName);
|
||||
eventType = [self eventTypeFromProbableCause: probableCause];
|
||||
|
||||
va_start (ap, proposedRepairAction);
|
||||
mesg = [NSString stringWithFormat: proposedRepairAction arguments: ap];
|
||||
va_end (ap);
|
||||
return [self alarmForManagedObject: managedObject
|
||||
at: nil
|
||||
withEventType: eventType
|
||||
probableCause: probableCause
|
||||
specificProblem: specificProblem
|
||||
perceivedSeverity: perceivedSeverity
|
||||
proposedRepairAction: mesg
|
||||
additionalText: nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
179
EcAlarmDestination.h
Normal file
179
EcAlarmDestination.h
Normal file
|
@ -0,0 +1,179 @@
|
|||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class EcAlarm;
|
||||
@class NSRecursiveLock;
|
||||
@class NSMutableArray;
|
||||
@class NSMutableSet;
|
||||
@class NSString;
|
||||
@class NSTimer;
|
||||
|
||||
/** The EcAlarmDestination protocol describes the interface which must be
|
||||
* provided by an object which handles alarms.<br />
|
||||
* <p>The sender expects to be able to 'fire and forget', sending the
|
||||
* messages in this protocol without having to wait for a response or deal
|
||||
* with any error conditions, so the destination must <em>not</em> block
|
||||
* for a long time or raise an exception.
|
||||
* </p>
|
||||
*/
|
||||
@protocol EcAlarmDestination
|
||||
|
||||
/** Passes an alarm to the destination.
|
||||
*/
|
||||
- (oneway void) alarm: (in bycopy EcAlarm*)event;
|
||||
|
||||
/** Inform the destination of the existence of a managed object.<br />
|
||||
* This is an indicator of a 'cold start' of that object ... meaning that the
|
||||
* object has just started up afresh, and all outstanding alarms for the object
|
||||
* are to be cleared.
|
||||
*/
|
||||
- (oneway void) domanage: (in bycopy NSString*)managedObject;
|
||||
|
||||
/** Inform the destination of the removal of a managed object.<br />
|
||||
* This is an indicator of a graceful shutdown of that object ... meaning that
|
||||
* the object has been stopped intentionally and all outstanding alarms for the
|
||||
* object are to be cleared.
|
||||
*/
|
||||
- (oneway void) unmanage: (in bycopy NSString*)managedObject;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>The EcAlarmDestination class provides an object to act as an alarm
|
||||
* destination which is capable of buffering, coalescing, and forwarding
|
||||
* alarms to another destination (usually in a separate process).
|
||||
* </p>
|
||||
* <p>The buffering and coalescing mechanism is important to prevent floods
|
||||
* of alarms being sent over network connections.
|
||||
* </p>
|
||||
* <p>An EcAlarmDestination instance can also be set to forward alarm
|
||||
* information to a number of other instances as backups for the main
|
||||
* destination.
|
||||
* </p>
|
||||
*/
|
||||
@interface EcAlarmDestination : NSObject <EcAlarmDestination>
|
||||
{
|
||||
NSRecursiveLock *_alarmLock;
|
||||
NSMutableArray *_alarmQueue;
|
||||
NSMutableSet *_alarmsActive;
|
||||
NSMutableSet *_managedObjects;
|
||||
NSTimer *_timer;
|
||||
BOOL _isRunning;
|
||||
BOOL _shouldStop;
|
||||
BOOL _coalesceOff;
|
||||
BOOL _inTimeout;
|
||||
NSString *_host;
|
||||
NSString *_name;
|
||||
id<EcAlarmDestination> _destination;
|
||||
NSArray *_backups;
|
||||
}
|
||||
|
||||
/** Passes an alarm to the destination by adding it to a queue of alarm
|
||||
* events which will be processed in the receivers running thread.
|
||||
*/
|
||||
- (oneway void) alarm: (in bycopy EcAlarm*)event;
|
||||
|
||||
/** Returns an array containing all the currently active alarms.
|
||||
*/
|
||||
- (NSArray*) alarms;
|
||||
|
||||
/** Returns an array of backup destinations (if set).<br />
|
||||
* See -setBackups: for more information.
|
||||
*/
|
||||
- (NSArray*) backups;
|
||||
|
||||
/** Inform the destination of the existence of a managed object.<br />
|
||||
* This is an indicator of a 'cold start' of that object ... meaning that the
|
||||
* object has just started up afresh, and all outstanding alarms for the object
|
||||
* are to be cleared.<br />
|
||||
* The managedObject information is added to a queue which is processed by
|
||||
* the receiver's running thread in order to pass the information on to the
|
||||
* destination.
|
||||
*/
|
||||
- (oneway void) domanage: (in bycopy NSString*)managedObject;
|
||||
|
||||
/* <init />
|
||||
* Initialises the receiver and starts up a secondary thread to manage
|
||||
* alarms for it.
|
||||
*/
|
||||
- (id) init;
|
||||
|
||||
/** Sets the name/host of the object in a remote process to which
|
||||
* alarms should be forwarded. If this information is set then the
|
||||
* forwarder will attempt to maintain a Distributed Objects connection
|
||||
* to the remote object.<br />
|
||||
* The host may be nil for a local connection (current machine and account),
|
||||
* or an empty string for a network connection to the local machine, or a
|
||||
* host name for a network connection to another machine, or an asterisk
|
||||
* for a network connection to any available machine.
|
||||
*/
|
||||
- (id) initWithHost: (NSString*)host name: (NSString*)name;
|
||||
|
||||
/** Returns a flag indicating whether the receiver is actually operating.
|
||||
*/
|
||||
- (BOOL) isRunning;
|
||||
|
||||
/** This method is called from -init in a secondary thread to start handling
|
||||
* of alarms by the receiver. Do not call it yourself.
|
||||
*/
|
||||
- (void) run;
|
||||
|
||||
/** Sets an array containing EcAlarmDestination objects as backups to receive
|
||||
* copies of the alarm and domanage/unmanage information sent to this
|
||||
* destination.<br />
|
||||
* You may set nil or an empty array to turn off backups, and may use the
|
||||
* -backups method to get the currently set values.<br />
|
||||
* Do not set up loops causing a destination to be its own backup either
|
||||
* directly or indirectly, as this will cause alarms to be forwarded endlessly.
|
||||
*/
|
||||
- (void) setBackups: (NSArray*)backups;
|
||||
|
||||
/** Sets coalescing behavior for the queue of alarms and managed object
|
||||
* changes. The default behavior is for coalescing to be turned on
|
||||
* (so new values replace those in the queue), but setting this to NO
|
||||
* will cause all events to be passed on (apart from repeated alarms at
|
||||
* the same perceivedSeverity level, which are never passed one).
|
||||
*/
|
||||
- (BOOL) setCoalesce: (BOOL)coalesce;
|
||||
|
||||
/** Sets the destination to which alarms should be forwarded.<br />
|
||||
* If nil this turns off forwarding until it is re-set to a non-nil
|
||||
* destination.<br />
|
||||
* The destination object is retained by the receiver.<br />
|
||||
* Returns the previously set destination.
|
||||
*/
|
||||
- (id<EcAlarmDestination>) setDestination: (id<EcAlarmDestination>)destination;
|
||||
|
||||
/** Requests that the receiver's running thread should shut down. This method
|
||||
* waits for a short while for the thread to shut down, but the process of
|
||||
* shutting down is not guaranteed to have completed by the time the method
|
||||
* returns.
|
||||
*/
|
||||
- (void) shutdown;
|
||||
|
||||
/** Inform the destination of the removal of a managed object.<br />
|
||||
* This is an indicator of a graceful shutdown of that object ... meaning that
|
||||
* the object has been stopped intentionally and all outstanding alarms for the
|
||||
* object are to be cleared.<br />
|
||||
* The managedObject information is added to a queue which is processed by
|
||||
* the receiver's running thread in order to pass the information on to the
|
||||
* destination.
|
||||
*/
|
||||
- (oneway void) unmanage: (in bycopy NSString*)managedObject;
|
||||
|
||||
@end
|
||||
|
||||
/** Methods called internally to forward events to the remote target of
|
||||
* the receiver. These are provided for subclasses to oveerride.
|
||||
*/
|
||||
@interface EcAlarmDestination (Forwarding)
|
||||
/** Forward an alarm event. */
|
||||
- (void) alarmFwd: (EcAlarm*)event;
|
||||
/** Forward a domanage event. */
|
||||
- (void) domanageFwd: (NSString*)managedObject;
|
||||
/** Forward an unmanage event. */
|
||||
- (void) unmanageFwd: (NSString*)managedObject;
|
||||
@end
|
||||
|
531
EcAlarmDestination.m
Normal file
531
EcAlarmDestination.m
Normal file
|
@ -0,0 +1,531 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "EcProcess.h"
|
||||
#import "EcAlarm.h"
|
||||
#import "EcAlarmDestination.h"
|
||||
|
||||
|
||||
@interface EcAlarmDestination (Private)
|
||||
|
||||
/* Loss of connection ... clear destination.
|
||||
*/
|
||||
- (void) _connectionBecameInvalid: (id)connection;
|
||||
|
||||
/* Regular timer to handle alarms.
|
||||
*/
|
||||
- (void) _timeout: (NSTimer*)t;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EcAlarmDestination
|
||||
|
||||
- (oneway void) alarm: (in bycopy EcAlarm*)event
|
||||
{
|
||||
if (NO == [event isKindOfClass: [EcAlarm class]])
|
||||
{
|
||||
NSLog(@"[%@-%@] invalid argument (%@)",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
|
||||
event);
|
||||
}
|
||||
else
|
||||
{
|
||||
[_alarmLock lock];
|
||||
if (YES == _coalesceOff)
|
||||
{
|
||||
[_alarmQueue addObject: event];
|
||||
}
|
||||
else
|
||||
{
|
||||
[event retain];
|
||||
[_alarmQueue removeObject: event];
|
||||
[_alarmQueue addObject: event];
|
||||
[event release];
|
||||
}
|
||||
[_alarmLock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray*) alarms
|
||||
{
|
||||
NSArray *a;
|
||||
|
||||
[_alarmLock lock];
|
||||
a = [_alarmsActive allObjects];
|
||||
[_alarmLock unlock];
|
||||
return a;
|
||||
}
|
||||
|
||||
- (NSArray*) backups
|
||||
{
|
||||
NSArray *a;
|
||||
|
||||
[_alarmLock lock];
|
||||
a = [_backups retain];
|
||||
[_alarmLock unlock];
|
||||
return [a autorelease];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[self shutdown];
|
||||
[_backups release];
|
||||
[(id)_destination release];
|
||||
_destination = nil;
|
||||
[_alarmQueue release];
|
||||
_alarmQueue = nil;
|
||||
[_alarmsActive release];
|
||||
_alarmsActive = nil;
|
||||
[_managedObjects release];
|
||||
_managedObjects = nil;
|
||||
[_alarmLock release];
|
||||
_alarmLock = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if (nil != (self = [super init]))
|
||||
{
|
||||
NSDate *begin;
|
||||
|
||||
_alarmLock = [NSRecursiveLock new];
|
||||
_alarmQueue = [NSMutableArray new];
|
||||
_alarmsActive = [NSMutableSet new];
|
||||
_managedObjects = [NSMutableSet new];
|
||||
|
||||
[NSThread detachNewThreadSelector: @selector(run)
|
||||
toTarget: self
|
||||
withObject: nil];
|
||||
begin = [NSDate date];
|
||||
while (NO == [self isRunning])
|
||||
{
|
||||
if ([begin timeIntervalSinceNow] < -5.0)
|
||||
{
|
||||
NSLog(@"alarm thread failed to start within 5 seconds");
|
||||
[_alarmLock lock];
|
||||
_shouldStop = YES; // If the thread starts ... shutdown
|
||||
[_alarmLock unlock];
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
[NSThread sleepForTimeInterval: 0.1];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (oneway void) domanage: (in bycopy NSString*)managedObject
|
||||
{
|
||||
if (NO == [managedObject isKindOfClass: [NSString class]]
|
||||
|| 4 != [[managedObject componentsSeparatedByString: @"_"] count])
|
||||
{
|
||||
NSLog(@"[%@-%@] invalid argument (%@)",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
|
||||
managedObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *event;
|
||||
|
||||
event = [NSString stringWithFormat: @"domanage %@", managedObject];
|
||||
[_alarmLock lock];
|
||||
if (YES == _coalesceOff)
|
||||
{
|
||||
[_alarmQueue addObject: event];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_alarmQueue removeObject: event];
|
||||
[_alarmQueue addObject: event];
|
||||
}
|
||||
[_alarmLock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (id) initWithHost: (NSString*)host name: (NSString*)name
|
||||
{
|
||||
/* We set the host namd name before calling -init, so that subclasses
|
||||
* which override -init may make use of the values we have set.
|
||||
*/
|
||||
_host = [host copy];
|
||||
_name = [name copy];
|
||||
return [self init];
|
||||
}
|
||||
|
||||
- (BOOL) isRunning
|
||||
{
|
||||
BOOL result;
|
||||
|
||||
[_alarmLock lock];
|
||||
result = _isRunning;
|
||||
[_alarmLock unlock];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void) run
|
||||
{
|
||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||
NSRunLoop *loop = [NSRunLoop currentRunLoop];
|
||||
NSDate *future = [NSDate distantFuture];
|
||||
|
||||
_isRunning = YES;
|
||||
_timer = [NSTimer scheduledTimerWithTimeInterval: 1.0
|
||||
target: self
|
||||
selector: @selector(_timeout:)
|
||||
userInfo: nil
|
||||
repeats: YES];
|
||||
|
||||
while (NO == _shouldStop)
|
||||
{
|
||||
[loop runMode: NSDefaultRunLoopMode beforeDate: future];
|
||||
}
|
||||
[pool release];
|
||||
|
||||
_isRunning = NO;
|
||||
}
|
||||
|
||||
- (void) setBackups: (NSArray*)backups
|
||||
{
|
||||
NSUInteger i;
|
||||
|
||||
if (nil != backups && NO == [backups isKindOfClass: [NSArray class]])
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"[%@-%@] argument is not nil or an array",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
||||
}
|
||||
i = [backups count];
|
||||
if (0 == i)
|
||||
{
|
||||
backups = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (i-- > 0)
|
||||
{
|
||||
if (NO == [[backups objectAtIndex: i]
|
||||
isKindOfClass: [EcAlarmDestination class]])
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"[%@-%@] array contains bad destination",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
||||
}
|
||||
}
|
||||
}
|
||||
[_alarmLock lock];
|
||||
ASSIGNCOPY(_backups, backups);
|
||||
[_alarmLock unlock];
|
||||
}
|
||||
|
||||
- (BOOL) setCoalesce: (BOOL)coalesce
|
||||
{
|
||||
BOOL old;
|
||||
|
||||
[_alarmLock lock];
|
||||
old = (NO == _coalesceOff) ? YES : NO;
|
||||
_coalesceOff = (NO == coalesce) ? YES : NO;
|
||||
[_alarmLock unlock];
|
||||
return old;
|
||||
}
|
||||
|
||||
- (id<EcAlarmDestination>) setDestination: (id<EcAlarmDestination>)destination
|
||||
{
|
||||
id old;
|
||||
|
||||
if (nil != (id)destination && NO == [(id)destination
|
||||
conformsToProtocol: @protocol(EcAlarmDestination)])
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"[%@-%@] arg does not conform to EcAlarmDestination protocol",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
||||
}
|
||||
[_alarmLock lock];
|
||||
old = (id)_destination;
|
||||
_destination = (id<EcAlarmDestination>)[(id)destination retain];
|
||||
[_alarmLock unlock];
|
||||
return (id<EcAlarmDestination>)[old autorelease];
|
||||
}
|
||||
|
||||
- (void) shutdown
|
||||
{
|
||||
NSDate *begin;
|
||||
|
||||
[_alarmLock lock];
|
||||
_shouldStop = YES;
|
||||
[_host release];
|
||||
_host = nil;
|
||||
[_name release];
|
||||
_name = nil;
|
||||
[_alarmLock unlock];
|
||||
begin = [NSDate date];
|
||||
while (YES == [self isRunning])
|
||||
{
|
||||
if ([begin timeIntervalSinceNow] < -5.0)
|
||||
{
|
||||
NSLog(@"alarm thread failed to stop within 5 seconds");
|
||||
return;
|
||||
}
|
||||
[NSThread sleepForTimeInterval: 0.1];
|
||||
}
|
||||
}
|
||||
|
||||
- (oneway void) unmanage: (in bycopy NSString*)managedObject
|
||||
{
|
||||
if (NO == [managedObject isKindOfClass: [NSString class]]
|
||||
|| 4 != [[managedObject componentsSeparatedByString: @"_"] count])
|
||||
{
|
||||
NSLog(@"[%@-%@] invalid argument (%@)",
|
||||
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
|
||||
managedObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *event;
|
||||
|
||||
event = [NSString stringWithFormat: @"unmanage %@", managedObject];
|
||||
[_alarmLock lock];
|
||||
if (YES == _coalesceOff)
|
||||
{
|
||||
[_alarmQueue addObject: event];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_alarmQueue removeObject: event];
|
||||
[_alarmQueue addObject: event];
|
||||
}
|
||||
[_alarmLock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation EcAlarmDestination (Private)
|
||||
|
||||
- (void) _connectionBecameInvalid: (id)connection
|
||||
{
|
||||
[self setDestination: nil];
|
||||
}
|
||||
|
||||
- (void) _timeout: (NSTimer*)t
|
||||
{
|
||||
[_alarmLock lock];
|
||||
if (NO == _inTimeout && YES == _isRunning && NO == _shouldStop)
|
||||
{
|
||||
_inTimeout = YES;
|
||||
NS_DURING
|
||||
{
|
||||
if ([_alarmQueue count] > 0)
|
||||
{
|
||||
if (nil == (id)_destination)
|
||||
{
|
||||
if (nil != _name)
|
||||
{
|
||||
id proxy;
|
||||
|
||||
if (nil == _host)
|
||||
{
|
||||
proxy = [NSConnection
|
||||
rootProxyForConnectionWithRegisteredName: _name
|
||||
host: _host
|
||||
usingNameServer:
|
||||
[NSMessagePortNameServer sharedInstance]];
|
||||
}
|
||||
else
|
||||
{
|
||||
proxy = [NSConnection
|
||||
rootProxyForConnectionWithRegisteredName: _name
|
||||
host: _host
|
||||
usingNameServer:
|
||||
[NSSocketPortNameServer sharedInstance]];
|
||||
}
|
||||
|
||||
if (proxy != nil)
|
||||
{
|
||||
id connection = [proxy connectionForProxy];
|
||||
|
||||
[connection setDelegate: self];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver: self
|
||||
selector: @selector(_connectionBecameInvalid:)
|
||||
name: NSConnectionDidDieNotification
|
||||
object: connection];
|
||||
[self setDestination: (id<EcAlarmDestination>)proxy];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Do stuff here
|
||||
|
||||
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)
|
||||
{
|
||||
/* send the clear for the entry and remove it
|
||||
*/
|
||||
[_alarmsActive removeObject: prev];
|
||||
[self alarmFwd: next];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the managed object is not registered,
|
||||
* register before sending an alarm for it.
|
||||
*/
|
||||
if (nil == [_managedObjects member: m])
|
||||
{
|
||||
[_managedObjects addObject: m];
|
||||
[self domanageFwd: m];
|
||||
}
|
||||
|
||||
/* If the alarm is new or of changed severity,
|
||||
* update the records and pass it on.
|
||||
*/
|
||||
if (nil == prev || [next perceivedSeverity]
|
||||
!= [prev perceivedSeverity])
|
||||
{
|
||||
[_alarmsActive addObject: next];
|
||||
[self alarmFwd: next];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *s = [o description];
|
||||
|
||||
if (YES == [s hasPrefix: @"domanage "])
|
||||
{
|
||||
NSString *m = [s substringFromIndex: 9];
|
||||
|
||||
if (nil == [_managedObjects member: m])
|
||||
{
|
||||
[_managedObjects addObject: m];
|
||||
[self domanageFwd: m];
|
||||
}
|
||||
}
|
||||
else if (YES == [s hasPrefix: @"unmanage "])
|
||||
{
|
||||
NSString *m = [s substringFromIndex: 9];
|
||||
|
||||
if (nil != [_managedObjects member: m])
|
||||
{
|
||||
[_managedObjects removeObject: m];
|
||||
[self unmanageFwd: m];
|
||||
}
|
||||
|
||||
/* When we unmanage an object, we also
|
||||
* implicitly unmanage objects which
|
||||
* are components of that object.
|
||||
*/
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"ERROR ... unexpected command '%@'", s);
|
||||
}
|
||||
}
|
||||
[_alarmQueue removeObjectAtIndex: 0];
|
||||
}
|
||||
}
|
||||
_inTimeout = NO;
|
||||
[_alarmLock unlock];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
_inTimeout = NO;
|
||||
[_alarmLock unlock];
|
||||
NSLog(@"%@ %@", NSStringFromClass([self class]), localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation EcAlarmDestination (Forwarding)
|
||||
|
||||
- (void) alarmFwd: (EcAlarm*)event
|
||||
{
|
||||
NS_DURING
|
||||
[_destination alarm: event];
|
||||
NS_DURING
|
||||
[_backups makeObjectsPerformSelector: @selector(alarm:)
|
||||
withObject: event];
|
||||
NS_HANDLER
|
||||
DESTROY(_backups);
|
||||
NSLog(@"Problem sending alarm to backups ... %@", localException);
|
||||
NS_ENDHANDLER
|
||||
NS_HANDLER
|
||||
[self setDestination: nil];
|
||||
NSLog(@"Problem sending alarm to destination ... %@", localException);
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
- (void) domanageFwd: (NSString*)managedObject
|
||||
{
|
||||
NS_DURING
|
||||
[_destination domanage: managedObject];
|
||||
NS_DURING
|
||||
[_backups makeObjectsPerformSelector: @selector(domanage:)
|
||||
withObject: managedObject];
|
||||
NS_HANDLER
|
||||
DESTROY(_backups);
|
||||
NSLog(@"Problem with domanage to backups ... %@", localException);
|
||||
NS_ENDHANDLER
|
||||
NS_HANDLER
|
||||
[self setDestination: nil];
|
||||
NSLog(@"Problem with domanage to destination ... %@", localException);
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
- (void) unmanageFwd: (NSString*)managedObject
|
||||
{
|
||||
NS_DURING
|
||||
[_destination unmanage: managedObject];
|
||||
NS_DURING
|
||||
[_backups makeObjectsPerformSelector: @selector(unmanage:)
|
||||
withObject: managedObject];
|
||||
NS_HANDLER
|
||||
DESTROY(_backups);
|
||||
NSLog(@"Problem with unmanage to backups ... %@", localException);
|
||||
NS_ENDHANDLER
|
||||
NS_HANDLER
|
||||
[self setDestination: nil];
|
||||
NSLog(@"Problem with unmanage to destination ... %@", localException);
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
@end
|
||||
|
174
EcAlarmSinkSNMP.h
Normal file
174
EcAlarmSinkSNMP.h
Normal file
|
@ -0,0 +1,174 @@
|
|||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
#import "EcAlarmDestination.h"
|
||||
|
||||
/** <p>The EcAlarmSinkSNMP class implements an alarm destination which
|
||||
* terminates a chain of destinations by delivering alarms to an SNMP
|
||||
* monitoring system rather than forwarding them to another EcAlarmDestination
|
||||
* object.
|
||||
* </p>
|
||||
* <p>The SNMP functionality of the EcAlarmSinkSNMP is essentially to maintain
|
||||
* a table of active alarms and to send out traps indicating changes of
|
||||
* state of that table as follows:
|
||||
* </p>
|
||||
* <p>Alarms in the table are each identified by a unique numeric identifier,
|
||||
* the 'notificationID' and traps concerning the alarms carry that ID.<br />
|
||||
* For each alarm trap sent out, there is a corresponding trap sent when
|
||||
* the alarm is cleared and removed from the table. The clear trap carries
|
||||
* the same notification identifier as that of the alarm being cleared, and
|
||||
* the pair (alarm/clear) is known as a correlation.<br />
|
||||
* A clear trap is never sent without a corresponding alarm trap having first
|
||||
* been sent.<br />
|
||||
* Each trap carries a sequence number so that the SNMP manager is able to
|
||||
* detect lost traps.
|
||||
* </p>
|
||||
* <p>For purposes of establishing a correlation between any alarms coming
|
||||
* in to the system, the managed object, event type, specific problem,
|
||||
* and probable cause values must be the same. When this occurs, any
|
||||
* matching alarms are given the same notification identifier as the first
|
||||
* alarm.<br />
|
||||
* When an alarm is added to the table, only one trap will to notify about it,
|
||||
* and no further traps will be sent for any correlated alarms arriving
|
||||
* unless those alarms have new perceived severity values.<br />
|
||||
* If an alarm changes its severity, a trap will be sent to clear the
|
||||
* alarm (removing it from the table) and the alarm with new severity will
|
||||
* be added to the table and its trap sent.
|
||||
* </p>
|
||||
* <p>To allow an SNMP manager to resynchronize with the agent, the manager
|
||||
* may examine the table of active alarms. This may be needed where there
|
||||
* is an agent error, manager error, or loss of connectivity/traps (jumps
|
||||
* in the trap sequence number).<br />
|
||||
* The entries in the table contain all the same information as the alarm
|
||||
* traps, apart from the trap sequence number.
|
||||
* </p>
|
||||
* <p>After a crash/shutdown and subsequent start-up of the agent, the
|
||||
* active alarm table is updated with the current situation of the system
|
||||
* before the manager is able to respond to the coldstart (see RFC 1157).
|
||||
* When the manager receives the agent's coldstart it may being a
|
||||
* resynchronization process gathering alarms related to the current
|
||||
* situation of the system by examining the alarm table.<br />
|
||||
* There is a resync flag variable which indicates whether a resynchronization
|
||||
* process is being carried out or not (set to 1 if resync is in progress,
|
||||
* 0 otherwise). Before sending a trap, the variable is checked to know
|
||||
* whether the resynchronization process is active or not. The traps will
|
||||
* only be sent to the manager if there is no resync in progress, otherwise
|
||||
* they will be deferred until the resync has completed.<br />
|
||||
* The manager may therefore set the resync flag to 1, read the contents of
|
||||
* the active alarms table, and then set the resync flag to zero to resume
|
||||
* active operation.<br />
|
||||
* As a safety measure to protect against manager failure, the resync flag
|
||||
* is automatically reset to zero if it is left set to 1 for more than five
|
||||
* minutes.
|
||||
* </p>
|
||||
* <p>In addition to the traps and the alarms table, a table of managed
|
||||
* objects is also maintained. The system guarantees that any managed object
|
||||
* referred to in a trap is present in the table before the trap is sent.
|
||||
* </p>
|
||||
* <p>In order for the manager to know that the agent is running, a heartbeat
|
||||
* trap is sent periodically. The heartbeat contains a notification
|
||||
* identifier of zero (never used other than for a heartbeat).<br />
|
||||
* A poll heartbeat interval variable is provided to allow the manager to
|
||||
* control the time between heartbeats (in minutes) and may be set to a
|
||||
* positive integer value, or queried.
|
||||
* </p>
|
||||
* <p>The class works by connecting to an SNMP agent using the Agent-X
|
||||
* protocol, and registering as the 'owner' of various OIDs in an SNMP MIB
|
||||
* so that SNMP requests made to the agent for those OIDs are forwarded to
|
||||
* the EcAlarmSinkSNMP, and so that the EcAlarmSinkSNMP can send SNMP 'traps'
|
||||
* via the agent.<br />
|
||||
* To do this, the agent must be configured to accept incoming Agent-X
|
||||
* connections from the host on which the EcAlarmSinkSNMP is running, and the
|
||||
* alarm sink object must be initialized using the -initWithHost:name: method
|
||||
* to specify the host and port on which the agent is listening.<br />
|
||||
* If the EcAlarmSinkSNMP object is initialized without a specific host and name
|
||||
* then it assumes that the agent is running on localhost and the standard
|
||||
* Agent-X tcp/ip port (705).
|
||||
* </p>
|
||||
* <p>To configure a net-snmp agent to work with this software you need to:
|
||||
* </p>
|
||||
* Edit /etc/snmp/snmpd.conf to get it to send traps to snmptrapd ...
|
||||
* <example>
|
||||
* rwcommunity public
|
||||
* trap2sink localhost public
|
||||
* </example>
|
||||
* and to accept agentx connections via tcp ...
|
||||
* <example>
|
||||
* agentxsocket tcp:localhost:705
|
||||
* master agentx
|
||||
* </example>
|
||||
* Then restart with '/etc/rc.d/init.d/snmpd restart'
|
||||
*
|
||||
* <p>All alarming is done a based on three OIDs which may be defined
|
||||
* within the user defaults system. The EcAlarmSinkSNMP is responsible for
|
||||
* managing those OIDs (and anything below them in the OID hierarchy).
|
||||
* </p>
|
||||
* <deflist>
|
||||
* <term>AlarmsOID</term>
|
||||
* <desc>All SNMP alarm data is in a fixed structure relative to this
|
||||
* OID in the MIB.<br />
|
||||
* The table of current alarms is at the OID 1 relative to AlarmsOID,
|
||||
* so you can interrogate the table using;
|
||||
* <example>snmpwalk -v 1 -c public localhost {AlarmsOID}.1
|
||||
* </example>
|
||||
* The resync flag is at OID 2 relative to AlarmsOID and can be queried
|
||||
* or set using:
|
||||
* <example>snmpget -v 1 -c public localhost {AlarmsOID}.2.0
|
||||
* </example>
|
||||
* <example>snmpset -v 1 -c public localhost {AlarmsOID}.2.0 i 1
|
||||
* </example>
|
||||
* The current trap sequence number is at OID 3 relative to AlarmsOID
|
||||
* and can be queried using:
|
||||
* <example>snmpget -v 1 -c public localhost {AlarmsOID}.3.0
|
||||
* </example>
|
||||
* The heartbeat poll interval is at OID 4 relative to AlarmsOID
|
||||
* and can be queried or set using:
|
||||
* <example>snmpget -v 1 -c public localhost {AlarmsOID}.4.0
|
||||
* </example>
|
||||
* <example>snmpset -v 1 -c public localhost {AlarmsOID}.4.0 i 5
|
||||
* </example>
|
||||
* </desc>
|
||||
* <term>ObjectsOID</term>
|
||||
* <desc>All SNMP managed object values are in a table relative to this
|
||||
* OID in the MIB.<br />
|
||||
* SNMP tools are able to interrogate the table at this OID to see which
|
||||
* managed objects are currently registered:
|
||||
* <example>snmpwalk -v 1 -c public localhost {ObjectsOID}
|
||||
* </example>
|
||||
* </desc>
|
||||
* <term>TrapOID</term>
|
||||
* <desc>The definition of the trap which carries alarms to any monitoring
|
||||
* system is at this OID.
|
||||
* </desc>
|
||||
* </deflist>
|
||||
* <p>Each of these OID strings must be a representation of an OID in the
|
||||
* integer dotted format (eg. 1.3.6.1.4.1.37374.3.0.1).
|
||||
* </p>
|
||||
*/
|
||||
@interface EcAlarmSinkSNMP : EcAlarmDestination
|
||||
|
||||
/** <p>Returns the singleton alarm sink instance.<br />
|
||||
* This instance is the link between the Objective-C world and the net-snmp
|
||||
* world which runs in a separate thread. You should stop the SNMP system
|
||||
* by calling -shutdown before process termination.
|
||||
* </p>
|
||||
* <p>If the agent is on another host and/or is using a non-standard port,
|
||||
* you need to call -initWithHost:name: before and/or instead of calling
|
||||
* this method.
|
||||
* </p>
|
||||
*/
|
||||
+ (EcAlarmSinkSNMP*) alarmSinkSNMP;
|
||||
|
||||
/** <p>Overrides the default behavior to specify a host and name for the
|
||||
* SNMP agent this instance is to connect to.<br />
|
||||
* The host must be the machine the agent is running on and the name must
|
||||
* be the tcp/ip port on which that agent is listening for Agent-X connections.
|
||||
* </p>
|
||||
* <p>If an instance has already been created/initialized, this method returns
|
||||
* the existing instance and its arguments are ignored.
|
||||
* </p>
|
||||
*/
|
||||
- (id) initWithHost: (NSString*)host name: (NSString*)name;
|
||||
|
||||
@end
|
||||
|
1604
EcAlarmSinkSNMP.m
Normal file
1604
EcAlarmSinkSNMP.m
Normal file
File diff suppressed because it is too large
Load diff
32
EcAlerter.h
Normal file
32
EcAlerter.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class GSMimeSMTPClient;
|
||||
@class NSArray;
|
||||
@class NSMutableArray;
|
||||
@class NSMutableDictionary;
|
||||
@class NSString;
|
||||
@class NSTimer;
|
||||
|
||||
|
||||
@interface EcAlerter : NSObject
|
||||
{
|
||||
NSArray *rules;
|
||||
NSMutableDictionary *email;
|
||||
NSMutableDictionary *sms;
|
||||
NSTimer *timer;
|
||||
NSString *eFrom;
|
||||
NSString *eHost;
|
||||
NSString *ePort;
|
||||
GSMimeSMTPClient *smtp;
|
||||
}
|
||||
- (BOOL) configure: (NSNotification*)n;
|
||||
- (void) handleInfo: (NSString*)str;
|
||||
- (void) flushEmail;
|
||||
- (void) flushSms;
|
||||
- (void) log: (NSMutableDictionary*)m to: (NSArray*)destinations;
|
||||
- (void) mail: (NSMutableDictionary*)m to: (NSArray*)destinations;
|
||||
- (void) sms: (NSMutableDictionary*)m to: (NSArray*)destinations;
|
||||
- (void) timeout: (NSTimer*)t;
|
||||
@end
|
||||
|
923
EcAlerter.m
Normal file
923
EcAlerter.m
Normal file
|
@ -0,0 +1,923 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <GNUstepBase/GSMime.h>
|
||||
|
||||
#import "EcProcess.h"
|
||||
#import "EcAlerter.h"
|
||||
#import "NSFileHandle+Printf.h"
|
||||
|
||||
#include <regex.h>
|
||||
|
||||
@interface Regex: NSObject
|
||||
{
|
||||
regex_t regex;
|
||||
BOOL built;
|
||||
}
|
||||
- (id) initWithString: (NSString*)pattern;
|
||||
- (NSString*) match: (NSString*)string;
|
||||
@end
|
||||
|
||||
@implementation Regex
|
||||
- (void) dealloc
|
||||
{
|
||||
if (built == YES)
|
||||
{
|
||||
built = NO;
|
||||
regfree(®ex);
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) initWithString: (NSString*)pattern
|
||||
{
|
||||
if (regcomp(®ex, [pattern UTF8String], REG_EXTENDED) != 0)
|
||||
{
|
||||
NSLog(@"Failed to compile regex - '%@'", pattern);
|
||||
DESTROY(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
built = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString*) match: (NSString*)string
|
||||
{
|
||||
regmatch_t matches[2];
|
||||
const char *str = [string UTF8String];
|
||||
|
||||
if (regexec(®ex, str, 1, matches, 0) != 0)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
int l = matches[0].rm_eo - matches[0].rm_so;
|
||||
char b[l+1];
|
||||
|
||||
memcpy(b, &str[matches[0].rm_so], l);
|
||||
b[l] = '\0';
|
||||
return [NSString stringWithUTF8String: b];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
static NSMutableString *
|
||||
replaceFields(NSDictionary *fields)
|
||||
{
|
||||
NSMutableString *m;
|
||||
|
||||
m = [[[fields objectForKey: @"Replacement"] mutableCopy] autorelease];
|
||||
if (nil != m)
|
||||
{
|
||||
NSEnumerator *e;
|
||||
NSString *k;
|
||||
|
||||
e = [fields keyEnumerator];
|
||||
while (nil != (k = [e nextObject]))
|
||||
{
|
||||
if (NO == [k isEqualToString: @"Replacement"])
|
||||
{
|
||||
NSString *v;
|
||||
|
||||
v = [[fields objectForKey: k] description];
|
||||
k = [NSString stringWithFormat: @"{%@}", k];
|
||||
[m replaceOccurrencesOfString: k
|
||||
withString: v
|
||||
options: NSLiteralSearch
|
||||
range: NSMakeRange(0, [m length])];
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This class handles delivery and logging of error and alert messages
|
||||
* to the people who should be monitoring the system. It is used by the
|
||||
* Control server (to which all these messages are delivered) and
|
||||
* implements a simple rule based mechanism for managing final
|
||||
* delivery of the messages.
|
||||
* </p>
|
||||
* <p>The configured rules are compared against each message and any
|
||||
* actions associated with a matching rule are performed.<br />
|
||||
* The matching fields in each rule are -
|
||||
* </p>
|
||||
* <deflist>
|
||||
* <term>Host</term>
|
||||
* <desc>An extended regular expression to match the name of the host
|
||||
* machine on which the message originated (possibly just the host name).
|
||||
* If this is not specified, messages from any host may match.
|
||||
* </desc>
|
||||
* <term>Server</term>
|
||||
* <desc>An extended regular expression to match the name of the server
|
||||
* process from which the message originated (possibly just the server
|
||||
* name).
|
||||
* If this is not specified, messages from any server may match.
|
||||
* </desc>
|
||||
* <term>Type</term>
|
||||
* <desc>The type of message ... <em>Error</em> or <em>Alert</em>.
|
||||
* If this is not specified, messages of any type may match.
|
||||
* </desc>
|
||||
* <term>Pattern</term>
|
||||
* <desc>An extended regular expression used to match the main text
|
||||
* of the message. See the posix regcomp documentation for details
|
||||
* of enhanced posix regular expressions. If this is not present,
|
||||
* any message text will match.
|
||||
* </desc>
|
||||
* <term>Stop</term>
|
||||
* <desc>A boolean (YES or NO) saying whether rule matching should
|
||||
* stop if this rule is matched. If this is NO (the default) then
|
||||
* after any action associated with this rule is performed, matching
|
||||
* continues at the next rule.<br />
|
||||
* <em>Don't use this option injudiciusly. Try to write your pattern
|
||||
* matching rules so that most messages match a single rule to map
|
||||
* them to a nice readable version, and also match a default rule to
|
||||
* log full details to the technical team.</em>
|
||||
* </desc>
|
||||
* <term>Flush</term>
|
||||
* <desc>A boolean (YES or NO) saying whether stored messages due to
|
||||
* be sent out later should be sent out immediately after processing
|
||||
* this rule. This is useful in the event that some time critical
|
||||
* message must be sent, but should not normally be used.<br />
|
||||
* As a special case, instead of the boolean value, this may take
|
||||
* the value <em>Email</em> or <em>Sms</em> indicating that a flush
|
||||
* should be performed, but only on the specified type of messages.<br />
|
||||
* <strong>beware</strong> The batching mechanism exists to prevent
|
||||
* a single problem triggering floods of messages. You should only
|
||||
* override it using <em>Flush</em> where you are <strong>sure</strong>
|
||||
* that messages triggering the flush will be infrequent.
|
||||
* </desc>
|
||||
* </deflist>
|
||||
* <p>There are two additional fields <em>Extra1</em> and <em>Extra2</em>
|
||||
* which are matched against the message. These patterns do not effect
|
||||
* whether the action of the rule is executed or not, but the text matched
|
||||
* is made available for substitution into replacement messages.
|
||||
* </p>
|
||||
* <p>When a match is found the full message is normally sent to all the
|
||||
* destinations listed in the <em>Email</em> and <em>Sms</em> arrays in
|
||||
* the rule, and logged to all the destinations in the <em>Log</em> array.<br />
|
||||
* However, the <em>Replacement</em> field may be used to specify
|
||||
* a message to be sent in the place of the one received. Within the
|
||||
* <em>Replacement</em> string values enclosed in curly brackets will
|
||||
* be substituted as follows -
|
||||
* </p>
|
||||
* <deflist>
|
||||
* <term>Extra1</term>
|
||||
* <desc>The text in the message matched by the Extra1 pattern (if any)</desc>
|
||||
* <term>Extra2</term>
|
||||
* <desc>The text in the message matched by the Extra2 pattern (if any)</desc>
|
||||
* <term>Host</term>
|
||||
* <desc>The host name of the original message</desc>
|
||||
* <term>Server</term>
|
||||
* <desc>The server name of the original message</desc>
|
||||
* <term>Type</term>
|
||||
* <desc>The type of the original message</desc>
|
||||
* <term>Timestamp</term>
|
||||
* <desc>The timestamp of the original message</desc>
|
||||
* <term>Message</term>
|
||||
* <desc>The text of the original message</desc>
|
||||
* <term>Match</term>
|
||||
* <desc>The text matched by the <em>Pattern</em> if any</desc>
|
||||
* </deflist>
|
||||
* <p>The <em>Log</em> array specifies a list of log destinations which are
|
||||
* normally treated as filenames (stored in the standard log directory).
|
||||
* However, a value beginning 'database:' * is logged to a
|
||||
* database (the default database configured for SQLClient).<br />
|
||||
* After the colon you may place a table name, but if you don't then
|
||||
* the message will be logged to the 'Alert' table.<br />
|
||||
* The values logged in separate fields are the Timestamp, Type, Server, Host,
|
||||
* Extra1, Extra2, and full log text (as produced by the Replacement config)
|
||||
* is written into the Message field of the table after having been truncated
|
||||
* to 200 chars. Because of the truncation limit, it is recommended that
|
||||
* if you are trying to include the original alert {Message} (rather
|
||||
* than rewriting it) the Replacement does not include Timestamp,
|
||||
* Type, Server, Host, Extra1, Extra2 which are already saved in
|
||||
* separate fields, and would take up a lot of the 200 chars, which would
|
||||
* be better used to log the actual message.
|
||||
*
|
||||
* </p>
|
||||
* <p>The <em>Sms</em> array lists phone numbers to which Sms alerts are
|
||||
* to be sent.
|
||||
* </p>
|
||||
* <p>The <em>Email</em> array lists email addresses to which email alerts are
|
||||
* to be sent.<br />
|
||||
* An optional 'Subject' field may be present in the rule ... this is used
|
||||
* to specify that the is to be tagged with the given subject line. This
|
||||
* <em>defeats</em> batching of messages in that only messages with the
|
||||
* same subject may be batched in the same email.
|
||||
* </p>
|
||||
*/
|
||||
@implementation EcAlerter : NSObject
|
||||
|
||||
/**
|
||||
* Called to set up or modify the configuration of the alerter.<br />
|
||||
* The dictionary c must contain (keyed on <code>Rules</code> an
|
||||
* array of dictionaries, each of which provides a rule for
|
||||
* delivering some form of alert.<br />
|
||||
* Other values in the configuration are used for standard configuration
|
||||
* of message delivery to the queueing system etc.
|
||||
*/
|
||||
- (BOOL) configure: (NSNotification*)n
|
||||
{
|
||||
NSUserDefaults *d;
|
||||
NSDictionary *c;
|
||||
NSMutableDictionary *m;
|
||||
NSMutableArray *r;
|
||||
unsigned int i;
|
||||
|
||||
d = [EcProc cmdDefaults];
|
||||
c = [d dictionaryForKey: @"Alerter"];
|
||||
|
||||
ASSIGNCOPY(eHost, [c objectForKey: @"EmailHost"]);
|
||||
ASSIGNCOPY(eFrom, [c objectForKey: @"EmailFrom"]);
|
||||
ASSIGNCOPY(ePort, [c objectForKey: @"EmailPort"]);
|
||||
|
||||
/*
|
||||
* Cache a copy of the Rules with modifications to store information
|
||||
* so we don't need to regenerate it every time we check a message.
|
||||
*/
|
||||
r = [[[m objectForKey: @"Rules"] mutableCopy]autorelease];
|
||||
[m removeObjectForKey: @"Rules"];
|
||||
for (i = 0; i < [r count]; i++)
|
||||
{
|
||||
NSMutableDictionary *md;
|
||||
NSString *str;
|
||||
Regex *val;
|
||||
|
||||
md = [[r objectAtIndex: i] mutableCopy];
|
||||
[r replaceObjectAtIndex: i withObject: md];
|
||||
[md release];
|
||||
|
||||
str = [md objectForKey: @"Host"];
|
||||
[md removeObjectForKey: @"HostRegex"];
|
||||
if (str != nil)
|
||||
{
|
||||
val = [[Regex alloc] initWithString: str];
|
||||
if (nil == val)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
[md setObject: val forKey: @"HostRegex"];
|
||||
[val release];
|
||||
}
|
||||
|
||||
str = [md objectForKey: @"Pattern"];
|
||||
[md removeObjectForKey: @"PatternRegex"];
|
||||
if (str != nil)
|
||||
{
|
||||
val = [[Regex alloc] initWithString: str];
|
||||
if (val == nil)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
[md setObject: val forKey: @"PatternRegex"];
|
||||
RELEASE(val);
|
||||
}
|
||||
|
||||
str = [md objectForKey: @"Server"];
|
||||
[md removeObjectForKey: @"ServerRegex"];
|
||||
if (str != nil)
|
||||
{
|
||||
val = [[Regex alloc] initWithString: str];
|
||||
if (val == nil)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
[md setObject: val forKey: @"ServerRegex"];
|
||||
RELEASE(val);
|
||||
}
|
||||
|
||||
str = [md objectForKey: @"Extra1"];
|
||||
[md removeObjectForKey: @"Extra1Regex"];
|
||||
if (str != nil)
|
||||
{
|
||||
val = [[Regex alloc] initWithString: str];
|
||||
if (val == nil)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
[md setObject: val forKey: @"Extra1Regex"];
|
||||
RELEASE(val);
|
||||
}
|
||||
|
||||
str = [md objectForKey: @"Extra2"];
|
||||
[md removeObjectForKey: @"Extra2Regex"];
|
||||
if (str != nil)
|
||||
{
|
||||
val = [[Regex alloc] initWithString: str];
|
||||
if (val == nil)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
[md setObject: val forKey: @"Extra2Regex"];
|
||||
RELEASE(val);
|
||||
}
|
||||
}
|
||||
ASSIGN(rules, r);
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
||||
[timer invalidate];
|
||||
[self flushSms];
|
||||
[self flushEmail];
|
||||
[smtp flush: [NSDate dateWithTimeIntervalSinceNow: 30.0]];
|
||||
RELEASE(smtp);
|
||||
RELEASE(email);
|
||||
RELEASE(sms);
|
||||
RELEASE(rules);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString*) description
|
||||
{
|
||||
return [NSString stringWithFormat: @"%@ -\nEmail:%@\nSms:%@",
|
||||
[super description], email, sms];
|
||||
}
|
||||
|
||||
- (void) flushEmailForAddress: (NSString*)address
|
||||
subject: (NSString*)subject
|
||||
text: (NSString*)text
|
||||
{
|
||||
GSMimeHeader *hdr;
|
||||
GSMimeDocument *doc;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
if (smtp == nil)
|
||||
{
|
||||
smtp = [GSMimeSMTPClient new];
|
||||
}
|
||||
|
||||
if (nil != eHost)
|
||||
{
|
||||
[smtp setHostname: eHost];
|
||||
}
|
||||
|
||||
if (nil != ePort)
|
||||
{
|
||||
[smtp setPort: ePort];
|
||||
}
|
||||
|
||||
if (nil == eFrom)
|
||||
{
|
||||
eFrom = [NSString stringWithFormat: @"alerter@%@",
|
||||
[[NSHost currentHost] name]];
|
||||
RETAIN(eFrom);
|
||||
}
|
||||
|
||||
doc = AUTORELEASE([GSMimeDocument new]);
|
||||
hdr = [[GSMimeHeader alloc] initWithName: @"subject"
|
||||
value: subject
|
||||
parameters: nil];
|
||||
[doc setHeader: hdr];
|
||||
RELEASE(hdr);
|
||||
hdr = [[GSMimeHeader alloc] initWithName: @"to"
|
||||
value: address
|
||||
parameters: nil];
|
||||
[doc setHeader: hdr];
|
||||
RELEASE(hdr);
|
||||
hdr = [[GSMimeHeader alloc] initWithName: @"from"
|
||||
value: eFrom
|
||||
parameters: nil];
|
||||
[doc setHeader: hdr];
|
||||
RELEASE(hdr);
|
||||
|
||||
[doc setContent: text type: @"text/plain" name: nil];
|
||||
[smtp send: doc];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Problem flushing email for address: %@, subject: %@, %@",
|
||||
address, subject, localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to flush any batched email messages.
|
||||
*/
|
||||
- (void) flushEmail
|
||||
{
|
||||
NSDictionary *destinations;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
if ((destinations = email) != nil)
|
||||
{
|
||||
NSEnumerator *addressEnumerator = [destinations keyEnumerator];
|
||||
NSString *address;
|
||||
|
||||
DESTROY(email);
|
||||
while ((address = [addressEnumerator nextObject]) != nil)
|
||||
{
|
||||
NSDictionary *items = [destinations objectForKey: address];
|
||||
NSEnumerator *itemEnumerator = [items keyEnumerator];
|
||||
NSString *subject;
|
||||
|
||||
while ((subject = [itemEnumerator nextObject]) != nil)
|
||||
{
|
||||
NSString *text = [items objectForKey: subject];
|
||||
|
||||
[self flushEmailForAddress: address
|
||||
subject: subject
|
||||
text: text];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Problem flushing email: %@", localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called periodically to flush any batched sms messages.
|
||||
*/
|
||||
- (void) flushSms
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
NSLog(@"Problem flushing sms: ...not currently supported");
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Problem flushing sms: %@", localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This method handles error/alert messages. It is able to handle
|
||||
* multiple (newline separated messages.
|
||||
* </p>
|
||||
* <p>Each message must be a line of the format -<br />
|
||||
* serverName(hostName): YYYY-MM-DD hh:mm:ss.mmm szzzz type - text
|
||||
* </p>
|
||||
* <p>Each message is matched against each rule in the <em>Rules</em>
|
||||
* configuration in turn, and the first match found is used. The
|
||||
* message is sent to the people listed in the <code>Email</code> and
|
||||
* <code>Sms</code> entries in the rule (which may be either single
|
||||
* names or arryas of names).
|
||||
* </p>
|
||||
*/
|
||||
- (void) handleInfo: (NSString*)str
|
||||
{
|
||||
NSArray *a;
|
||||
unsigned int i;
|
||||
NSMutableDictionary *m;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
a = [str componentsSeparatedByString: @"\n"];
|
||||
for (i = 0; i < [a count]; i++)
|
||||
{
|
||||
NSString *inf = [a objectAtIndex: i];
|
||||
NSRange r;
|
||||
NSString *timestamp;
|
||||
NSString *serverName;
|
||||
NSString *hostName;
|
||||
NSString *type = @"Error";
|
||||
unsigned pos;
|
||||
unsigned j;
|
||||
|
||||
str = inf;
|
||||
if ([str length] == 0)
|
||||
{
|
||||
continue; // Nothing to do
|
||||
}
|
||||
|
||||
/* Record format is -
|
||||
* serverName(hostName): timestamp Alert - message
|
||||
* or
|
||||
* serverName(hostName): timestamp Error - message
|
||||
*/
|
||||
r = [str rangeOfString: @":"];
|
||||
if (r.length == 0)
|
||||
{
|
||||
continue; // Not an alert or error
|
||||
}
|
||||
serverName = [str substringToIndex: r.location];
|
||||
str = [str substringFromIndex: NSMaxRange(r) + 1];
|
||||
r = [serverName rangeOfString: @"("];
|
||||
if (r.length == 0)
|
||||
{
|
||||
continue; // Not an alert or error
|
||||
}
|
||||
pos = NSMaxRange(r);
|
||||
hostName = [serverName substringWithRange:
|
||||
NSMakeRange(pos, [serverName length] - pos - 1)];
|
||||
serverName = [serverName substringToIndex: r.location];
|
||||
|
||||
r = [str rangeOfString: @" Alert - "];
|
||||
if (r.length == 0)
|
||||
{
|
||||
r = [str rangeOfString: @" Error - "];
|
||||
if (r.length == 0)
|
||||
{
|
||||
continue; // Not an alert or error
|
||||
}
|
||||
type = @"Error";
|
||||
}
|
||||
else
|
||||
{
|
||||
type = @"Alert";
|
||||
}
|
||||
timestamp = [str substringToIndex: r.location];
|
||||
|
||||
str = [str substringFromIndex: NSMaxRange(r)];
|
||||
|
||||
for (j = 0; j < [rules count]; j++)
|
||||
{
|
||||
NSDictionary *d = [rules objectAtIndex: j];
|
||||
NSString *match = nil;
|
||||
Regex *e;
|
||||
NSString *s;
|
||||
id o;
|
||||
|
||||
s = [d objectForKey: @"Type"];
|
||||
if (s != nil && [s isEqualToString: type] == NO)
|
||||
{
|
||||
continue; // Not a match.
|
||||
}
|
||||
e = [d objectForKey: @"ServerRegex"];
|
||||
if (e != nil && [e match: serverName] == nil)
|
||||
{
|
||||
continue; // Not a match.
|
||||
}
|
||||
e = [d objectForKey: @"HostRegex"];
|
||||
if (e != nil && [e match: hostName] == nil)
|
||||
{
|
||||
continue; // Not a match.
|
||||
}
|
||||
e = [d objectForKey: @"PatternRegex"];
|
||||
if (e != nil && (match = [e match: str]) == nil)
|
||||
{
|
||||
continue; // Not a match.
|
||||
}
|
||||
|
||||
m = [NSMutableDictionary new];
|
||||
|
||||
/*
|
||||
* If the Extra1 or Extra2 patterns are matched,
|
||||
* The matching strings are made available for
|
||||
* substitution inot the replacement message.
|
||||
*/
|
||||
[m removeObjectForKey: @"Extra1"];
|
||||
e = [d objectForKey: @"Extra1Regex"];
|
||||
if (e != nil && (match = [e match: str]) != nil)
|
||||
{
|
||||
[m setObject: match forKey: @"Extra1"];
|
||||
}
|
||||
|
||||
[m removeObjectForKey: @"Extra2"];
|
||||
e = [d objectForKey: @"Extra2Regex"];
|
||||
if (e != nil && (match = [e match: str]) != nil)
|
||||
{
|
||||
[m setObject: match forKey: @"Extra2"];
|
||||
}
|
||||
|
||||
/* We set the Replacement later, because if it is not
|
||||
* set, we want to set a different default for Sms/Email
|
||||
* and database: for Sms/Email logs we want to include
|
||||
* Server, Host, Timestamp, Type and Message (possibly
|
||||
* trying to use as little spaces as possible for Sms,
|
||||
* while trying to display comfortably for Email), while
|
||||
* for database logs we only want to include the
|
||||
* Message.
|
||||
*/
|
||||
|
||||
[m setObject: serverName forKey: @"Server"];
|
||||
[m setObject: hostName forKey: @"Host"];
|
||||
[m setObject: type forKey: @"Type"];
|
||||
[m setObject: timestamp forKey: @"Timestamp"];
|
||||
[m setObject: str forKey: @"Message"];
|
||||
if (match != nil)
|
||||
{
|
||||
[m setObject: match forKey: @"Match"];
|
||||
}
|
||||
|
||||
// NSLog(@"Match produced %@", s);
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
o = [d objectForKey: @"Log"];
|
||||
if ([o isKindOfClass: [NSString class]] == YES)
|
||||
{
|
||||
if ([o hasPrefix: @"("])
|
||||
{
|
||||
o = [(NSString*)o propertyList];
|
||||
}
|
||||
else
|
||||
{
|
||||
o = [NSArray arrayWithObject: o];
|
||||
}
|
||||
}
|
||||
if (o != nil)
|
||||
{
|
||||
NSString *s = [d objectForKey: @"Replacement"];
|
||||
if (s == nil)
|
||||
{
|
||||
s = @"{Message}";
|
||||
}
|
||||
[m setObject: s forKey: @"Replacement"];
|
||||
|
||||
[self log: m to: o];
|
||||
}
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Exception handling database log for rule: %@",
|
||||
localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
o = [d objectForKey: @"Email"];
|
||||
if ([o isKindOfClass: [NSString class]] == YES)
|
||||
{
|
||||
if ([o hasPrefix: @"("])
|
||||
{
|
||||
o = [(NSString*)o propertyList];
|
||||
}
|
||||
else
|
||||
{
|
||||
o = [NSArray arrayWithObject: o];
|
||||
}
|
||||
}
|
||||
if (o != nil)
|
||||
{
|
||||
NSString *s = [d objectForKey: @"Subject"];
|
||||
|
||||
if (s != nil)
|
||||
{
|
||||
[m setObject: s forKey: @"Subject"];
|
||||
}
|
||||
|
||||
s = [d objectForKey: @"Replacement"];
|
||||
if (s == nil)
|
||||
{
|
||||
/* Full details. */
|
||||
s = @"{Server}({Host}): {Timestamp} {Type} - {Message}";
|
||||
}
|
||||
[m setObject: s forKey: @"Replacement"];
|
||||
|
||||
[self mail: m to: o];
|
||||
}
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Exception handling Email send for rule: %@",
|
||||
localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
o = [d objectForKey: @"Sms"];
|
||||
if ([o isKindOfClass: [NSString class]] == YES)
|
||||
{
|
||||
if ([o hasPrefix: @"("])
|
||||
{
|
||||
o = [(NSString*)o propertyList];
|
||||
}
|
||||
else
|
||||
{
|
||||
o = [NSArray arrayWithObject: o];
|
||||
}
|
||||
}
|
||||
if (o != nil)
|
||||
{
|
||||
NSString *s = [d objectForKey: @"Replacement"];
|
||||
if (s == nil)
|
||||
{
|
||||
/* Use few spaces so that more of the
|
||||
* message fits into an Sms. */
|
||||
s = @"{Server}({Host}):{Timestamp} {Type}-{Message}";
|
||||
}
|
||||
[m setObject: s forKey: @"Replacement"];
|
||||
|
||||
[self sms: m to: o];
|
||||
}
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Exception handling Sms send for rule: %@",
|
||||
localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
|
||||
RELEASE(m);
|
||||
|
||||
s = [d objectForKey: @"Flush"];
|
||||
if (s != nil)
|
||||
{
|
||||
if ([s caseInsensitiveCompare: @"Email"] == NSOrderedSame)
|
||||
{
|
||||
[self flushEmail];
|
||||
}
|
||||
else if ([s caseInsensitiveCompare: @"Sms"] == NSOrderedSame)
|
||||
{
|
||||
[self flushSms];
|
||||
}
|
||||
else if ([s boolValue] == YES)
|
||||
{
|
||||
[self flushSms];
|
||||
[self flushEmail];
|
||||
}
|
||||
}
|
||||
|
||||
if ([[d objectForKey: @"Stop"] boolValue] == YES)
|
||||
{
|
||||
break; // Don't want to perform any more matches.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Problem in handleInfo:'%@' ... %@", str, localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if (nil != (self = [super init]))
|
||||
{
|
||||
timer = [NSTimer scheduledTimerWithTimeInterval: 240.0
|
||||
target: self
|
||||
selector: @selector(timeout:)
|
||||
userInfo: nil
|
||||
repeats: YES];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver: self
|
||||
selector: @selector(configure:)
|
||||
name: NSUserDefaultsDidChangeNotification
|
||||
object: [NSUserDefaults standardUserDefaults]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by -handleInfo: to log a message to an array of destinations.
|
||||
*/
|
||||
- (void) log: (NSMutableDictionary*)m to: (NSArray*)destinations
|
||||
{
|
||||
NSEnumerator *e = [destinations objectEnumerator];
|
||||
NSString *d;
|
||||
NSString *s;
|
||||
|
||||
/*
|
||||
* Perform {field-name} substitutions ...
|
||||
*/
|
||||
s = replaceFields(m);
|
||||
while ((d = [e nextObject]) != nil)
|
||||
{
|
||||
[[EcProc cmdLogFile: d] printf: @"%@\n", s];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by -handleInfo: to pass a message to an array of destinations.
|
||||
* The message is actually appended to any cached messages for those
|
||||
* destinations ... and the cache is periodically flushed.
|
||||
*/
|
||||
- (void) mail: (NSMutableDictionary*)m to: (NSArray*)destinations
|
||||
{
|
||||
NSEnumerator *e = [destinations objectEnumerator];
|
||||
NSString *d;
|
||||
NSString *s;
|
||||
NSString *subject = [m objectForKey: @"Subject"];
|
||||
|
||||
if (subject == nil)
|
||||
{
|
||||
subject = @"system alert";
|
||||
}
|
||||
else
|
||||
{
|
||||
AUTORELEASE(RETAIN(subject));
|
||||
[m removeObjectForKey: @"Subject"];
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform {field-name} substitutions ...
|
||||
*/
|
||||
s = replaceFields(m);
|
||||
if (email == nil)
|
||||
{
|
||||
email = [NSMutableDictionary new];
|
||||
}
|
||||
while ((d = [e nextObject]) != nil)
|
||||
{
|
||||
NSMutableDictionary *md = [email objectForKey: d];
|
||||
NSString *msg;
|
||||
|
||||
if (md == nil)
|
||||
{
|
||||
md = [NSMutableDictionary new];
|
||||
[email setObject: md forKey: d];
|
||||
RELEASE(md);
|
||||
}
|
||||
|
||||
msg = [md objectForKey: subject];
|
||||
|
||||
/*
|
||||
* If adding the new text would take an existing message over the
|
||||
* size limit, send the existing stuff first.
|
||||
*/
|
||||
if ([msg length] > 0 && [msg length] + [s length] + 2 > 20*1024)
|
||||
{
|
||||
AUTORELEASE(RETAIN(msg));
|
||||
[md removeObjectForKey: subject];
|
||||
[self flushEmailForAddress: d
|
||||
subject: subject
|
||||
text: msg];
|
||||
msg = nil;
|
||||
}
|
||||
if (msg == nil)
|
||||
{
|
||||
msg = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = [msg stringByAppendingFormat: @"\n\n%@", s];
|
||||
}
|
||||
[md setObject: msg forKey: subject];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by -handleInfo: to pass a message to an array of destinations.
|
||||
* The message replaces any cached messages for those
|
||||
* destinations (and has a count of the lost messages noted) ... and
|
||||
* the cache is periodically flushed.
|
||||
*/
|
||||
- (void) sms: (NSMutableDictionary*)m to: (NSArray*)destinations
|
||||
{
|
||||
NSEnumerator *e = [destinations objectEnumerator];
|
||||
NSString *d;
|
||||
NSString *s;
|
||||
NSString *t;
|
||||
|
||||
/*
|
||||
* Perform {field-name} substitutions, but to shorten the message
|
||||
* remove any Timestamp value from the dictionary.
|
||||
*/
|
||||
t = RETAIN([m objectForKey: @"Timestamp"]);
|
||||
[m removeObjectForKey: @"Timestamp"];
|
||||
s = replaceFields(m);
|
||||
if (t != nil)
|
||||
{
|
||||
[m setObject: t forKey: @"Timestamp"];
|
||||
RELEASE(t);
|
||||
}
|
||||
|
||||
if (sms == nil)
|
||||
{
|
||||
sms = [NSMutableDictionary new];
|
||||
}
|
||||
while ((d = [e nextObject]) != nil)
|
||||
{
|
||||
NSString *msg = [sms objectForKey: d];
|
||||
|
||||
if (msg == nil)
|
||||
{
|
||||
msg = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
int missed = 0;
|
||||
|
||||
if ([msg hasPrefix: @"Missed("] == YES)
|
||||
{
|
||||
NSRange r = [msg rangeOfString: @")"];
|
||||
|
||||
r = NSMakeRange(7, r.location - 7);
|
||||
msg = [msg substringWithRange: r];
|
||||
missed = [msg intValue];
|
||||
}
|
||||
missed++;
|
||||
msg = [NSString stringWithFormat: @"Missed(%d)\n%@", missed, s];
|
||||
}
|
||||
[sms setObject: msg forKey: d];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for the periodic calling of -flushEmail and -flushSms
|
||||
*/
|
||||
- (void) timeout: (NSTimer*)t
|
||||
{
|
||||
[self flushSms];
|
||||
[self flushEmail];
|
||||
}
|
||||
@end
|
||||
|
132
EcBroadcastProxy.h
Normal file
132
EcBroadcastProxy.h
Normal file
|
@ -0,0 +1,132 @@
|
|||
/* -*-objc-*-
|
||||
* Nicola Pero, Brainstorm, October 2000
|
||||
* EcBroadcastProxy - an object able to broadcast a message to
|
||||
* a list of remote objects.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ECBROADCASTPROXY_H
|
||||
#define _ECBROADCASTPROXY_H
|
||||
|
||||
#include <Foundation/NSObject.h>
|
||||
|
||||
@class NSArray;
|
||||
@class NSMutableArray;
|
||||
@class NSString;
|
||||
|
||||
enum EcBroadcastProxyError
|
||||
{
|
||||
BCP_NO_ERROR = 0,
|
||||
BCP_COULD_NOT_CONNECT = 1,
|
||||
BCP_CONNECTION_WENT_DOWN = 2,
|
||||
BCP_MESSAGING_TIMEOUT = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* In this class, remote servers are always listed in the same order.
|
||||
* So, you can access them by their index; to get the host/name, you
|
||||
* can get it by asking the receiverNames and receiverHosts and then
|
||||
* looking up the one at the index you want.
|
||||
*/
|
||||
|
||||
@interface EcBroadcastProxy : NSObject
|
||||
{
|
||||
/* The names of the receiver object */
|
||||
NSArray *receiverNames;
|
||||
/* The hosts the receiver objects are on */
|
||||
NSArray *receiverHosts;
|
||||
|
||||
/* The [proxy to the] remote objects */
|
||||
NSMutableArray *receiverObjects;
|
||||
|
||||
/* The delegate (if any) */
|
||||
id delegate;
|
||||
|
||||
/* The statistical info about what we did */
|
||||
|
||||
/* Messages returning void */
|
||||
int onewayFullySent;
|
||||
int onewayPartiallySent;
|
||||
int onewayFailed;
|
||||
/* Messages returning id */
|
||||
int idFullySent;
|
||||
int idPartiallySent;
|
||||
int idFailed;
|
||||
}
|
||||
|
||||
- (id) initWithReceiverNames: (NSArray *)names
|
||||
receiverHosts: (NSArray *)hosts;
|
||||
|
||||
/** Configuration array contains a list of dictionaries (one for each
|
||||
receiver) - each dictionary has two keys: `Name' and `Host', with
|
||||
the corresponding values set. */
|
||||
|
||||
- (id) initWithReceivers: (NSArray *)receivers;
|
||||
|
||||
/* Methods specific to EcBroadcastProxy (which should not be forwarded
|
||||
to remote servers) are prefixed with `BCP' to avoid name clashes.
|
||||
Anything not prefixed with BCP is forwarded. */
|
||||
|
||||
/** Create connections to the receivers if needed. It is called
|
||||
internally when a message to broadcast comes in; but you may want
|
||||
to call this method in advance to raise the connections so that
|
||||
when a message to broadcast comes in, the connections are already
|
||||
up and ready. */
|
||||
- (void) BCPraiseConnections;
|
||||
|
||||
/** Get a string describing the status of the broadcast object */
|
||||
- (NSString *) BCPstatus;
|
||||
|
||||
/** Set a delegate.<br />
|
||||
* The delegate gets a -BCP:lostConnectionToServer:onHost: message
|
||||
* upon connection lost, and -BCP:madeConnectionToServer:onHost:
|
||||
* message upon connection made.
|
||||
*/
|
||||
- (void) BCPsetDelegate: (id)delegate;
|
||||
|
||||
- (id) BCPdelegate;
|
||||
|
||||
/* [Advanced stuff]
|
||||
Access to a single one of the servers we are broadcasting to.
|
||||
|
||||
Eg, after sending a search to multiple servers, you might need to
|
||||
get further information only from the servers which answered
|
||||
affirmatively to your query. To do it, use BCPproxyForName:host:
|
||||
to get the single remote server(s) you want to talk to, and talk to
|
||||
it (each of them) directly. */
|
||||
|
||||
/* Get the list of servers */
|
||||
- (NSArray *) BCPreceiverNames;
|
||||
|
||||
- (NSArray *) BCPreceiverHosts;
|
||||
|
||||
/* Get only the number of receivers */
|
||||
- (int) BCPreceiverCount;
|
||||
|
||||
/* Raise connection to server at index */
|
||||
- (void) BCPraiseConnection: (int)index;
|
||||
|
||||
/* The following one gives you back the proxy to talk to.
|
||||
It automatically BCPraise[s]Connection to that server before sending
|
||||
you back a proxy. It returns nil upon failure. */
|
||||
- (id) BCPproxy: (int)index;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSObject (BCPdelegate)
|
||||
|
||||
- (void) BCP: (EcBroadcastProxy *)proxy
|
||||
lostConnectionToServer: (NSString *)name
|
||||
host: (NSString *)host;
|
||||
|
||||
- (void) BCP: (EcBroadcastProxy *)proxy
|
||||
madeConnectionToServer: (NSString *)name
|
||||
host: (NSString *)host;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* _BROADCASTPROXY_H */
|
||||
|
||||
|
||||
|
||||
|
676
EcBroadcastProxy.m
Normal file
676
EcBroadcastProxy.m
Normal file
|
@ -0,0 +1,676 @@
|
|||
/*
|
||||
* Nicola Pero, Brainstorm, October 2000
|
||||
* EcBroadcastProxy - an object able to broadcast a message to
|
||||
* a list of remote objects.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "EcBroadcastProxy.h"
|
||||
|
||||
/*
|
||||
EcBroadcastProxy
|
||||
*/
|
||||
|
||||
static NSNotificationCenter *nc;
|
||||
static NSNull *null;
|
||||
|
||||
@interface EcBroadcastProxy (Private)
|
||||
/* Methods which make it work :) */
|
||||
|
||||
/* NB: We post a @"EcBroadcastProxyHadIPNotification" when this method
|
||||
is called, and a @"EcBroadcastProxyHadOPNotification" when it completed
|
||||
(if any output was sent). */
|
||||
- (void) forwardInvocation: (NSInvocation*)anInvocation;
|
||||
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector;
|
||||
- (void) BCPforwardOneWayInvocation: (NSInvocation*)anInvocation;
|
||||
- (void) BCPforwardIdInvocation: (NSInvocation*)anInvocation;
|
||||
- (void) BCPforwardPrivateInvocation: (NSInvocation*)anInvocation;
|
||||
@end
|
||||
|
||||
@implementation EcBroadcastProxy
|
||||
+ (void) initialize
|
||||
{
|
||||
if (self == [EcBroadcastProxy class])
|
||||
{
|
||||
nc = [NSNotificationCenter defaultCenter];
|
||||
null = [NSNull null];
|
||||
}
|
||||
}
|
||||
|
||||
/* Designated initializer */
|
||||
- (id) initWithReceiverNames: (NSArray *)names
|
||||
receiverHosts: (NSArray *)hosts
|
||||
{
|
||||
int i, count;
|
||||
|
||||
NSLog (@"EcBroadcastProxy: initializing with %@ names and %@ host",
|
||||
names, hosts);
|
||||
|
||||
if (nil != (self = [super init]))
|
||||
{
|
||||
|
||||
if ([names count] != [hosts count])
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException format:
|
||||
@"invalid initialization of EcBroadcastProxy: "
|
||||
@"[names count] != [hosts count]"];
|
||||
}
|
||||
|
||||
ASSIGN (receiverNames, names);
|
||||
ASSIGN (receiverHosts, hosts);
|
||||
receiverObjects = [NSMutableArray new];
|
||||
count = [receiverNames count];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
[receiverObjects addObject: null];
|
||||
}
|
||||
}
|
||||
/* All the statistical ivars are automatically initialized to zero */
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) initWithReceivers: (NSArray *)receivers
|
||||
{
|
||||
int i, count;
|
||||
NSMutableArray *names;
|
||||
NSMutableArray *hosts;
|
||||
|
||||
names = AUTORELEASE ([NSMutableArray new]);
|
||||
hosts = AUTORELEASE ([NSMutableArray new]);
|
||||
|
||||
count = [receivers count];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
NSString *string;
|
||||
NSDictionary *dict = (NSDictionary *)[receivers objectAtIndex: i];
|
||||
|
||||
if ([dict isKindOfClass: [NSDictionary class]] == NO)
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException format:
|
||||
@"invalid initialization of EcBroadcastProxy: "
|
||||
@"config dictionary is not a dictionary"];
|
||||
}
|
||||
|
||||
string = [dict objectForKey: @"Name"];
|
||||
[(NSMutableArray *)names addObject: string];
|
||||
|
||||
string = [dict objectForKey: @"Host"];
|
||||
if (string == nil)
|
||||
{
|
||||
/* Would `localhost' be a better choice ? */
|
||||
string = @"*";
|
||||
}
|
||||
[(NSMutableArray *)hosts addObject: string];
|
||||
}
|
||||
|
||||
return [self initWithReceiverNames: names receiverHosts: hosts];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE (receiverNames);
|
||||
RELEASE (receiverHosts);
|
||||
RELEASE (receiverObjects);
|
||||
[nc removeObserver: self];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) BCPraiseConnections
|
||||
{
|
||||
int i, count;
|
||||
|
||||
count = [receiverNames count];
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
[self BCPraiseConnection: i];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) BCPraiseConnection: (int)index
|
||||
{
|
||||
NSString *host = [receiverHosts objectAtIndex: index];
|
||||
NSString *name = [receiverNames objectAtIndex: index];
|
||||
id object = [receiverObjects objectAtIndex: index];
|
||||
|
||||
if (object == null)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
object = [NSConnection
|
||||
rootProxyForConnectionWithRegisteredName: name
|
||||
host: host
|
||||
usingNameServer: [NSSocketPortNameServer sharedInstance]];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Caught exception trying to connect to '%@:%@' - %@\n",
|
||||
host, name, localException);
|
||||
object = nil;
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
|
||||
if (object != nil)
|
||||
{
|
||||
id c = [object connectionForProxy];
|
||||
|
||||
[receiverObjects replaceObjectAtIndex: index withObject: object];
|
||||
[nc addObserver: self
|
||||
selector: @selector(BCPconnectionBecameInvalid:)
|
||||
name: NSConnectionDidDieNotification
|
||||
object: c];
|
||||
if ([delegate respondsToSelector:
|
||||
@selector(BCP:madeConnectionToServer:host:)])
|
||||
{
|
||||
[delegate BCP: self madeConnectionToServer: name
|
||||
host: host];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) BCPsetDelegate: (id)object
|
||||
{
|
||||
delegate = object;
|
||||
}
|
||||
|
||||
- (id) BCPdelegate
|
||||
{
|
||||
return delegate;
|
||||
}
|
||||
|
||||
- (NSArray *) BCPreceiverNames
|
||||
{
|
||||
return receiverNames;
|
||||
}
|
||||
|
||||
- (NSArray *) BCPreceiverHosts
|
||||
{
|
||||
return receiverHosts;
|
||||
}
|
||||
|
||||
- (int) BCPreceiverCount
|
||||
{
|
||||
return [receiverNames count];
|
||||
}
|
||||
|
||||
- (id) BCPproxy: (int)index
|
||||
{
|
||||
id proxy;
|
||||
|
||||
[self BCPraiseConnection: index];
|
||||
proxy = [receiverObjects objectAtIndex: index];
|
||||
|
||||
if (proxy == null)
|
||||
{ return nil; }
|
||||
else
|
||||
{ return proxy; }
|
||||
}
|
||||
|
||||
- (NSString *) BCPstatus
|
||||
{
|
||||
NSMutableString *output;
|
||||
int i, count;
|
||||
|
||||
output = AUTORELEASE ([NSMutableString new]);
|
||||
|
||||
count = [receiverNames count];
|
||||
|
||||
[output appendFormat: @"EcBroadcastProxy with %d receivers:\n", count];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
NSString *host = [receiverHosts objectAtIndex: i];
|
||||
NSString *name = [receiverNames objectAtIndex: i];
|
||||
id object = [receiverObjects objectAtIndex: i];
|
||||
|
||||
[output appendFormat: @" (%d) Connection to `%@' on host `%@' is ",
|
||||
i, name, host];
|
||||
|
||||
if (object == null)
|
||||
{
|
||||
[output appendString: @"DEAD\n"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[output appendString: @"LIVE\n"];
|
||||
}
|
||||
}
|
||||
[output appendString: @"\nVoid messages statistics:\n"];
|
||||
[output appendFormat: @" * succesfully broadcasted: %d\n", onewayFullySent];
|
||||
[output appendFormat: @" * partially broadcasted: %d\n",
|
||||
onewayPartiallySent];
|
||||
[output appendFormat: @" * failed to broadcast: %d\n", onewayFailed];
|
||||
|
||||
[output appendString: @"\nId messages statistics:\n"];
|
||||
[output appendFormat: @" * succesfully broadcasted: %d\n", idFullySent];
|
||||
[output appendFormat: @" * partially broadcasted: %d\n", idPartiallySent];
|
||||
[output appendFormat: @" * failed to broadcast: %d\n", idFailed];
|
||||
|
||||
/* TODO: Should display info about the last message ? */
|
||||
return output;
|
||||
}
|
||||
|
||||
- (void) BCPforwardOneWayInvocation: (NSInvocation*)anInvocation
|
||||
{
|
||||
unsigned int i, count;
|
||||
NSMutableArray *list;
|
||||
unsigned int sent = 0;
|
||||
|
||||
/* Raise any pending connection */
|
||||
[self BCPraiseConnections];
|
||||
|
||||
/* Prepare a mutable list of servers to message */
|
||||
list = [NSMutableArray arrayWithArray: receiverObjects];
|
||||
|
||||
count = [list count];
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
id receiver = [list objectAtIndex: i];
|
||||
|
||||
/* Check that we are connected to the server. */
|
||||
if (receiver == null)
|
||||
{ continue; }
|
||||
|
||||
/* Check in case server has died. */
|
||||
if ([receiverObjects indexOfObjectIdenticalTo: receiver]
|
||||
== NSNotFound)
|
||||
{ continue; }
|
||||
|
||||
/* Send the method */
|
||||
NS_DURING
|
||||
{
|
||||
[anInvocation invokeWithTarget: receiver];
|
||||
sent++;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
/* Not sure what to do here */
|
||||
NSLog (@"Caught exception trying to send event - %@\n",
|
||||
localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
/* Update statistical records */
|
||||
if (sent == [receiverObjects count])
|
||||
{
|
||||
onewayFullySent++;
|
||||
}
|
||||
else if (sent == 0)
|
||||
{
|
||||
onewayFailed++;
|
||||
}
|
||||
else
|
||||
{
|
||||
onewayPartiallySent++;
|
||||
}
|
||||
|
||||
if (sent != 0)
|
||||
{
|
||||
/* Post notification we had an output */
|
||||
[nc postNotificationName: @"EcBroadcastProxyHadOPNotification"
|
||||
object: self];
|
||||
}
|
||||
|
||||
NSLog (@"Broadcasted oneway message to %d (out of %@PRIuPTR@) targets",
|
||||
sent, [receiverObjects count]);
|
||||
}
|
||||
|
||||
- (void) BCPforwardIdInvocation: (NSInvocation*)anInvocation
|
||||
{
|
||||
int i, count;
|
||||
NSMutableArray *list;
|
||||
NSMutableArray *returnArray;
|
||||
unsigned int sent = 0;
|
||||
|
||||
/* Raise any pending connection */
|
||||
[self BCPraiseConnections];
|
||||
|
||||
/* Prepare a mutable list of servers to message */
|
||||
list = [NSMutableArray arrayWithArray: receiverObjects];
|
||||
|
||||
/* Prepare the return array - this will contain an entry for *each*
|
||||
server. Failures in contacting a server result in the error info
|
||||
being set for that server. */
|
||||
returnArray = [NSMutableArray new];
|
||||
|
||||
count = [list count];
|
||||
|
||||
/* We need to make sure we keep the order */
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
id receiver = [list objectAtIndex: i];
|
||||
NSDictionary *dict;
|
||||
id returnValue;
|
||||
|
||||
/* Check that we are connected to the server. */
|
||||
if (receiver == null)
|
||||
{
|
||||
/* No server to talk to */
|
||||
dict = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithInt:
|
||||
BCP_COULD_NOT_CONNECT],
|
||||
@"error", nil];
|
||||
[returnArray addObject: dict];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([receiverObjects indexOfObjectIdenticalTo: receiver]
|
||||
== NSNotFound)
|
||||
{
|
||||
/* Server died in the meanwhile */
|
||||
dict = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithInt:
|
||||
BCP_CONNECTION_WENT_DOWN],
|
||||
@"error", nil];
|
||||
[returnArray addObject: dict];
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Send the method */
|
||||
NS_DURING
|
||||
{
|
||||
[anInvocation invokeWithTarget: receiver];
|
||||
[anInvocation getReturnValue: &returnValue];
|
||||
/* OK - done it! */
|
||||
dict = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithInt: BCP_NO_ERROR],
|
||||
@"error",
|
||||
returnValue, @"response", nil];
|
||||
[returnArray addObject: dict];
|
||||
sent++;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
/* Didn't work - messaging timeout */
|
||||
NSLog (@"Caught exception trying to send event - %@\n",
|
||||
localException);
|
||||
dict = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithInt:
|
||||
BCP_MESSAGING_TIMEOUT],
|
||||
@"error",
|
||||
[localException description],
|
||||
@"error description", nil];
|
||||
[returnArray addObject: dict];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
[anInvocation setReturnValue: &returnArray];
|
||||
|
||||
/* Update statistical records */
|
||||
if (sent == [receiverObjects count])
|
||||
{
|
||||
idFullySent++;
|
||||
}
|
||||
else if (sent == 0)
|
||||
{
|
||||
idFailed++;
|
||||
}
|
||||
else
|
||||
{
|
||||
idPartiallySent++;
|
||||
}
|
||||
|
||||
if (sent != 0)
|
||||
{
|
||||
/* Post notification we had an output */
|
||||
[nc postNotificationName: @"EcBroadcastProxyHadOPNotification"
|
||||
object: self];
|
||||
}
|
||||
|
||||
NSLog (@"Broadcasted Id message to %d (out of %@PRIuPTR@) targets",
|
||||
sent, [receiverObjects count]);
|
||||
}
|
||||
|
||||
- (void) forwardInvocation: (NSInvocation*)anInvocation
|
||||
{
|
||||
NSMethodSignature *methodSig;
|
||||
|
||||
NSLog (@"ForwardInvocation: %@", anInvocation);
|
||||
|
||||
/* Post notification we had an input */
|
||||
[nc postNotificationName: @"EcBroadcastProxyHadIPNotification" object: self];
|
||||
|
||||
/* Get information about the return type */
|
||||
methodSig = [anInvocation methodSignature];
|
||||
|
||||
if ([methodSig isOneway] == YES)
|
||||
{
|
||||
[self BCPforwardOneWayInvocation: anInvocation];
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *signature;
|
||||
|
||||
signature = [methodSig methodReturnType];
|
||||
|
||||
if (*signature == _C_VOID)
|
||||
{
|
||||
/* Issue a warning here ? */
|
||||
[self BCPforwardOneWayInvocation: anInvocation];
|
||||
}
|
||||
else if (*signature == _C_ID)
|
||||
{
|
||||
[self BCPforwardIdInvocation: anInvocation];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We only accept (id) return types */
|
||||
[NSException raise: NSInvalidArgumentException format:
|
||||
@"EcBroadcastProxy can only respond to oneway methods "
|
||||
@"or methods returning an object or void"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) BCPconnectionBecameInvalid: (NSNotification*)notification
|
||||
{
|
||||
id connection;
|
||||
|
||||
connection = [notification object];
|
||||
|
||||
[nc removeObserver: self
|
||||
name: NSConnectionDidDieNotification
|
||||
object: connection];
|
||||
|
||||
if ([connection isKindOfClass: [NSConnection class]])
|
||||
{
|
||||
unsigned int i;
|
||||
/*
|
||||
* Now - remove any proxy that uses this connection from the cache.
|
||||
*/
|
||||
for (i = 0; i < [receiverObjects count]; i++)
|
||||
{
|
||||
id obj = [receiverObjects objectAtIndex: i];
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
if ([obj connectionForProxy] == connection)
|
||||
{
|
||||
[receiverObjects replaceObjectAtIndex: i
|
||||
withObject: null];
|
||||
if ([delegate respondsToSelector:
|
||||
@selector(BCP:lostConnectionToServer:host:)])
|
||||
{
|
||||
NSString *name = [receiverNames objectAtIndex: i];
|
||||
NSString *host = [receiverHosts objectAtIndex: i];
|
||||
[delegate BCP: self lostConnectionToServer: name
|
||||
host: host];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NSLog (@"client (%p) gone away.", connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog (@"non-Connection sent invalidation");
|
||||
}
|
||||
}
|
||||
|
||||
/* And now the stuff which makes it work */
|
||||
|
||||
- (void) BCPforwardPrivateInvocation: (NSInvocation*)anInvocation
|
||||
{
|
||||
/* TODO - Better stuff */
|
||||
int i, count;
|
||||
int nilValue = 0;
|
||||
BOOL ok = NO;
|
||||
|
||||
/* Raise any pending connection */
|
||||
[self BCPraiseConnections];
|
||||
|
||||
count = [receiverObjects count];
|
||||
|
||||
/* Look for the first not-null receiver */
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
id receiver = [receiverObjects objectAtIndex: i];
|
||||
|
||||
if (receiver != null)
|
||||
{
|
||||
/* Send the method to him */
|
||||
NS_DURING
|
||||
{
|
||||
[anInvocation invokeWithTarget: receiver];
|
||||
ok =YES;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog (@"Caught exception trying to send event - %@\n",
|
||||
localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
|
||||
if (ok == YES)
|
||||
return;
|
||||
/* No luck - try with the next one */
|
||||
}
|
||||
}
|
||||
/* Eh - should perhaps raise an exception ? */
|
||||
NSLog (@"Could not forward private invocation %@", anInvocation);
|
||||
/* Try this trick */
|
||||
[anInvocation setReturnValue: &nilValue];
|
||||
}
|
||||
|
||||
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
|
||||
{
|
||||
/* TODO - Better stuff */
|
||||
BOOL ok = NO;
|
||||
NSMethodSignature *methodSig = nil;
|
||||
int i, count;
|
||||
|
||||
/* Raise any pending connection */
|
||||
[self BCPraiseConnections];
|
||||
|
||||
count = [receiverObjects count];
|
||||
|
||||
/* Look for the first not-null receiver */
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
id receiver = [receiverObjects objectAtIndex: i];
|
||||
|
||||
if (receiver != null)
|
||||
{
|
||||
/* Ask to him for the method signature */
|
||||
NS_DURING
|
||||
{
|
||||
methodSig = [receiver methodSignatureForSelector: aSelector];
|
||||
ok = YES;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog (@"Caught exception trying to send event - %@\n",
|
||||
localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
|
||||
if (ok == YES)
|
||||
{
|
||||
return methodSig;
|
||||
}
|
||||
/* No luck - try with the next one */
|
||||
}
|
||||
}
|
||||
/* Eh - should perhaps raise an exception ? */
|
||||
NSLog (@"Could not determine method signature of selector %@",
|
||||
NSStringFromSelector(aSelector));
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL) respondsToSelector:(SEL)aSelector
|
||||
{
|
||||
NSMethodSignature *ms;
|
||||
NSInvocation *inv;
|
||||
SEL selector = @selector (respondsToSelector:);
|
||||
BOOL result;
|
||||
|
||||
/* Recognize standard methods */
|
||||
if ([super respondsToSelector: aSelector] == YES)
|
||||
return YES;
|
||||
|
||||
/* And remote ones - FIXME/TODO: do not respond YES to stuff which
|
||||
is not returning (oneway void) or (id) */
|
||||
ms = [self methodSignatureForSelector: selector];
|
||||
inv = [NSInvocation invocationWithMethodSignature: ms];
|
||||
[inv setSelector: selector];
|
||||
[inv setTarget: self];
|
||||
[inv setArgument: &aSelector atIndex: 2];
|
||||
[self BCPforwardPrivateInvocation: inv];
|
||||
[inv getReturnValue: &result];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) conformsToProtocol: (Protocol *)aProtocol
|
||||
{
|
||||
NSMethodSignature *ms;
|
||||
NSInvocation *inv;
|
||||
SEL selector = @selector (conformsToProtocol:);
|
||||
BOOL result;
|
||||
|
||||
if ([super conformsToProtocol: aProtocol] == YES)
|
||||
return YES;
|
||||
|
||||
ms = [self methodSignatureForSelector: selector];
|
||||
inv = [NSInvocation invocationWithMethodSignature: ms];
|
||||
[inv setSelector: selector];
|
||||
[inv setTarget: self];
|
||||
[inv setArgument: &aProtocol atIndex: 2];
|
||||
[self BCPforwardPrivateInvocation: inv];
|
||||
[inv getReturnValue: &result];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Attention - this will be forwarded as any normal method !
|
||||
The return is not a NSString, but an NSArray of dictionaries with
|
||||
the responses of the various servers ! */
|
||||
- (NSString *) description
|
||||
{
|
||||
NSMethodSignature *ms;
|
||||
NSInvocation *inv;
|
||||
SEL selector = @selector (description);
|
||||
NSString *result;
|
||||
|
||||
ms = [self methodSignatureForSelector: selector];
|
||||
inv = [NSInvocation invocationWithMethodSignature: ms];
|
||||
[inv setSelector: selector];
|
||||
[inv setTarget: self];
|
||||
[self BCPforwardIdInvocation: inv];
|
||||
[inv getReturnValue: &result];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* who cares but anyway */
|
||||
- (BOOL) isProxy
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
@end
|
79
EcHost.h
Normal file
79
EcHost.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
|
||||
#import <Foundation/NSHost.h>
|
||||
|
||||
@class NSDictionary;
|
||||
@class NSString;
|
||||
|
||||
/**
|
||||
* <p>This category provides additional methods to standardise host names so
|
||||
* that software can consistently refer to a host by a single well known
|
||||
* name.<br />
|
||||
* This mechanism, as well as ensuring naming consistency, can be used to
|
||||
* work with logical names for hosts when the actual naming of the hosts
|
||||
* (ie in the domain name system) is not under your control.
|
||||
* </p>
|
||||
* <p>This operates by managing a map from the various names and addresses a
|
||||
* host may be known by, to a single well known name. This host name may,
|
||||
* but need not, be a public domain name.
|
||||
* </p>
|
||||
* <p>The well known name methods are thread-safe, and on initial use the
|
||||
* NSUserDefaults system is queried to set up two well known names
|
||||
* automatically:<br />
|
||||
* The value of EcHostCurrentName specifies the well known name for the
|
||||
* current host (the machine on which the software is running).<br />
|
||||
* The value of EcHostControlName specifies the well known name for the
|
||||
* control host (the machine on which control functions for your software
|
||||
* are centralised). If this is specified without EcHostControlDomain,
|
||||
* it is ignored and the well known name of the current host is used.<br />
|
||||
* The value of EcHostControlDomain specifies the fully qualified domain
|
||||
* name of the control host. If it is specified without EcHostControlName,
|
||||
* then it is used as the well known name for the control host.<br />
|
||||
* NB. the defaults system is accessed via EcUserDefaults, so if a
|
||||
* defaults prefix other than Ec has been set, these keys will use that
|
||||
* alternative prefix.
|
||||
* </p>
|
||||
*/
|
||||
@interface NSHost (EcHost)
|
||||
|
||||
/** Returns the well known name of the 'control' host as obtained from the
|
||||
* NSUserDefaults system.<br />
|
||||
* If EcHostControlName and EcHostControlDomain are both defined,
|
||||
* the well known name is the string specified by EcHostControlName.<br />
|
||||
* If EcHostControlDomain is defined, the well known name is the string
|
||||
* specified by it.<br />
|
||||
* If neither is defined, but EcHostCurrentName is defined, then the well
|
||||
* known name is the string specified by that default.<br />
|
||||
* Otherwise, the well known name is set to an arbitrarily selected name
|
||||
* of the current machine.
|
||||
*/
|
||||
+ (NSString*) controlWellKnownName;
|
||||
|
||||
/** Returns a host previously established as having the well known name,
|
||||
* or nil if no such association exists.
|
||||
*/
|
||||
+ (NSHost*) hostWithWellKnownName: (NSString*)aName;
|
||||
|
||||
/** Establishes mappings from a variety of host names (the dictionary keys)
|
||||
* to well known names (the dictionary values).<br />
|
||||
* The keys and values may be (and often are) identical in the case where
|
||||
* the well known name for a host is the same as one of its normal names
|
||||
* (eg its fully qualified domain name).<br />
|
||||
* It is possible to set a well known name for a host which does not yet
|
||||
* exist ... in which case the mapping will be established and will take
|
||||
* effect later, when the host is set up.
|
||||
*/
|
||||
+ (void) setWellKnownNames: (NSDictionary*)map;
|
||||
|
||||
/** Sets the well known name for the receiver.<br />
|
||||
* This replaces any previous well known name for the receiver and, if the
|
||||
* name is already in use, removes any associations of that well known name
|
||||
* with other hosts.
|
||||
*/
|
||||
- (void) setWellKnownName: (NSString*)aName;
|
||||
|
||||
/** Returns the well known name for the receiver (or any name of the receiver
|
||||
* if no well known name has been set for it).
|
||||
*/
|
||||
- (NSString*) wellKnownName;
|
||||
@end
|
||||
|
285
EcHost.m
Normal file
285
EcHost.m
Normal file
|
@ -0,0 +1,285 @@
|
|||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSLock.h>
|
||||
#import <Foundation/NSRunLoop.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSThread.h>
|
||||
|
||||
#import "EcUserDefaults.h"
|
||||
#import "EcHost.h"
|
||||
|
||||
static NSRecursiveLock *lock = nil;
|
||||
static NSMutableDictionary *fromWellKnown = nil;
|
||||
static NSMutableDictionary *toWellKnown = nil;
|
||||
static NSString *controlName = nil;
|
||||
|
||||
@implementation NSHost (EcHost)
|
||||
|
||||
+ (void) _EcHostSetup
|
||||
{
|
||||
if (nil == lock)
|
||||
{
|
||||
if (YES == [NSThread isMainThread])
|
||||
{
|
||||
NSUserDefaults *defs;
|
||||
NSString *name;
|
||||
NSHost *host;
|
||||
NSRecursiveLock *l;
|
||||
|
||||
/* Create the lock locked ... until it is unlocked all other
|
||||
* threads are waiting for this method to complete, and we
|
||||
* can do what we like.
|
||||
*/
|
||||
l = [NSRecursiveLock new];
|
||||
[l lock];
|
||||
lock = l;
|
||||
fromWellKnown = [NSMutableDictionary alloc];
|
||||
toWellKnown = [NSMutableDictionary alloc];
|
||||
|
||||
/* Perform initial setup of control host and current host
|
||||
* from user defaults system.
|
||||
*/
|
||||
defs = [NSUserDefaults prefixedDefaults];
|
||||
if (nil == defs)
|
||||
{
|
||||
defs = [NSUserDefaults userDefaultsWithPrefix: nil
|
||||
strict: NO];
|
||||
}
|
||||
name = [defs stringForKey: @"HostControlName"];
|
||||
if (nil != name && nil != [defs stringForKey: @"HostControlDomain"])
|
||||
{
|
||||
/* Use mapping from domain name to well known name.
|
||||
*/
|
||||
controlName = [name copy];
|
||||
name = [defs stringForKey: @"HostControlDomain"];
|
||||
}
|
||||
else if (nil != (name = [defs stringForKey: @"HostControlDomain"]))
|
||||
{
|
||||
/* Use domain name as the known name.
|
||||
*/
|
||||
controlName = [name copy];
|
||||
}
|
||||
if (nil != name)
|
||||
{
|
||||
host = [self hostWithName: name];
|
||||
if (nil == host)
|
||||
{
|
||||
/* No such host ... set up name mapping in case
|
||||
* the host becomes available later.
|
||||
*/
|
||||
[toWellKnown setObject: controlName forKey: name];
|
||||
[fromWellKnown setObject: name forKey: controlName];
|
||||
}
|
||||
else
|
||||
{
|
||||
[host setWellKnownName: controlName];
|
||||
}
|
||||
}
|
||||
host = [self currentHost];
|
||||
name = [defs stringForKey: @"HostCurrentName"];
|
||||
if (nil == name)
|
||||
{
|
||||
/* If the current host is the control host, we may have the
|
||||
* well known name set already, but if we don't this method
|
||||
* will select an arbitrary name that we can use.
|
||||
*/
|
||||
name = [host wellKnownName];
|
||||
}
|
||||
[host setWellKnownName: name];
|
||||
if (nil == controlName)
|
||||
{
|
||||
/* use current host as the control host.
|
||||
*/
|
||||
controlName = [name copy];
|
||||
}
|
||||
[lock unlock];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSArray *modes;
|
||||
|
||||
modes = [NSArray arrayWithObject: NSDefaultRunLoopMode];
|
||||
[self performSelectorOnMainThread: _cmd
|
||||
withObject: nil
|
||||
waitUntilDone: YES
|
||||
modes: modes];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString*) controlWellKnownName
|
||||
{
|
||||
if (nil == lock) [self _EcHostSetup];
|
||||
return controlName;
|
||||
}
|
||||
|
||||
+ (NSHost*) hostWithWellKnownName: (NSString*)aName
|
||||
{
|
||||
NSHost *found = nil;
|
||||
|
||||
if (nil == lock) [self _EcHostSetup];
|
||||
if (YES == [aName isKindOfClass: [NSString class]])
|
||||
{
|
||||
[lock lock];
|
||||
aName = [[[fromWellKnown objectForKey: aName] retain] autorelease];
|
||||
[lock unlock];
|
||||
if (nil != aName)
|
||||
{
|
||||
found = [self hostWithName: aName];
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
+ (void) setWellKnownNames: (NSDictionary*)map
|
||||
{
|
||||
if (nil == lock) [self _EcHostSetup];
|
||||
if ([map isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
NSEnumerator *e;
|
||||
NSString *k;
|
||||
|
||||
e = [map keyEnumerator];
|
||||
while (nil != (k = [e nextObject]))
|
||||
{
|
||||
NSString *v = [map objectForKey: k];
|
||||
|
||||
if ([k isKindOfClass: [NSString class]]
|
||||
&& [v isKindOfClass: [NSString class]])
|
||||
{
|
||||
NSHost *h = [self hostWithName: k];
|
||||
|
||||
if (nil == h)
|
||||
{
|
||||
NSEnumerator *e;
|
||||
NSString *name;
|
||||
|
||||
/* No such host ... set up name mapping in case
|
||||
* the host becomes available later.
|
||||
*/
|
||||
|
||||
[lock lock];
|
||||
/* Remove any existing names which map to this new
|
||||
* well known name.
|
||||
*/
|
||||
e = [[toWellKnown allKeys] objectEnumerator];
|
||||
while (nil != (name = [e nextObject]))
|
||||
{
|
||||
NSString *wellKnown;
|
||||
|
||||
wellKnown = [toWellKnown objectForKey: name];
|
||||
if ([wellKnown isEqualToString: v])
|
||||
{
|
||||
[toWellKnown removeObjectForKey: name];
|
||||
}
|
||||
}
|
||||
/* Set up the specified mappings to and from the new
|
||||
* well known name and its normal host name.
|
||||
*/
|
||||
[toWellKnown setObject: v forKey: k];
|
||||
[fromWellKnown setObject: k forKey: v];
|
||||
[lock unlock];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We have found a host with the specified names ...
|
||||
* so set the well known name for it.
|
||||
*/
|
||||
[h setWellKnownName: v];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setWellKnownName: (NSString*)aName
|
||||
{
|
||||
if (nil == lock) [[self class] _EcHostSetup];
|
||||
if ([aName isKindOfClass: [NSString class]])
|
||||
{
|
||||
NSEnumerator *e;
|
||||
NSString *name;
|
||||
|
||||
[lock lock];
|
||||
|
||||
/* Set a mapping to this host from the well known name.
|
||||
*/
|
||||
name = [self name];
|
||||
if (nil == name)
|
||||
{
|
||||
name = [self address];
|
||||
}
|
||||
[fromWellKnown setObject: name forKey: aName];
|
||||
|
||||
/* Remove any old mappings to this well known name.
|
||||
*/
|
||||
e = [[toWellKnown allKeys] objectEnumerator];
|
||||
while (nil != (name = [e nextObject]))
|
||||
{
|
||||
if ([[toWellKnown objectForKey: name] isEqualToString: aName])
|
||||
{
|
||||
[toWellKnown removeObjectForKey: name];
|
||||
}
|
||||
}
|
||||
|
||||
/* Add mappings to the well known name from any of our names.
|
||||
*/
|
||||
e = [[self names] objectEnumerator];
|
||||
while (nil != (name = [e nextObject]))
|
||||
{
|
||||
[toWellKnown setObject: aName forKey: name];
|
||||
}
|
||||
|
||||
/* Add mappings to the well known name from any of our addresses.
|
||||
*/
|
||||
e = [[self addresses] objectEnumerator];
|
||||
while (nil != (name = [e nextObject]))
|
||||
{
|
||||
[toWellKnown setObject: aName forKey: name];
|
||||
}
|
||||
|
||||
[lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*) wellKnownName
|
||||
{
|
||||
NSString *found;
|
||||
|
||||
if (nil == lock) [[self class] _EcHostSetup];
|
||||
[lock lock];
|
||||
found = [[[toWellKnown objectForKey: [self name]] retain] autorelease];
|
||||
if (nil == found)
|
||||
{
|
||||
NSEnumerator *e;
|
||||
NSString *name;
|
||||
|
||||
e = [[self names] objectEnumerator];
|
||||
while (nil == found && nil != (name = [e nextObject]))
|
||||
{
|
||||
found = [[[toWellKnown objectForKey: name] retain] autorelease];
|
||||
}
|
||||
if (nil == found)
|
||||
{
|
||||
e = [[self addresses] objectEnumerator];
|
||||
while (nil == found && nil != (name = [e nextObject]))
|
||||
{
|
||||
found = [[[toWellKnown objectForKey: name] retain] autorelease];
|
||||
}
|
||||
}
|
||||
}
|
||||
[lock unlock];
|
||||
if (nil == found)
|
||||
{
|
||||
found = [self name];
|
||||
if (nil == found)
|
||||
{
|
||||
found = [self address];
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
@end
|
||||
|
37
EcLogger.h
Normal file
37
EcLogger.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
|
||||
#ifndef _ECLOGGER_H
|
||||
#define _ECLOGGER_H
|
||||
|
||||
@interface EcLogger : NSObject <CmdPing>
|
||||
{
|
||||
NSRecursiveLock *lock;
|
||||
NSDate *last;
|
||||
NSTimer *timer;
|
||||
unsigned interval;
|
||||
unsigned size;
|
||||
NSMutableString *message;
|
||||
EcLogType type;
|
||||
NSString *key;
|
||||
NSString *flushKey;
|
||||
NSString *serverKey;
|
||||
NSString *serverName;
|
||||
BOOL inFlush;
|
||||
BOOL externalFlush;
|
||||
BOOL registered;
|
||||
BOOL pendingFlush;
|
||||
}
|
||||
+ (EcLogger*) loggerForType: (EcLogType)t;
|
||||
- (void) cmdGnip: (id <CmdPing>)from
|
||||
sequence: (unsigned)num
|
||||
extra: (NSData*)data;
|
||||
- (void) cmdMadeConnectionToServer: (NSString*)name;
|
||||
- (void) cmdPing: (id <CmdPing>)from
|
||||
sequence: (unsigned)num
|
||||
extra: (NSData*)data;
|
||||
- (void) flush;
|
||||
- (void) log: (NSString*)fmt arguments: (va_list)args;
|
||||
- (void) update;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
514
EcLogger.m
Normal file
514
EcLogger.m
Normal file
|
@ -0,0 +1,514 @@
|
|||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <Foundation/NSDebug.h>
|
||||
|
||||
#include "EcProcess.h"
|
||||
#include "EcLogger.h"
|
||||
|
||||
@implementation EcLogger
|
||||
|
||||
static NSLock *loggersLock;
|
||||
static NSMutableArray *loggers;
|
||||
static NSArray *modes;
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
if (self == [EcLogger class])
|
||||
{
|
||||
id objects[1];
|
||||
|
||||
loggersLock = [NSLock new];
|
||||
loggers = [[NSMutableArray alloc] initWithCapacity: 6];
|
||||
objects[0] = NSDefaultRunLoopMode;
|
||||
modes = [[NSArray alloc] initWithObjects: objects count: 1];
|
||||
}
|
||||
}
|
||||
|
||||
+ (EcLogger*) loggerForType: (EcLogType)t
|
||||
{
|
||||
unsigned count;
|
||||
EcLogger *logger;
|
||||
|
||||
[loggersLock lock];
|
||||
count = [loggers count];
|
||||
while (count-- > 0)
|
||||
{
|
||||
logger = [loggers objectAtIndex: count];
|
||||
|
||||
if (logger->type == t)
|
||||
{
|
||||
[loggersLock unlock];
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
logger = [[EcLogger alloc] init];
|
||||
if (logger != nil)
|
||||
{
|
||||
logger->lock = [NSRecursiveLock new];
|
||||
logger->type = t;
|
||||
logger->key = [cmdLogKey(t) copy];
|
||||
logger->flushKey
|
||||
= [[NSString alloc] initWithFormat: @"BS%@Flush", logger->key];
|
||||
logger->serverKey
|
||||
= [[NSString alloc] initWithFormat: @"BS%@Server", logger->key];
|
||||
logger->interval = 10;
|
||||
logger->size = 8 * 1024;
|
||||
logger->message = [[NSMutableString alloc] initWithCapacity: 2048];
|
||||
[EcProc setCmdUpdate: logger withMethod: @selector(update)];
|
||||
[logger update];
|
||||
[loggers addObject: logger];
|
||||
RELEASE(logger);
|
||||
}
|
||||
[loggersLock unlock];
|
||||
return logger;
|
||||
}
|
||||
|
||||
/* Should only be called on main thread, but doesn't matter.
|
||||
*/
|
||||
- (void) cmdGnip: (id <CmdPing>)from
|
||||
sequence: (unsigned)num
|
||||
extra: (NSData*)data
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* When connecting to a logging server, we need to register so it
|
||||
* knows who we are.
|
||||
* Should only be called on main thread.
|
||||
*/
|
||||
- (void) cmdMadeConnectionToServer: (NSString*)name
|
||||
{
|
||||
id<CmdLogger> server;
|
||||
|
||||
server = (id<CmdLogger>)[EcProc server: name];
|
||||
[server registerClient: self name: cmdLogName()];
|
||||
}
|
||||
|
||||
/* Should only be called on main thread, but doesn't matter.
|
||||
*/
|
||||
- (void) cmdPing: (id <CmdPing>)from
|
||||
sequence: (unsigned)num
|
||||
extra: (NSData*)data
|
||||
{
|
||||
[from cmdGnip: self sequence: num extra: nil];
|
||||
}
|
||||
|
||||
/* Should only be called on main thread.
|
||||
*/
|
||||
- (oneway void) cmdQuit: (int)status
|
||||
{
|
||||
[EcProc cmdQuit: status];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[self flush];
|
||||
[timer invalidate];
|
||||
RELEASE(key);
|
||||
RELEASE(flushKey);
|
||||
RELEASE(serverKey);
|
||||
RELEASE(serverName);
|
||||
RELEASE(message);
|
||||
RELEASE(lock);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString*) description
|
||||
{
|
||||
NSMutableString *s = [NSMutableString stringWithCapacity: 256];
|
||||
|
||||
[lock lock];
|
||||
if (size == 0)
|
||||
{
|
||||
[s appendFormat: @"%@ output is immediate.\n", key];
|
||||
}
|
||||
else if (interval > 0)
|
||||
{
|
||||
[s appendFormat: @"%@ flushed every %u seconds", key, interval];
|
||||
[s appendFormat: @" or with a %u byte buffer.\n", size];
|
||||
if (timer != nil)
|
||||
{
|
||||
[s appendFormat: @"Next flush - %@\n", [timer fireDate]];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[s appendFormat: @"%@ flushed with a %u byte buffer.\n", key, size];
|
||||
}
|
||||
[lock unlock];
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal flush operation ... writes data out from us, but
|
||||
* doesn't try any further.
|
||||
*/
|
||||
- (void) _flush
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
if (inFlush == YES)
|
||||
{
|
||||
return;
|
||||
}
|
||||
[lock lock];
|
||||
if (inFlush == NO && (len = [message length]) > 0)
|
||||
{
|
||||
BOOL ok = YES;
|
||||
NSData *buf;
|
||||
NSString *str;
|
||||
|
||||
inFlush = YES;
|
||||
str = [message copy];
|
||||
[message setString: @""];
|
||||
[lock unlock];
|
||||
|
||||
buf = [str dataUsingEncoding: [NSString defaultCStringEncoding]];
|
||||
if (buf == nil)
|
||||
{
|
||||
buf = [str dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
fwrite([buf bytes], 1, [buf length], stderr);
|
||||
if (LT_DEBUG != type)
|
||||
{
|
||||
if (nil == serverName)
|
||||
{
|
||||
id<CmdLogger> server;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
server = (id<CmdLogger>)[EcProc cmdNewServer];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
server = nil;
|
||||
NSLog(@"Exception contacting Command server: %@\n",
|
||||
localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
if (server != nil)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
[server logMessage: str
|
||||
type: type
|
||||
for: EcProc];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Exception sending info to logger: %@",
|
||||
localException);
|
||||
ok = NO;
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = NO;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
id<CmdLogger> server;
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
server = (id<CmdLogger>)
|
||||
[EcProc server: serverName];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
server = nil;
|
||||
NSLog(@"Exception contacting logging server: %@\n",
|
||||
localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
if (server != nil)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
[server logMessage: str
|
||||
type: type
|
||||
for: self];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Exception sending info to %@: %@",
|
||||
serverName, localException);
|
||||
ok = NO;
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (NO == ok)
|
||||
{
|
||||
if (serverName == nil)
|
||||
{
|
||||
NSLog(@"Unable to log to Command server - %@", str);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Unable to log to %@ - %@", serverName, str);
|
||||
}
|
||||
/*
|
||||
* Flush any messages that might have been generated by
|
||||
* (or during) our failed attempt to contact server.
|
||||
*/
|
||||
[lock lock];
|
||||
if ([message length] > 0)
|
||||
{
|
||||
NSString *tmp = [message copy];
|
||||
|
||||
[message setString: @""];
|
||||
NSLog(@"%@", tmp);
|
||||
[tmp release];
|
||||
}
|
||||
[lock unlock];
|
||||
}
|
||||
RELEASE(str);
|
||||
inFlush = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
[lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) _externalFlush: (id)dummy
|
||||
{
|
||||
if (externalFlush == NO)
|
||||
{
|
||||
externalFlush = YES;
|
||||
|
||||
[self _flush];
|
||||
if (LT_DEBUG != type)
|
||||
{
|
||||
id<CmdLogger> server;
|
||||
|
||||
if (serverName == nil)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
server = (id<CmdLogger>)[EcProc cmdNewServer];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
server = nil;
|
||||
NSLog(@"Exception contacting Command server: %@\n",
|
||||
localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
server = (id<CmdLogger>)
|
||||
[EcProc server: serverName];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
server = nil;
|
||||
NSLog(@"Exception contacting logging server: %@\n",
|
||||
localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
if (server != nil)
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
[server flush]; // Force round trip.
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Exception flushing info to %@: %@",
|
||||
serverName, localException);
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
}
|
||||
externalFlush = NO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* External flush operation ... writes out data and asks any server
|
||||
* we write to to flush its data out too.
|
||||
*/
|
||||
- (void) flush
|
||||
{
|
||||
[self performSelectorOnMainThread: @selector(_externalFlush:)
|
||||
withObject: nil
|
||||
waitUntilDone: YES
|
||||
modes: modes];
|
||||
}
|
||||
|
||||
- (void) _scheduleFlush: (id)reset
|
||||
{
|
||||
/* A non-nil value of reset means that we should reset to do
|
||||
* a flush real soon.
|
||||
*/
|
||||
[lock lock];
|
||||
if (reset != nil)
|
||||
{
|
||||
if (timer != nil && [[timer fireDate] timeIntervalSinceNow] > 0.001)
|
||||
{
|
||||
[timer invalidate];
|
||||
timer = nil;
|
||||
}
|
||||
if (timer == nil)
|
||||
{
|
||||
timer = [NSTimer scheduledTimerWithTimeInterval: 0.0001
|
||||
target: self selector: @selector(_timeout:)
|
||||
userInfo: nil repeats: NO];
|
||||
}
|
||||
}
|
||||
else if ([message length] >= size)
|
||||
{
|
||||
/*
|
||||
* Buffer too large - schedule immediate flush.
|
||||
*/
|
||||
if (timer != nil && [[timer fireDate] timeIntervalSinceNow] > 0.001)
|
||||
{
|
||||
[timer invalidate];
|
||||
timer = nil;
|
||||
}
|
||||
if (timer == nil)
|
||||
{
|
||||
timer = [NSTimer scheduledTimerWithTimeInterval: 0.0001
|
||||
target: self selector: @selector(_timeout:)
|
||||
userInfo: nil repeats: NO];
|
||||
}
|
||||
}
|
||||
else if (interval > 0 && timer == nil)
|
||||
{
|
||||
/*
|
||||
* No timer running - so schedule one to output the debug info.
|
||||
*/
|
||||
timer = [NSTimer scheduledTimerWithTimeInterval: interval
|
||||
target: self selector: @selector(_timeout:)
|
||||
userInfo: nil repeats: NO];
|
||||
}
|
||||
[lock unlock];
|
||||
}
|
||||
|
||||
- (void) log: (NSString*)fmt arguments: (va_list)args
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(arp);
|
||||
NSString *format;
|
||||
NSString *text;
|
||||
|
||||
format = cmdLogFormat(type, fmt);
|
||||
text = [NSString stringWithFormat: format arguments: args];
|
||||
|
||||
[lock lock];
|
||||
if (message == nil)
|
||||
{
|
||||
message = [[NSMutableString alloc] initWithCapacity: 1024];
|
||||
}
|
||||
[message appendString: text];
|
||||
if ([message length] >= size || (interval > 0 && timer == nil))
|
||||
{
|
||||
[self performSelectorOnMainThread: @selector(_scheduleFlush:)
|
||||
withObject: nil
|
||||
waitUntilDone: NO
|
||||
modes: modes];
|
||||
}
|
||||
[lock unlock];
|
||||
RELEASE(arp);
|
||||
}
|
||||
|
||||
/* Should only be called on main thread.
|
||||
*/
|
||||
- (void) _timeout: (NSTimer*)t
|
||||
{
|
||||
if (t == nil)
|
||||
{
|
||||
[timer invalidate];
|
||||
}
|
||||
timer = nil;
|
||||
[self _flush];
|
||||
}
|
||||
|
||||
/* Should only be called on main thread.
|
||||
*/
|
||||
- (void) update
|
||||
{
|
||||
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
||||
NSString *str;
|
||||
|
||||
/*
|
||||
* If there is a server specified for this debug logger, we want it
|
||||
* registered so we can use it - otherwise we will default to using
|
||||
* the Command server for logging.
|
||||
*/
|
||||
str = [defs stringForKey: serverKey];
|
||||
if ([str isEqual: @""] == YES)
|
||||
{
|
||||
str = nil; // An empty string means no server is used.
|
||||
}
|
||||
if ([serverName isEqual: str] == NO)
|
||||
{
|
||||
if (serverName != nil)
|
||||
{
|
||||
[EcProc removeServerFromList: serverName];
|
||||
}
|
||||
ASSIGN(serverName, str);
|
||||
if (serverName != nil)
|
||||
{
|
||||
[EcProc addServerToList: serverName for: self];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the program to flush at intervals or at
|
||||
* a particular buffer size (or both)?
|
||||
*/
|
||||
str = [defs stringForKey: flushKey];
|
||||
if (str == nil)
|
||||
{
|
||||
str = [defs stringForKey: @"BSDefaultFlush"]; // Default settings.
|
||||
if (str == nil)
|
||||
{
|
||||
str = [defs stringForKey: @"BSDebugFlush"]; // Backward compat.
|
||||
}
|
||||
}
|
||||
if (str != nil)
|
||||
{
|
||||
NSScanner *scanner = [NSScanner scannerWithString: str];
|
||||
int i;
|
||||
|
||||
if ([scanner scanInt: &i] == YES)
|
||||
{
|
||||
if (i < 0)
|
||||
interval = 0;
|
||||
else
|
||||
interval = i;
|
||||
}
|
||||
if (([scanner scanString: @":" intoString: 0] == YES)
|
||||
&& ([scanner scanInt: &i] == YES))
|
||||
{
|
||||
if (i < 0)
|
||||
size = 0;
|
||||
else
|
||||
size = i*1024;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure new values take effect real soon.
|
||||
*/
|
||||
[self performSelectorOnMainThread: @selector(_scheduleFlush:)
|
||||
withObject: self
|
||||
waitUntilDone: NO
|
||||
modes: modes];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
662
EcProcess.h
Normal file
662
EcProcess.h
Normal file
|
@ -0,0 +1,662 @@
|
|||
#ifndef INCLUDED_ECPROCESS_H
|
||||
#define INCLUDED_ECPROCESS_H
|
||||
|
||||
/**
|
||||
<chapter>
|
||||
<heading>The EcProcess class</heading>
|
||||
<p>
|
||||
The EcProcess class provides basic configuration, control, logging,
|
||||
and inter-process connection systems for servers processors.
|
||||
</p>
|
||||
<section>
|
||||
<heading>Configuration options</heading>
|
||||
<p>
|
||||
This class understands two groups of configuration options ...
|
||||
those that take effect at process startup, and must therefore
|
||||
be supplied on the command-line or in the defaults system, and
|
||||
those which can take effect at any time, in which case the
|
||||
values supplied by the configuration system will take
|
||||
precedence over command line and defaults database settings.
|
||||
</p>
|
||||
<subsect>
|
||||
<heading>startup settings</heading>
|
||||
<deflist>
|
||||
<term>EcDebug-</term>
|
||||
<desc>
|
||||
Any key of the form EcDebug-xxx turns on the xxx debug level
|
||||
on program startup.
|
||||
</desc>
|
||||
<term>EcMemory</term>
|
||||
<desc>
|
||||
This boolean value determines whether statistics on creation
|
||||
and destruction of objects are maintained.<br />
|
||||
This may be set on the command line or in Control.plist, but
|
||||
may be overridden by using the 'release' command in the
|
||||
Console program.
|
||||
</desc>
|
||||
<term>EcRelease</term>
|
||||
<desc>
|
||||
This boolean value determines whether checks for memory problems
|
||||
caused by release an object too many times are done. Turning
|
||||
this on has a big impact on program performance and is not
|
||||
recommended except for debugging crashes and other memory
|
||||
issues.<br />
|
||||
This may be set on the command line or in Control.plist, but
|
||||
may be overridden by using the 'release' command in the
|
||||
Console program.
|
||||
</desc>
|
||||
<term>EcTesting</term>
|
||||
<desc>
|
||||
This boolean value determines whether the server is running in
|
||||
test mode (which may enable extra logging or prevent the server
|
||||
from comm,unicating with live systems etc ... the actual
|
||||
behavior is server dependent).<br />
|
||||
This may be set on the command line or in Control.plist, but
|
||||
may be overridden by using the 'testing' command in the
|
||||
Console program.
|
||||
</desc>
|
||||
<term>EcTransient</term>
|
||||
<desc>
|
||||
This boolean option is used to specify that the program
|
||||
should not be restarted automatically by the Command
|
||||
server if/when it disconnects from that server.
|
||||
</desc>
|
||||
</deflist>
|
||||
</subsect>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "EcAlarmDestination.h"
|
||||
|
||||
@class NSFileHandle;
|
||||
|
||||
typedef enum {
|
||||
LT_DEBUG, /* Debug message - internal info. */
|
||||
LT_WARNING, /* Warning - possibly a real problem. */
|
||||
LT_ERROR, /* Error - needs dealing with. */
|
||||
LT_AUDIT, /* Not a problem but needs logging. */
|
||||
LT_ALERT, /* Severe - needs immediate attention. */
|
||||
} EcLogType;
|
||||
|
||||
|
||||
/**
|
||||
* A 'ping' can be sent from or to any process to check that it is alive.
|
||||
* A 'gnip' should be sent in response to each 'ping'
|
||||
* Each 'ping' carries a sequence number which should be echoed in its 'gnip'.
|
||||
* Optionally, the 'ping' can also transport a serialized property-list
|
||||
* to provide additional data or instructions - the value of which is
|
||||
* dependent on the program being pinged - normally this value is nil.
|
||||
*/
|
||||
@protocol CmdPing
|
||||
- (oneway void) cmdGnip: (id <CmdPing>)from
|
||||
sequence: (unsigned)num
|
||||
extra: (in bycopy NSData*)data;
|
||||
- (oneway void) cmdPing: (id <CmdPing>)from
|
||||
sequence: (unsigned)num
|
||||
extra: (in bycopy NSData*)data;
|
||||
@end
|
||||
|
||||
/** The CmdConfig protocol is needed by objects that send and receive
|
||||
* configuarion information.
|
||||
*/
|
||||
@protocol CmdConfig
|
||||
- (oneway void) requestConfigFor: (id<CmdConfig>)c;
|
||||
- (oneway void) updateConfig: (in bycopy NSData*)info;
|
||||
@end
|
||||
|
||||
/** Messages that the Command server may send to clients.
|
||||
*/
|
||||
@protocol CmdClient <CmdPing,CmdConfig>
|
||||
- (oneway void) cmdMesgData: (in bycopy NSData*)dat from: (NSString*)name;
|
||||
- (oneway void) cmdQuit: (int)status;
|
||||
@end
|
||||
|
||||
/** Messages a Command logging process can be expected to handle.
|
||||
*/
|
||||
@protocol CmdLogger <CmdClient>
|
||||
- (void) flush;
|
||||
- (oneway void) logMessage: (NSString*)msg
|
||||
type: (EcLogType)t
|
||||
name: (NSString*)c;
|
||||
- (oneway void) logMessage: (NSString*)msg
|
||||
type: (EcLogType)t
|
||||
for: (id)o;
|
||||
- (bycopy NSData*) registerClient: (id)c
|
||||
name: (NSString*)n;
|
||||
- (void) unregisterByObject: (id)obj;
|
||||
- (void) unregisterByName: (NSString*)n;
|
||||
@end
|
||||
|
||||
@protocol Console <NSObject>
|
||||
- (oneway void) information: (NSString*)txt;
|
||||
@end
|
||||
|
||||
/**
|
||||
* Messages that clients may send to the server.
|
||||
* NB. The 'registerClient...' message must be sent first.
|
||||
*/
|
||||
@protocol Command <CmdLogger,CmdConfig,EcAlarmDestination>
|
||||
- (oneway void) alarm: (in bycopy EcAlarm*)alarm;
|
||||
- (oneway void) command: (in bycopy NSData*)dat
|
||||
to: (NSString*)t
|
||||
from: (NSString*)f;
|
||||
- (bycopy NSData*) registerClient: (id<CmdClient>)c
|
||||
name: (NSString*)n
|
||||
transient: (BOOL)t;
|
||||
- (oneway void) reply: (NSString*)msg
|
||||
to: (NSString*)n
|
||||
from: (NSString*)c;
|
||||
/** Shut down the Command server and all its clients */
|
||||
- (oneway void) terminate;
|
||||
/** An exceptional method and can be used without registering first
|
||||
* (ie, can be used by anyone, not only clients of the Command server).
|
||||
* It's meant to be used remotely by java servlets, and all sort of
|
||||
* software running on the machine and which is *not* a full Command
|
||||
* client (ie, a subclass of EcProcess) but which still wants to retrieve
|
||||
* configuration from a central location (the Control/Command servers).
|
||||
* NB: The configuration might change later on, so you must not cache
|
||||
* the configuration after asking for it, but rather ask for it each
|
||||
* time your software needs it.
|
||||
*/
|
||||
- (bycopy NSData *) configurationFor: (NSString *)name;
|
||||
@end
|
||||
|
||||
/*
|
||||
* Messages that clients may send to the server.
|
||||
*/
|
||||
@protocol Control <CmdPing,CmdConfig,EcAlarmDestination>
|
||||
- (oneway void) alarm: (in bycopy EcAlarm*)alarm;
|
||||
- (oneway void) command: (in bycopy NSData*)cmd
|
||||
from: (NSString*)f;
|
||||
- (oneway void) domanage: (NSString*)name;
|
||||
- (oneway void) information: (NSString*)msg
|
||||
type: (EcLogType)t
|
||||
to: (NSString*)to
|
||||
from: (NSString*)from;
|
||||
- (bycopy NSData*) registerCommand: (id<Command>)c
|
||||
name: (NSString*)n;
|
||||
- (bycopy NSString*) registerConsole: (id<Console>)c
|
||||
name: (NSString*)n
|
||||
pass: (NSString*)p;
|
||||
- (oneway void) reply: (NSString*)msg
|
||||
to: (NSString*)n
|
||||
from: (NSString*)c;
|
||||
- (oneway void) servers: (in bycopy NSData*)a
|
||||
on: (id<Command>)s;
|
||||
- (oneway void) unmanage: (NSString*)name;
|
||||
- (void) unregister: (id)o;
|
||||
@end
|
||||
|
||||
/*
|
||||
* Useful functions -
|
||||
*/
|
||||
extern void cmdSetHome(NSString *home);
|
||||
extern NSString* cmdLogsDir(NSString *date);
|
||||
extern NSString* cmdLogKey(EcLogType t);
|
||||
extern NSString* cmdLogName();
|
||||
extern NSString* cmdLogFormat(EcLogType t, NSString *fmt);
|
||||
|
||||
/* Return value of lat signal received, or zero.
|
||||
*/
|
||||
extern int cmdSignalled();
|
||||
|
||||
/* Set/get version/compilation date.
|
||||
*/
|
||||
extern NSString* cmdVersion(NSString *ver);
|
||||
|
||||
/**
|
||||
* Command line arguments -
|
||||
*
|
||||
*
|
||||
* <p>On startup, these are taken from the command line, or from the
|
||||
* local user defaults database of the person running the program.<br />
|
||||
* If the EcEffectiveUser specifies an alternative user, or the
|
||||
* program is able to read the database for the 'ecuser' user, then
|
||||
* the other values are read from the defaults database of that user.
|
||||
* </p>
|
||||
* <p>After startup, the command line arguments still take precedence,
|
||||
* but values retrieved from the network configuration system will
|
||||
* then override any read from the local user defaults database.
|
||||
* </p>
|
||||
* <p>Settings in the network configuration system will have no effect on
|
||||
* the following defaults which are used BEFORE the network configuration
|
||||
* can be read.
|
||||
* </p>
|
||||
* <deflist>
|
||||
* <term>EcDaemon</term>
|
||||
* <desc>To specify whether the program should run in the background
|
||||
* (boolean, YES if the program is to run as a daemon, NO otherwise).<br />
|
||||
* The value in the network configuration has no effect.
|
||||
* </desc>
|
||||
* <term>EcEffectiveUser</term>
|
||||
* <desc>To tell the server to change to being this user on startup.
|
||||
* defaults to 'ecuser', but the default can be overridden by specifying
|
||||
* '-DEC_EFFECTIVE_USER+@"username"' in the local.make make file.<br />
|
||||
* Set a value of '' or '*' to remain whoever runs the program
|
||||
* rather than changing.
|
||||
* </desc>
|
||||
* <term>EcInstance</term>
|
||||
* <desc>To set the program instance ID (an arbitrary string).<br />
|
||||
* If this is specified, the program name has a hyphen and the
|
||||
* id appended to it by the '-initWithDefaults:' method.
|
||||
* </desc>
|
||||
* </deflist>
|
||||
* <p>The following settings will be revised after startup to include the
|
||||
* values from the network configuration system.
|
||||
* </p>
|
||||
* <deflist>
|
||||
* <term>EcAuditLocal</term>
|
||||
* <desc>A boolean used to specify that audit information should
|
||||
* be logged locally rather than sending it to be logged centrally.<br />
|
||||
* Default value is NO.
|
||||
* </desc>
|
||||
* <term>EcAuditFlush</term>
|
||||
* <desc>A flush interval in seconds (optionally followed by a colon
|
||||
* and a buffer size in KiloBytes) to control flushing of audit logs.<br />
|
||||
* Setting an interval of zero or less disables flushing by timer.<br />
|
||||
* Setting a size of zero or less, disables buffering (so logs are
|
||||
* flushed immediately).
|
||||
* </desc>
|
||||
* <term>EcDebug-XXX</term>
|
||||
* <desc>A boolean used to ensure that the debug mode named 'XXX' is either
|
||||
* turned on or turned off. The value of 'XXX' must match
|
||||
* the name of a debug mode used by the program!
|
||||
* </desc>
|
||||
* <term>EcDebugLocal</term>
|
||||
* <desc>A boolean used to specify that debug information should
|
||||
* be logged locally rather than sending it to be logged centrally.<br />
|
||||
* Default value is YES.
|
||||
* </desc>
|
||||
* <term>EcDebugFlush</term>
|
||||
* <desc>A flush interval in seconds (optionally followed by a colon
|
||||
* and a buffer size in KiloBytes) to control flushing of debug logs.<br />
|
||||
* Setting an interval of zero or less disables flushing by timer.<br />
|
||||
* Setting a size of zero or less, disables buffering (so logs are
|
||||
* flushed immediately).
|
||||
* </desc>
|
||||
* <term>EcMemory</term>
|
||||
* <desc>A boolean used to ensure that monitoring of memory allocation is
|
||||
* turned on or turned off.
|
||||
* </desc>
|
||||
* <term>EcRelease</term>
|
||||
* <desc>A boolean used to specify whether the program should perform
|
||||
* sanity checks for retain/release combinations. Slows things down a lot!
|
||||
* </desc>
|
||||
* <term>EcTesting</term>
|
||||
* <desc>A boolean used to specify whether the program is running
|
||||
* in test mode or not.
|
||||
* </desc>
|
||||
* <term>EcWellKnownHostNames</term>
|
||||
* <desc>A dictionary mapping host names/address values to well known
|
||||
* names (the canonical values used by Command and Control).
|
||||
* </desc>
|
||||
* </deflist>
|
||||
*/
|
||||
@interface EcProcess : NSObject <CmdClient,EcAlarmDestination>
|
||||
|
||||
/** Return a short copyright notice ... subclasses should override.
|
||||
*/
|
||||
- (NSString*) prcCopyright;
|
||||
|
||||
/* Call these methods during initialisation of your instance
|
||||
* to set up automatic management of connections to servers.
|
||||
* You then access the servers by calling -(id)server: (NSString*)serverName.
|
||||
*/
|
||||
|
||||
- (void) addServerToList: (NSString*)serverName;
|
||||
- (void) addServerToList: (NSString*)serverName for: (id)anObject;
|
||||
- (void) removeServerFromList: (NSString*)serverName;
|
||||
|
||||
|
||||
/** Send a SEVERE error message to the server.
|
||||
*/
|
||||
- (void) cmdAlert: (NSString*)fmt, ...;
|
||||
|
||||
/** Archives debug log files into the specified subdirectory of the debug
|
||||
* logging directory. If subdir is nil then a subdirectory name corresponding
|
||||
* to the current date is generated and that subdirectory is created if
|
||||
* necessary.
|
||||
*/
|
||||
- (NSString*) cmdArchive: (NSString*)subdir;
|
||||
|
||||
/** Send a log message to the server.
|
||||
*/
|
||||
- (void) cmdAudit: (NSString*)fmt, ...;
|
||||
|
||||
/** Handles loss of connection to the server.
|
||||
*/
|
||||
- (id) cmdConnectionBecameInvalid: (NSNotification*)notification;
|
||||
|
||||
/** Returns the path to the data storage directory used by this process
|
||||
* to store files containing persistent information.
|
||||
*/
|
||||
- (NSString*) cmdDataDirectory;
|
||||
|
||||
/** Send a debug message - as long as the debug mode specified as 'type'
|
||||
* is currently set.
|
||||
*/
|
||||
- (void) cmdDbg: (NSString*)type msg: (NSString*)fmt, ...;
|
||||
|
||||
/** Send a debug message with debug mode 'defaultMode'.
|
||||
*/
|
||||
- (void) cmdDebug: (NSString*)fmt, ...;
|
||||
|
||||
/** Called whenever the user defaults are updated due to a central
|
||||
* configuration change (or another defaults system change).<br />
|
||||
* If you override this to handle configuration changes, don't forget
|
||||
* to call the superclass implementation.
|
||||
*/
|
||||
- (void) cmdDefaultsChanged: (NSNotification*)n;
|
||||
|
||||
/** Send an error message to the server.
|
||||
*/
|
||||
- (void) cmdError: (NSString*)fmt, ...;
|
||||
|
||||
/** Flush logging information
|
||||
*/
|
||||
- (void) cmdFlushLogs;
|
||||
|
||||
/** This message returns YES if the receiver is intended to be a client
|
||||
* of a Command server, and NO if it is a standalone process which does
|
||||
* not need to contact the Command server.<br />
|
||||
* The default implementation returns YES, but subclasses may override
|
||||
* this method to return NO if they do not wish to contact the Command
|
||||
* server.
|
||||
*/
|
||||
- (BOOL) cmdIsClient;
|
||||
|
||||
/** Returns a fag indicating whether this process is currently connected
|
||||
* it its Command server.
|
||||
*/
|
||||
- (BOOL) cmdIsConnected;
|
||||
|
||||
/** Returns YES is the process is running in test mode, NO otherwise.
|
||||
*/
|
||||
- (BOOL) cmdIsTesting;
|
||||
|
||||
/** Closes a file handle previously obtained using the -cmdLogFile: method.
|
||||
* You should not close a logging handle directly, use this method.
|
||||
*/
|
||||
- (void) cmdLogEnd: (NSString*)name;
|
||||
|
||||
/** Obtain a file handle for logging purposes. The file will have the
|
||||
* specified name and will be created (if necessary) in the processes
|
||||
* logging directory.<br />
|
||||
* If there is already a handle for the specified file, this method
|
||||
* returns the existing handle rather than creating a new one.<br />
|
||||
* Do not close this file handle other than by calling the -cmdLogEnd: method.
|
||||
*/
|
||||
- (NSFileHandle*) cmdLogFile: (NSString*)name;
|
||||
|
||||
/** Used by the Command server to send messages to your application.
|
||||
*/
|
||||
- (void) cmdMesgData: (NSData*)dat from: (NSString*)name;
|
||||
|
||||
/** This method is called whenever the Command server sends an instruction
|
||||
* for your Command client process to act upon. Often the command has been
|
||||
* entered by an operator and you need to respond with a text string.<br />
|
||||
* To implement support for an operator command, you must write a method
|
||||
* whose name is of the form -cmdMesgXXX: where XXX is the command (a
|
||||
* lowercase string) that the operator will enter. This method must accept
|
||||
* a single NSArray object as an argument and must return a readable string
|
||||
* as a result.<br />
|
||||
* The array argument will contain the words in the command line entered
|
||||
* by the operator (with the command itsself as the first item in the
|
||||
* array).<br />
|
||||
* There are two special cases ... when the operator types 'help XXX' your
|
||||
* method will be called with 'help'as the first element of the array and
|
||||
* you should respond with some useful hep text, and when the operator
|
||||
* simply wants a short description of what the command does, the array
|
||||
* argument will be nil (and your method should respond with a short
|
||||
* description of the cmmand).
|
||||
*/
|
||||
- (NSString*) cmdMesg: (NSArray*)msg;
|
||||
|
||||
/** Attempt to establish connection to Command server etc.
|
||||
* Return a proxy to that server if it is available.
|
||||
*/
|
||||
- (id) cmdNewServer;
|
||||
|
||||
/** Return dictionary giving info about specified operator. If the
|
||||
* password string matches the password of the operator (or the operator
|
||||
* has no password) then the dictionary field @"Password" will be set to
|
||||
* @"yes", otherwise it will be @"no".
|
||||
* If the named operator does not exist, the method will return nil.
|
||||
*/
|
||||
- (NSMutableDictionary*)cmdOperator: (NSString*)name password: (NSString*)pass;
|
||||
|
||||
/** Used to tell your application to quit.
|
||||
*/
|
||||
- (void) cmdQuit: (int)status;
|
||||
|
||||
/** Used to tell your application about configuration changes.
|
||||
*/
|
||||
- (void) cmdUpdate: (NSMutableDictionary*)info;
|
||||
|
||||
|
||||
|
||||
|
||||
- (void) log: (NSString*)message type: (EcLogType)t;
|
||||
|
||||
/** Send a warning message to the server.
|
||||
*/
|
||||
- (void) cmdWarn: (NSString*)fmt, ...;
|
||||
|
||||
/* Return interval between timeouts.
|
||||
*/
|
||||
- (NSTimeInterval) cmdInterval;
|
||||
|
||||
- (void) cmdUnregister;
|
||||
/*
|
||||
* All this to unregister from the server.
|
||||
*/
|
||||
|
||||
/** Register a debug mode 'mode'
|
||||
*/
|
||||
- (void) setCmdDebug: (NSString*)mode withDescription: (NSString*)desc;
|
||||
|
||||
/* Sets the interval between timeouts.
|
||||
*/
|
||||
- (void) setCmdInterval: (NSTimeInterval)interval;
|
||||
|
||||
/** Specify a handler method to be invoked after each timeout to let you
|
||||
* perform additional tasks.
|
||||
*/
|
||||
- (void) setCmdTimeout: (SEL)sel;
|
||||
|
||||
/** Specify an object 'obj' to be sent a message 'sel' when the
|
||||
* network configuration information for this process has been changed.
|
||||
* If 'sel' is a nul selector, then the specified object is removed
|
||||
* from the list of objects to be notified. The EcProces class will
|
||||
* retain the object given.
|
||||
*/
|
||||
- (void) setCmdUpdate: (id)obj withMethod: (SEL)sel;
|
||||
|
||||
/*
|
||||
* Trigger a timeout to go off as soon as possible ... subsequent timeouts
|
||||
* go off at the normal interval after that one.
|
||||
*/
|
||||
- (void) triggerCmdTimeout;
|
||||
|
||||
/** Obtains the configuration value for the specified key from the
|
||||
* NSUserDefaults system (as modified by the Command server).<br />
|
||||
* If you need more than one value, or if you want a typed values,
|
||||
* you should call -cmdDefaults to get the defaults object, and then
|
||||
* call methods of that object directly.
|
||||
*/
|
||||
- (id) cmdConfig: (NSString*)key;
|
||||
|
||||
/** Check to see if a debug mode is active.
|
||||
*/
|
||||
- (BOOL) cmdDebugMode: (NSString*)mode;
|
||||
|
||||
/** Set a particular (named) debug mode to be active or inactive.
|
||||
*/
|
||||
- (void) cmdDebugMode: (NSString*)mode active: (BOOL)flag;
|
||||
|
||||
/** Returns the NSUserDefaults instance containing the configuration
|
||||
* information for this process.
|
||||
*/
|
||||
- (NSUserDefaults*) cmdDefaults;
|
||||
|
||||
/** Utility method to perform partial (case insensitive) matching of
|
||||
* an abbreviated command word (val) to a keyword (key)
|
||||
*/
|
||||
- (BOOL) cmdMatch: (NSString*)val toKey: (NSString*)key;
|
||||
|
||||
/** Handle with care - this method invokes the cmdMesg... methods.
|
||||
*/
|
||||
- (NSString*) cmdMesg: (NSArray*)msg;
|
||||
|
||||
/** Retrurns the name by which this process is known to the Command server.
|
||||
*/
|
||||
- (NSString*) cmdName;
|
||||
|
||||
/** May be used withing cmdMesg... methods to return formatted text to
|
||||
* the Console.
|
||||
*/
|
||||
- (void) cmdPrintf: (NSString*)fmt, ...;
|
||||
|
||||
/** Should be over-ridden to perform extra tidy up on shutdown of the
|
||||
* process - should call [super cmdQuit:...] at the end of the method.
|
||||
*/
|
||||
- (void) cmdQuit: (int)status;
|
||||
|
||||
/** Used to tell your application about configuration changes (the
|
||||
* default implementation merges the configuration change into the
|
||||
* NSUserDefaults system and sends the defaults change notification).<br />
|
||||
* If you want to deal with configuration changes actively - override
|
||||
* this and call [super cmdUpdate:...] to install the changed
|
||||
* configuration before anything else.
|
||||
* NB. This method WILL be called before your application is
|
||||
* initialised. Make sure it is safe.
|
||||
*/
|
||||
- (void) cmdUpdate: (NSMutableDictionary*)info;
|
||||
|
||||
/** [-initWithDefaults:] is the Designated initialiser<br />
|
||||
* It adds the defaults specified to the defaults system.
|
||||
* It sets the process name to be that specified in the
|
||||
* 'EcProgramName' default with an '-id' affix if EcInstance is used
|
||||
* to provide an instance id.
|
||||
* Moves to the directory (relative to the current user's home directory)
|
||||
* given in 'EcHomeDirectory'.
|
||||
* If 'EcHomeDirectory' is not present in the defaults system (or is
|
||||
* an empty string) then no directory change is done.
|
||||
*/
|
||||
- (id) initWithDefaults: (NSDictionary*)defs;
|
||||
|
||||
/*
|
||||
* How commands sent to the client via cmdMesg: are invoked -
|
||||
*
|
||||
* 1. If there is an action registered using [-setCmdAction:name] whose
|
||||
* name unambiguously matches the command given, then the specified
|
||||
* selector is used.
|
||||
*
|
||||
* 2. If a method exists whose name is 'cmdMesgfoo:' where 'foo' is the
|
||||
* command given, then that method is registered and invoked.
|
||||
*
|
||||
* The action methods should use the [-cmdPrintf:] method repeatedly to
|
||||
* add text to be returned to the caller.
|
||||
*
|
||||
* By default the method 'cmdMesghelp:' is defined to handle the 'help'
|
||||
* command. To extend the help facility - invoke [super cmdMesghelp:]
|
||||
* at the start of your own implementation.
|
||||
* NB. This method may call any other 'cmdMesg...:' method passing it
|
||||
* an array with the first parameter set to be the string 'help'.
|
||||
* In this case the method should use 'cmdLine:' to return it's help
|
||||
* information.
|
||||
*
|
||||
* We also have 'cmdMesgdebug:' to activate debug logging and
|
||||
* 'cmdMesgnodebug:' to deactivate it. The known debug modes may
|
||||
* be extended by using the 'setCmdDebug:withDescription:' method.
|
||||
*
|
||||
* 'cmdMesgmemory:' is for reporting process memory allocation stats -
|
||||
* it should be overridden to give detailed info.
|
||||
*
|
||||
* 'cmdMesgstatus:' is for reporting process status information and
|
||||
* should be overridden to give detailed info.
|
||||
*
|
||||
* 'cmdMesgarchive:' forces an archive of the debug log.
|
||||
*/
|
||||
- (void) cmdMesgarchive: (NSArray*)msg;
|
||||
- (void) cmdMesgdebug: (NSArray*)msg;
|
||||
- (void) cmdMesghelp: (NSArray*)msg;
|
||||
- (void) cmdMesgmemory: (NSArray*)msg;
|
||||
- (void) cmdMesgnodebug: (NSArray*)msg;
|
||||
- (void) cmdMesgstatus: (NSArray*)msg;
|
||||
|
||||
/*
|
||||
* Returns a proxy object to a[n automatically managed] server
|
||||
*/
|
||||
- (id) server: (NSString *)serverName;
|
||||
|
||||
/*
|
||||
* Like server:, but if the configuration contains a multiple servers,
|
||||
* this tries to locate the specific server that is set up to deal with
|
||||
* users where the last two digits of the phone number are as specified.
|
||||
*/
|
||||
- (id) server: (NSString *)serverName forNumber: (NSString*)num;
|
||||
|
||||
/*
|
||||
* Standard servers return NO to the following. But if we are using
|
||||
* a multiple/broadcast server, this returns YES.
|
||||
*/
|
||||
- (BOOL) isServerMultiple: (NSString *)serverName;
|
||||
|
||||
/** Records the timestamp of the latest significant input for this process.
|
||||
* If when is nil the current timestmp is used.
|
||||
*/
|
||||
- (void) prcHadIP: (NSDate*)when;
|
||||
|
||||
/** Records the timestamp of the latest significant output for this process.
|
||||
* If when is nil the current timestmp is used.
|
||||
*/
|
||||
- (void) prcHadOP: (NSDate*)when;
|
||||
|
||||
/** Called on the first timeout of a new day.<br />
|
||||
* The argument 'when' is the timestamp of the timeout.
|
||||
*/
|
||||
- (void) prcNewDay: (NSCalendarDate*)when;
|
||||
|
||||
/** Called on the first timeout of a new hour.<br />
|
||||
* The argument 'when' is the timestamp of the timeout.
|
||||
*/
|
||||
- (void) prcNewHour: (NSCalendarDate*)when;
|
||||
|
||||
/** Called on the first timeout of a new minute.<br />
|
||||
* The argument 'when' is the timestamp of the timeout.
|
||||
*/
|
||||
- (void) prcNewMinute: (NSCalendarDate*)when;
|
||||
|
||||
/** Return heap memory known not to be leaked ... for use in internal
|
||||
* monitoring of memory usage. You should override this ti add in any
|
||||
* heap store you have used and know is not leaked.
|
||||
*/
|
||||
- (NSUInteger) prcNotLeaked;
|
||||
|
||||
/** Establishes the receiver as a DO server and runs the runloop.<br />
|
||||
* Returns zero when the run loop completes.<br />
|
||||
* Returns one (immediately) if the receiver is transent.<br />
|
||||
* Returns two if unable to register as a DO server.
|
||||
*/
|
||||
- (int) prcRun;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSObject (RemoteServerDelegate)
|
||||
- (void) cmdMadeConnectionToServer: (NSString *)serverName;
|
||||
- (void) cmdLostConnectionToServer: (NSString *)serverName;
|
||||
@end
|
||||
|
||||
extern EcProcess *EcProc; /* Single instance ir nil */
|
||||
|
||||
extern NSString* cmdConnectDbg; /* Debug connection attempts. */
|
||||
extern NSString* cmdDefaultDbg; /* Debug normal stuff. */
|
||||
extern NSString* cmdDetailDbg; /* Debug stuff in more detail. */
|
||||
|
||||
|
||||
#endif /* INCLUDED_ECPROCESS_H */
|
3594
EcProcess.m
Normal file
3594
EcProcess.m
Normal file
File diff suppressed because it is too large
Load diff
52
EcUserDefaults.h
Normal file
52
EcUserDefaults.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
|
||||
@class NSString;
|
||||
|
||||
/**
|
||||
* This category simply provides an easy way to work with user defaults
|
||||
* where you want all keys to share a common prefix.
|
||||
*/
|
||||
@interface NSUserDefaults (EcUserDefaults)
|
||||
|
||||
/** Returns the latest prefixed version of the shared user defaults,
|
||||
* or nil if none has been set up.
|
||||
*/
|
||||
+ (NSUserDefaults*) prefixedDefaults;
|
||||
|
||||
/** Returns a proxy to the shared user defaults instance, which will use
|
||||
* aPrefix at the start of every key.<br />
|
||||
* If aPrefix is nil, the string given by the EcUserDefaultsPrefix user
|
||||
* default is used.<br />
|
||||
* If the enforcePrefix flag is YES, the prefix is strictly enforced,
|
||||
* otherwise the system will read defaults using the unprefixed key
|
||||
* if no value is found for the prefixed key.
|
||||
*/
|
||||
+ (NSUserDefaults*) userDefaultsWithPrefix: (NSString*)aPrefix
|
||||
strict: (BOOL)enforcePrefix;
|
||||
|
||||
/** Returns the prefix used by the receiver, or nil if no prefix is in use.
|
||||
*/
|
||||
- (NSString*) defaultsPrefix;
|
||||
|
||||
/** Convenience method to prepend the pefix to the supplied aKey value
|
||||
* if it is not already present.
|
||||
*/
|
||||
- (NSString*) key: (NSString*)aKey;
|
||||
|
||||
/** Sets a value to take precedence over others (in the volatile domain
|
||||
* reserved for commands issued to the current process by an operator).<br />
|
||||
* Returns YES if the configuration was changed, NO otherwise.
|
||||
*/
|
||||
- (BOOL) setCommand: (id)val forKey: (NSString*)key;
|
||||
|
||||
/** Replaces the system central configuration information for this process
|
||||
* with the contents of the dictionary. Values in this dictionary take
|
||||
* precedence over other configured values except for those set using the
|
||||
* -setCommand:forKey: method.<br />
|
||||
* Returns YES if the configuration changed, NO otherwise.
|
||||
*/
|
||||
- (BOOL) setConfiguration: (NSDictionary*)config;
|
||||
|
||||
@end
|
||||
|
347
EcUserDefaults.m
Normal file
347
EcUserDefaults.m
Normal file
|
@ -0,0 +1,347 @@
|
|||
|
||||
#import <Foundation/NSInvocation.h>
|
||||
#import <Foundation/NSLock.h>
|
||||
#import <Foundation/NSMethodSignature.h>
|
||||
#import <Foundation/NSNotification.h>
|
||||
#import <Foundation/NSProxy.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import "EcUserDefaults.h"
|
||||
|
||||
static NSUserDefaults *latest = nil;
|
||||
static NSLock *lock = nil;
|
||||
|
||||
@interface EcUserDefaults : NSProxy
|
||||
{
|
||||
NSUserDefaults *defs;
|
||||
NSString *prefix;
|
||||
BOOL enforce;
|
||||
}
|
||||
- (id) initWithPrefix: (NSString*)p strict: (BOOL)s;
|
||||
- (NSString*) _getKey: (NSString*)baseKey;
|
||||
@end
|
||||
|
||||
@implementation EcUserDefaults
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
if (nil == lock)
|
||||
{
|
||||
lock = [NSLock new];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray*) arrayForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs arrayForKey: [self _getKey: aKey]];
|
||||
}
|
||||
|
||||
- (BOOL) boolForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs boolForKey: [self _getKey: aKey]];
|
||||
}
|
||||
|
||||
- (NSData*) dataForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs dataForKey: [self _getKey: aKey]];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[lock lock];
|
||||
if (latest == (NSUserDefaults*)self)
|
||||
{
|
||||
latest = nil;
|
||||
}
|
||||
[lock unlock];
|
||||
[prefix release];
|
||||
[defs release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString*) defaultsPrefix
|
||||
{
|
||||
return prefix;
|
||||
}
|
||||
|
||||
- (NSDictionary*) dictionaryForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs dictionaryForKey: [self _getKey: aKey]];
|
||||
}
|
||||
|
||||
#if 0
|
||||
- (double) doubleForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs doubleForKey: [self _getKey: aKey]];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (float) floatForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs floatForKey: [self _getKey: aKey]];
|
||||
}
|
||||
|
||||
- (void) forwardInvocation: (NSInvocation*)anInvocation
|
||||
{
|
||||
[anInvocation invokeWithTarget: defs];
|
||||
}
|
||||
|
||||
- (NSString*) _getKey: (NSString*)aKey
|
||||
{
|
||||
/* Make sure we have the prefix.
|
||||
*/
|
||||
if (nil != prefix)
|
||||
{
|
||||
if (NO == [aKey hasPrefix: prefix])
|
||||
{
|
||||
aKey = [prefix stringByAppendingString: aKey];
|
||||
}
|
||||
if (NO == enforce && nil == [defs objectForKey: aKey])
|
||||
{
|
||||
/* Nothing found for key ... try without the prefix.
|
||||
*/
|
||||
aKey = [aKey substringFromIndex: [prefix length]];
|
||||
}
|
||||
}
|
||||
return aKey;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id) initWithPrefix: (NSString*)p strict: (BOOL)s
|
||||
{
|
||||
NSMutableArray *list;
|
||||
|
||||
[lock lock];
|
||||
enforce = s;
|
||||
defs = [[NSUserDefaults standardUserDefaults] retain];
|
||||
if (0 == [p length])
|
||||
{
|
||||
p = [defs stringForKey: @"EcUserDefaultsPrefix"];
|
||||
if (0 == [p length])
|
||||
{
|
||||
p = nil;
|
||||
}
|
||||
}
|
||||
prefix = [p copy];
|
||||
|
||||
/* Make sure the defaults database has our special domains at the start
|
||||
* of the search list and in the correct order.
|
||||
*/
|
||||
list = [[defs searchList] mutableCopy];
|
||||
[list removeObject: @"EcCommand"];
|
||||
[list removeObject: @"EcConfiguration"];
|
||||
[list insertObject: @"EcCommand" atIndex: 0];
|
||||
[list insertObject: @"EcConfiguration" atIndex: 1];
|
||||
[defs setSearchList: list];
|
||||
[list release];
|
||||
latest = (NSUserDefaults*)self;
|
||||
[lock unlock];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSInteger) integerForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs integerForKey: [self _getKey: aKey]];
|
||||
}
|
||||
|
||||
- (NSString*) key: (NSString*)aKey
|
||||
{
|
||||
/* Make sure we have the prefix.
|
||||
*/
|
||||
if (nil != prefix && NO == [aKey hasPrefix: prefix])
|
||||
{
|
||||
aKey = [prefix stringByAppendingString: aKey];
|
||||
}
|
||||
return aKey;
|
||||
}
|
||||
|
||||
- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector
|
||||
{
|
||||
if (class_respondsToSelector(object_getClass(self), aSelector))
|
||||
{
|
||||
return [super methodSignatureForSelector: aSelector];
|
||||
}
|
||||
return [defs methodSignatureForSelector: aSelector];
|
||||
}
|
||||
|
||||
- (id) objectForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs objectForKey: [self _getKey: aKey]];
|
||||
}
|
||||
|
||||
- (void) removeObjectForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs removeObjectForKey: [self _getKey: aKey]];
|
||||
}
|
||||
|
||||
- (void) setBool: (BOOL)value forKey: (NSString*)aKey
|
||||
{
|
||||
[defs setBool: value forKey: [self key: aKey]];
|
||||
}
|
||||
|
||||
- (BOOL) setCommand: (id)val forKey: (NSString*)key
|
||||
{
|
||||
return [defs setCommand: val forKey: [self key: key]];
|
||||
}
|
||||
|
||||
#if 0
|
||||
- (void) setDouble: (double)value forKey: (NSString*)aKey
|
||||
{
|
||||
[defs setDouble: value forKey: [self key: aKey]];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void) setFloat: (float)value forKey: (NSString*)aKey
|
||||
{
|
||||
[defs setFloat: value forKey: [self key: aKey]];
|
||||
}
|
||||
|
||||
- (void) setInteger: (NSInteger)value forKey: (NSString*)aKey
|
||||
{
|
||||
[defs setInteger: value forKey: [self key: aKey]];
|
||||
}
|
||||
|
||||
- (void) setObject: (id)anObject forKey: (NSString*)aKey
|
||||
{
|
||||
[defs setObject: anObject forKey: [self key: aKey]];
|
||||
}
|
||||
|
||||
- (NSArray*) stringArrayForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs stringArrayForKey: [self _getKey: aKey]];
|
||||
}
|
||||
|
||||
- (NSString*) stringForKey: (NSString*)aKey
|
||||
{
|
||||
return [defs stringForKey: [self _getKey: aKey]];
|
||||
}
|
||||
|
||||
- (NSUserDefaults*) target
|
||||
{
|
||||
return defs;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSUserDefaults (EcUserDefaults)
|
||||
|
||||
+ (NSUserDefaults*) prefixedDefaults
|
||||
{
|
||||
NSUserDefaults *defs = nil;
|
||||
|
||||
if (Nil != [EcUserDefaults class])
|
||||
{
|
||||
[lock lock];
|
||||
defs = [latest retain];
|
||||
[lock unlock];
|
||||
}
|
||||
return [defs autorelease];
|
||||
}
|
||||
|
||||
+ (NSUserDefaults*) userDefaultsWithPrefix: (NSString*)aPrefix
|
||||
strict: (BOOL)enforcePrefix
|
||||
{
|
||||
return (NSUserDefaults*)[[[EcUserDefaults alloc] initWithPrefix:
|
||||
aPrefix strict: enforcePrefix] autorelease];
|
||||
}
|
||||
|
||||
- (NSString*) defaultsPrefix
|
||||
{
|
||||
return nil; // No prefix in use ... this is not a proxy
|
||||
}
|
||||
|
||||
- (NSString*) key: (NSString*)aKey
|
||||
{
|
||||
NSString *prefix = [self defaultsPrefix];
|
||||
|
||||
/* Make sure we have the prefix.
|
||||
*/
|
||||
if (nil != prefix && NO == [aKey hasPrefix: prefix])
|
||||
{
|
||||
aKey = [prefix stringByAppendingString: aKey];
|
||||
}
|
||||
return aKey;
|
||||
}
|
||||
|
||||
- (BOOL) setCommand: (id)val forKey: (NSString*)key
|
||||
{
|
||||
NSDictionary *old = [self volatileDomainForName: @"EcCommand"];
|
||||
NSDictionary *new = nil;
|
||||
NSString *pre = [self defaultsPrefix];
|
||||
|
||||
/* Make sure prefix is used if we have one set.
|
||||
*/
|
||||
if (nil != pre)
|
||||
{
|
||||
if (NO == [key hasPrefix: pre])
|
||||
{
|
||||
key = [pre stringByAppendingString: key];
|
||||
}
|
||||
}
|
||||
if (nil == val)
|
||||
{
|
||||
if (nil != [old objectForKey: key])
|
||||
{
|
||||
new = [old mutableCopy];
|
||||
[new removeObjectForKey: key];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NO == [val isEqual: [old objectForKey: key]])
|
||||
{
|
||||
new = [old mutableCopy];
|
||||
if (nil == new)
|
||||
{
|
||||
new = [NSMutableDictionary new];
|
||||
}
|
||||
[new setObject: val forKey: key];
|
||||
}
|
||||
}
|
||||
if (nil != new)
|
||||
{
|
||||
if (nil != old)
|
||||
{
|
||||
[self removeVolatileDomainForName: @"EcCommand"];
|
||||
}
|
||||
[self setVolatileDomain: new forName: @"EcCommand"];
|
||||
[new release];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:
|
||||
NSUserDefaultsDidChangeNotification object: self];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL) setConfiguration: (NSDictionary*)config
|
||||
{
|
||||
NSDictionary *old = [self volatileDomainForName: @"EcConfiguration"];
|
||||
BOOL changed = NO;
|
||||
|
||||
if (NO == [old isEqual: config])
|
||||
{
|
||||
[self removeVolatileDomainForName: @"EcConfiguration"];
|
||||
changed = YES;
|
||||
}
|
||||
if (nil != config)
|
||||
{
|
||||
[self setVolatileDomain: config forName: @"EcConfiguration"];
|
||||
changed = YES;
|
||||
}
|
||||
if (YES == changed)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:
|
||||
NSUserDefaultsDidChangeNotification
|
||||
object: self];
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@end
|
||||
|
99
GNUmakefile
Normal file
99
GNUmakefile
Normal file
|
@ -0,0 +1,99 @@
|
|||
|
||||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
GNUSTEP_MAKEFILES := $(shell gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null)
|
||||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
$(warning )
|
||||
$(warning Unable to obtain GNUSTEP_MAKEFILES setting from gnustep-config!)
|
||||
$(warning Perhaps gnustep-make is not properly installed,)
|
||||
$(warning so gnustep-config is not in your PATH.)
|
||||
$(warning )
|
||||
$(warning Your PATH is currently $(PATH))
|
||||
$(warning )
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(GNUSTEP_MAKEFILES),)
|
||||
$(error You need to set GNUSTEP_MAKEFILES before compiling!)
|
||||
endif
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
|
||||
-include config.make
|
||||
-include local.make
|
||||
|
||||
PACKAGE_NAME=EnterpriseControlConfigurationLogging
|
||||
PACKAGE_VERSION=0.1.0
|
||||
Ec_INTERFACE_VERSION=0.1
|
||||
|
||||
NEEDS_GUI=NO
|
||||
|
||||
# The library to be compiled
|
||||
LIBRARY_NAME=ECCL
|
||||
|
||||
# The Objective-C source files to be compiled
|
||||
ECCL_OBJC_FILES = \
|
||||
EcAlarm.m \
|
||||
EcAlarmDestination.m \
|
||||
EcAlarmSinkSNMP.m \
|
||||
EcAlerter.m \
|
||||
EcBroadcastProxy.m \
|
||||
EcHost.m \
|
||||
EcLogger.m \
|
||||
EcProcess.m \
|
||||
EcUserDefaults.m \
|
||||
|
||||
ECCL_HEADER_FILES = \
|
||||
EcAlarm.h \
|
||||
EcAlarmDestination.h \
|
||||
EcAlarmSinkSNMP.h \
|
||||
EcAlerter.h \
|
||||
EcBroadcastProxy.h \
|
||||
EcHost.h \
|
||||
EcLogger.h \
|
||||
EcProcess.h \
|
||||
EcUserDefaults.h \
|
||||
|
||||
TOOL_NAME = \
|
||||
Command \
|
||||
Console \
|
||||
Control \
|
||||
|
||||
|
||||
Command_OBJC_FILES = Command.m Client.m NSFileHandle+Printf.m
|
||||
Command_TOOL_LIBS += -lECCL
|
||||
Command_LIB_DIRS += -L./$(GNUSTEP_OBJ_DIR)
|
||||
|
||||
Console_OBJC_FILES = Console.m NSFileHandle+Printf.m
|
||||
Console_TOOL_LIBS += -lECCL
|
||||
Console_LIB_DIRS += -L./$(GNUSTEP_OBJ_DIR)
|
||||
|
||||
Control_OBJC_FILES = Control.m Client.m NSFileHandle+Printf.m
|
||||
Control_TOOL_LIBS += -lECCL
|
||||
Control_LIB_DIRS += -L./$(GNUSTEP_OBJ_DIR)
|
||||
|
||||
|
||||
|
||||
|
||||
DOCUMENT_NAME = ECCL
|
||||
|
||||
ECCL_AGSDOC_FILES = \
|
||||
EcAlarm.h \
|
||||
EcAlarmDestination.h \
|
||||
EcAlarmSinkSNMP.h \
|
||||
EcAlerter.h \
|
||||
EcHost.h \
|
||||
EcLogger.h \
|
||||
EcProcess.h \
|
||||
EcUserDefaults.h \
|
||||
|
||||
|
||||
ECCL_DOC_INSTALL_DIR = Libraries
|
||||
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/library.make
|
||||
include $(GNUSTEP_MAKEFILES)/tool.make
|
||||
include $(GNUSTEP_MAKEFILES)/documentation.make
|
||||
|
||||
-include GNUmakefile.postamble
|
48
GNUmakefile.postamble
Normal file
48
GNUmakefile.postamble
Normal file
|
@ -0,0 +1,48 @@
|
|||
#
|
||||
# Makefile.postamble
|
||||
#
|
||||
# Project specific makefile rules
|
||||
#
|
||||
# Uncomment the targets you want.
|
||||
# The double colons (::) are important, do not make them single colons
|
||||
# otherwise the normal makefile rules will not be performed.
|
||||
#
|
||||
|
||||
# Things to do before compiling
|
||||
# before-all::
|
||||
|
||||
# Things to do after compiling
|
||||
# after-all::
|
||||
|
||||
# Things to do before installing
|
||||
# before-install::
|
||||
|
||||
# Things to do after installing
|
||||
# after-install::
|
||||
|
||||
# Things to do before uninstalling
|
||||
# before-uninstall::
|
||||
|
||||
# Things to do after uninstalling
|
||||
# after-uninstall::
|
||||
|
||||
# Things to do before cleaning
|
||||
# before-clean::
|
||||
|
||||
# Things to do after cleaning
|
||||
# after-clean::
|
||||
|
||||
# Things to do before distcleaning
|
||||
# before-distclean::
|
||||
|
||||
# Things to do after distcleaning
|
||||
# after-distclean::
|
||||
|
||||
# Things to do before checking
|
||||
# before-check::
|
||||
|
||||
# Things to do after checking
|
||||
# after-check::
|
||||
|
||||
EcAlarmSink.m_FILE_FILTER_OUT_FLAGS = -O%
|
||||
|
47
GNUmakefile.preamble
Normal file
47
GNUmakefile.preamble
Normal file
|
@ -0,0 +1,47 @@
|
|||
#
|
||||
# Makefile.preamble
|
||||
#
|
||||
# Project specific makefile variables, and additional
|
||||
#
|
||||
# Do not put any Makefile rules in this file, instead they should
|
||||
# be put into Makefile.postamble.
|
||||
#
|
||||
|
||||
#
|
||||
# Flags dealing with compiling and linking
|
||||
#
|
||||
|
||||
# Additional flags to pass to the preprocessor
|
||||
#ADDITIONAL_CPPFLAGS +=
|
||||
|
||||
# Additional flags to pass to the Objective-C compiler
|
||||
#ADDITIONAL_OBJCFLAGS +=
|
||||
|
||||
# Additional flags to pass to the C compiler
|
||||
#ADDITIONAL_CFLAGS +=
|
||||
|
||||
# Additional include directories the compiler should search
|
||||
#ADDITIONAL_INCLUDE_DIRS +=
|
||||
|
||||
# Additional LDFLAGS to pass to the linker
|
||||
# ADDITIONAL_LDFLAGS +=
|
||||
|
||||
# Additional library directories the linker should search
|
||||
ADDITIONAL_LIB_DIRS +=
|
||||
|
||||
# Additional libraries when linking tools
|
||||
#ADDITIONAL_TOOL_LIBS +=
|
||||
|
||||
# Additional libraries when linking applications
|
||||
#ADDITIONAL_GUI_LIBS +=
|
||||
|
||||
#
|
||||
# Flags dealing with installing and uninstalling
|
||||
#
|
||||
|
||||
# Additional directories to be created during installation
|
||||
#ADDITIONAL_INSTALL_DIRS +=
|
||||
|
||||
EcAlarmSink_OBJCFLAGS += $(shell net-snmp-config --cflags)
|
||||
LIBRARIES_DEPEND_UPON += $(shell net-snmp-config --agent-libs)
|
||||
|
13
NSFileHandle+Printf.h
Normal file
13
NSFileHandle+Printf.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
#include <Foundation/NSFileHandle.h>
|
||||
|
||||
/** File handle printing utilities.
|
||||
*/
|
||||
@interface NSFileHandle (Printf)
|
||||
|
||||
- (void) printWithFormat: (NSString*)format arguments: (va_list)args;
|
||||
- (void) printf: (NSString*)format,...;
|
||||
- (void) puts: (NSString*)text;
|
||||
|
||||
@end
|
||||
|
48
NSFileHandle+Printf.m
Normal file
48
NSFileHandle+Printf.m
Normal file
|
@ -0,0 +1,48 @@
|
|||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSException.h>
|
||||
#import <Foundation/NSFileHandle.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#include "NSFileHandle+Printf.h"
|
||||
|
||||
@implementation NSFileHandle (Printf)
|
||||
|
||||
- (void) printWithFormat: (NSString*)format arguments: (va_list)args
|
||||
{
|
||||
NSString *text;
|
||||
|
||||
text = [NSString stringWithFormat: format arguments: args];
|
||||
[self puts: text];
|
||||
}
|
||||
|
||||
- (void) printf: (NSString*)format,...
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
[self printWithFormat: format arguments: ap];
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
- (void) puts: (NSString*)text
|
||||
{
|
||||
NS_DURING
|
||||
{
|
||||
NSData *data;
|
||||
|
||||
data = [text dataUsingEncoding: [NSString defaultCStringEncoding]];
|
||||
if (data == nil)
|
||||
{
|
||||
data = [text dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
[self writeData: data];
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog(@"Exception writing to log file: %@", localException);
|
||||
[localException raise];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
@end
|
||||
|
50
Terminate.m
Normal file
50
Terminate.m
Normal file
|
@ -0,0 +1,50 @@
|
|||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include "EcProcess.h"
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(arp);
|
||||
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
||||
NSString *host;
|
||||
NSString *name;
|
||||
id proxy;
|
||||
|
||||
host = [defs stringForKey: @"BSCommandHost"];
|
||||
if (host == nil)
|
||||
{
|
||||
host = [defs stringForKey: @"CommandHost"];
|
||||
if (host == nil)
|
||||
{
|
||||
host = @"*";
|
||||
}
|
||||
}
|
||||
if ([host length] == 0)
|
||||
{
|
||||
host = [[NSHost currentHost] name];
|
||||
}
|
||||
|
||||
/*
|
||||
* Shut down the local command server.
|
||||
*/
|
||||
name = [defs stringForKey: @"BSCommandName"];
|
||||
if (name == nil)
|
||||
{
|
||||
name = [defs stringForKey: @"CommandName"];
|
||||
if (name == nil)
|
||||
{
|
||||
name = CMD_SERVER_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
proxy = [NSConnection rootProxyForConnectionWithRegisteredName: name
|
||||
host: host
|
||||
usingNameServer: [NSSocketPortNameServer sharedInstance]];
|
||||
[(id<Command>)proxy terminate];
|
||||
|
||||
RELEASE(arp);
|
||||
return 0;
|
||||
}
|
||||
|
1326
config.guess
vendored
Executable file
1326
config.guess
vendored
Executable file
File diff suppressed because it is too large
Load diff
105
config.h
Normal file
105
config.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.in by autoheader. */
|
||||
|
||||
/* Define to the type of elements in the array set by `getgroups'. Usually
|
||||
this is either `int' or `gid_t'. */
|
||||
#define GETGROUPS_T gid_t
|
||||
|
||||
/* Define to 1 if you have the <arpa/inet.h> header file. */
|
||||
#define HAVE_ARPA_INET_H 1
|
||||
|
||||
/* Define to 1 if you have the <arpa/telnet.h> header file. */
|
||||
#define HAVE_ARPA_TELNET_H 1
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if you have the `getpid' function. */
|
||||
#define HAVE_GETPID 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the <netdb.h> header file. */
|
||||
#define HAVE_NETDB_H 1
|
||||
|
||||
/* Define to 1 if you have the <netinet/in.h> header file. */
|
||||
#define HAVE_NETINET_IN_H 1
|
||||
|
||||
/* Define to 1 if you have the <pwd.h> header file. */
|
||||
#define HAVE_PWD_H 1
|
||||
|
||||
/* Define to 1 if you have the `setpgid' function. */
|
||||
#define HAVE_SETPGID 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/fcntl.h> header file. */
|
||||
#define HAVE_SYS_FCNTL_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/file.h> header file. */
|
||||
#define HAVE_SYS_FILE_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/signal.h> header file. */
|
||||
#define HAVE_SYS_SIGNAL_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
|
||||
#define HAVE_SYS_WAIT_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME ""
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING ""
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION ""
|
||||
|
||||
/* Define as the return type of signal handlers (`int' or `void'). */
|
||||
#define RETSIGTYPE void
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#define TIME_WITH_SYS_TIME 1
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
/* #undef gid_t */
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
/* #undef mode_t */
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
/* #undef uid_t */
|
104
config.h.in
Normal file
104
config.h.in
Normal file
|
@ -0,0 +1,104 @@
|
|||
/* config.h.in. Generated from configure.in by autoheader. */
|
||||
|
||||
/* Define to the type of elements in the array set by `getgroups'. Usually
|
||||
this is either `int' or `gid_t'. */
|
||||
#undef GETGROUPS_T
|
||||
|
||||
/* Define to 1 if you have the <arpa/inet.h> header file. */
|
||||
#undef HAVE_ARPA_INET_H
|
||||
|
||||
/* Define to 1 if you have the <arpa/telnet.h> header file. */
|
||||
#undef HAVE_ARPA_TELNET_H
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#undef HAVE_FCNTL_H
|
||||
|
||||
/* Define to 1 if you have the `getpid' function. */
|
||||
#undef HAVE_GETPID
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the <netdb.h> header file. */
|
||||
#undef HAVE_NETDB_H
|
||||
|
||||
/* Define to 1 if you have the <netinet/in.h> header file. */
|
||||
#undef HAVE_NETINET_IN_H
|
||||
|
||||
/* Define to 1 if you have the <pwd.h> header file. */
|
||||
#undef HAVE_PWD_H
|
||||
|
||||
/* Define to 1 if you have the `setpgid' function. */
|
||||
#undef HAVE_SETPGID
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/fcntl.h> header file. */
|
||||
#undef HAVE_SYS_FCNTL_H
|
||||
|
||||
/* Define to 1 if you have the <sys/file.h> header file. */
|
||||
#undef HAVE_SYS_FILE_H
|
||||
|
||||
/* Define to 1 if you have the <sys/signal.h> header file. */
|
||||
#undef HAVE_SYS_SIGNAL_H
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#undef HAVE_SYS_SOCKET_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
|
||||
#undef HAVE_SYS_WAIT_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define as the return type of signal handlers (`int' or `void'). */
|
||||
#undef RETSIGTYPE
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#undef TIME_WITH_SYS_TIME
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
#undef gid_t
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
#undef mode_t
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
#undef uid_t
|
0
config.make.in
Normal file
0
config.make.in
Normal file
1451
config.sub
vendored
Executable file
1451
config.sub
vendored
Executable file
File diff suppressed because it is too large
Load diff
25
configure.in
Normal file
25
configure.in
Normal file
|
@ -0,0 +1,25 @@
|
|||
dnl Process this file with autoconf to produce configure.
|
||||
|
||||
AC_INIT(EcProcess.h)
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
AC_PROG_CC
|
||||
AC_PROG_CPP
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
dnl start proper config checks
|
||||
AC_TYPE_SIGNAL
|
||||
|
||||
AC_HEADER_STDC
|
||||
AC_HEADER_TIME
|
||||
AC_HEADER_SYS_WAIT
|
||||
AC_CHECK_HEADERS(arpa/inet.h arpa/telnet.h netinet/in.h netdb.h pwd.h string.h fcntl.h sys/fcntl.h sys/file.h sys/types.h sys/socket.h sys/signal.h stdlib.h unistd.h)
|
||||
|
||||
AC_TYPE_GETGROUPS
|
||||
AC_TYPE_SIGNAL
|
||||
AC_TYPE_MODE_T
|
||||
|
||||
AC_CHECK_FUNCS(getpid setpgid)
|
||||
|
||||
AC_OUTPUT(config.make)
|
||||
|
250
install-sh
Executable file
250
install-sh
Executable file
|
@ -0,0 +1,250 @@
|
|||
#! /bin/sh
|
||||
#
|
||||
# install - install a program, script, or datafile
|
||||
# This comes from X11R5 (mit/util/scripts/install.sh).
|
||||
#
|
||||
# Copyright 1991 by the Massachusetts Institute of Technology
|
||||
#
|
||||
# Permission to use, copy, modify, distribute, and sell this software and its
|
||||
# documentation for any purpose is hereby granted without fee, provided that
|
||||
# the above copyright notice appear in all copies and that both that
|
||||
# copyright notice and this permission notice appear in supporting
|
||||
# documentation, and that the name of M.I.T. not be used in advertising or
|
||||
# publicity pertaining to distribution of the software without specific,
|
||||
# written prior permission. M.I.T. makes no representations about the
|
||||
# suitability of this software for any purpose. It is provided "as is"
|
||||
# without express or implied warranty.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch. It can only install one file at a time, a restriction
|
||||
# shared with many OS's install programs.
|
||||
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit="${DOITPROG-}"
|
||||
|
||||
|
||||
# put in absolute paths if you don't have them in your path; or use env. vars.
|
||||
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
mkdirprog="${MKDIRPROG-mkdir}"
|
||||
|
||||
transformbasename=""
|
||||
transform_arg=""
|
||||
instcmd="$mvprog"
|
||||
chmodcmd="$chmodprog 0755"
|
||||
chowncmd=""
|
||||
chgrpcmd=""
|
||||
stripcmd=""
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
src=""
|
||||
dst=""
|
||||
dir_arg=""
|
||||
|
||||
while [ x"$1" != x ]; do
|
||||
case $1 in
|
||||
-c) instcmd="$cpprog"
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-d) dir_arg=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-m) chmodcmd="$chmodprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-s) stripcmd="$stripprog"
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
|
||||
shift
|
||||
continue;;
|
||||
|
||||
*) if [ x"$src" = x ]
|
||||
then
|
||||
src=$1
|
||||
else
|
||||
# this colon is to work around a 386BSD /bin/sh bug
|
||||
:
|
||||
dst=$1
|
||||
fi
|
||||
shift
|
||||
continue;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ x"$src" = x ]
|
||||
then
|
||||
echo "install: no input file specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
if [ x"$dir_arg" != x ]; then
|
||||
dst=$src
|
||||
src=""
|
||||
|
||||
if [ -d $dst ]; then
|
||||
instcmd=:
|
||||
else
|
||||
instcmd=mkdir
|
||||
fi
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
|
||||
if [ -f $src -o -d $src ]
|
||||
then
|
||||
true
|
||||
else
|
||||
echo "install: $src does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ x"$dst" = x ]
|
||||
then
|
||||
echo "install: no destination specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# If destination is a directory, append the input filename; if your system
|
||||
# does not like double slashes in filenames, you may need to add some logic
|
||||
|
||||
if [ -d $dst ]
|
||||
then
|
||||
dst="$dst"/`basename $src`
|
||||
else
|
||||
true
|
||||
fi
|
||||
fi
|
||||
|
||||
## this sed command emulates the dirname command
|
||||
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
|
||||
|
||||
# Make sure that the destination directory exists.
|
||||
# this part is taken from Noah Friedman's mkinstalldirs script
|
||||
|
||||
# Skip lots of stat calls in the usual case.
|
||||
if [ ! -d "$dstdir" ]; then
|
||||
defaultIFS='
|
||||
'
|
||||
IFS="${IFS-${defaultIFS}}"
|
||||
|
||||
oIFS="${IFS}"
|
||||
# Some sh's can't handle IFS=/ for some reason.
|
||||
IFS='%'
|
||||
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
|
||||
IFS="${oIFS}"
|
||||
|
||||
pathcomp=''
|
||||
|
||||
while [ $# -ne 0 ] ; do
|
||||
pathcomp="${pathcomp}${1}"
|
||||
shift
|
||||
|
||||
if [ ! -d "${pathcomp}" ] ;
|
||||
then
|
||||
$mkdirprog "${pathcomp}"
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
pathcomp="${pathcomp}/"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ x"$dir_arg" != x ]
|
||||
then
|
||||
$doit $instcmd $dst &&
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
|
||||
else
|
||||
|
||||
# If we're going to rename the final executable, determine the name now.
|
||||
|
||||
if [ x"$transformarg" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
dstfile=`basename $dst $transformbasename |
|
||||
sed $transformarg`$transformbasename
|
||||
fi
|
||||
|
||||
# don't allow the sed command to completely eliminate the filename
|
||||
|
||||
if [ x"$dstfile" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# Make a temp file name in the proper directory.
|
||||
|
||||
dsttmp=$dstdir/#inst.$$#
|
||||
|
||||
# Move or copy the file name to the temp name
|
||||
|
||||
$doit $instcmd $src $dsttmp &&
|
||||
|
||||
trap "rm -f ${dsttmp}" 0 &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits
|
||||
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $instcmd $src $dsttmp" command.
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
|
||||
$doit $rmcmd -f $dstdir/$dstfile &&
|
||||
$doit $mvcmd $dsttmp $dstdir/$dstfile
|
||||
|
||||
fi &&
|
||||
|
||||
|
||||
exit 0
|
6
local.make
Normal file
6
local.make
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
ADDITIONAL_CPPFLAGS += \
|
||||
'-DEC_DEFAULTS_PREFIX=@"BS"'\
|
||||
'-DEC_DEFAULTS_STRICT=NO'\
|
||||
'-DEC_EFFECTIVE_USER=@"brains99"'\
|
||||
|
Loading…
Reference in a new issue