From acbe80c889789b2a039c396fb3d635551606cd95 Mon Sep 17 00:00:00 2001 From: rfm Date: Sun, 19 Feb 2012 11:59:22 +0000 Subject: [PATCH] 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 --- Client.h | 34 + Client.m | 170 ++ Command.m | 2436 ++++++++++++++++++ Console.m | 900 +++++++ Control.m | 2548 +++++++++++++++++++ EcAlarm.h | 589 +++++ EcAlarm.m | 879 +++++++ EcAlarmDestination.h | 179 ++ EcAlarmDestination.m | 531 ++++ EcAlarmSinkSNMP.h | 174 ++ EcAlarmSinkSNMP.m | 1604 ++++++++++++ EcAlerter.h | 32 + EcAlerter.m | 923 +++++++ EcBroadcastProxy.h | 132 + EcBroadcastProxy.m | 676 +++++ EcHost.h | 79 + EcHost.m | 285 +++ EcLogger.h | 37 + EcLogger.m | 514 ++++ EcProcess.h | 662 +++++ EcProcess.m | 3594 +++++++++++++++++++++++++++ EcUserDefaults.h | 52 + EcUserDefaults.m | 347 +++ GNUmakefile | 99 + GNUmakefile.postamble | 48 + GNUmakefile.preamble | 47 + NSFileHandle+Printf.h | 13 + NSFileHandle+Printf.m | 48 + Terminate.m | 50 + config.guess | 1326 ++++++++++ config.h | 105 + config.h.in | 104 + config.make.in | 0 config.sub | 1451 +++++++++++ configure | 5445 +++++++++++++++++++++++++++++++++++++++++ configure.in | 25 + install-sh | 250 ++ local.make | 6 + 38 files changed, 26394 insertions(+) create mode 100644 Client.h create mode 100644 Client.m create mode 100644 Command.m create mode 100644 Console.m create mode 100644 Control.m create mode 100644 EcAlarm.h create mode 100644 EcAlarm.m create mode 100644 EcAlarmDestination.h create mode 100644 EcAlarmDestination.m create mode 100644 EcAlarmSinkSNMP.h create mode 100644 EcAlarmSinkSNMP.m create mode 100644 EcAlerter.h create mode 100644 EcAlerter.m create mode 100644 EcBroadcastProxy.h create mode 100644 EcBroadcastProxy.m create mode 100644 EcHost.h create mode 100644 EcHost.m create mode 100644 EcLogger.h create mode 100644 EcLogger.m create mode 100644 EcProcess.h create mode 100644 EcProcess.m create mode 100644 EcUserDefaults.h create mode 100644 EcUserDefaults.m create mode 100644 GNUmakefile create mode 100644 GNUmakefile.postamble create mode 100644 GNUmakefile.preamble create mode 100644 NSFileHandle+Printf.h create mode 100644 NSFileHandle+Printf.m create mode 100644 Terminate.m create mode 100755 config.guess create mode 100644 config.h create mode 100644 config.h.in create mode 100644 config.make.in create mode 100755 config.sub create mode 100755 configure create mode 100644 configure.in create mode 100755 install-sh create mode 100644 local.make diff --git a/Client.h b/Client.h new file mode 100644 index 0000000..8e52460 --- /dev/null +++ b/Client.h @@ -0,0 +1,34 @@ + +@interface ClientInfo : NSObject +{ + id 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)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 + diff --git a/Client.m b/Client.m new file mode 100644 index 0000000..933e0e6 --- /dev/null +++ b/Client.m @@ -0,0 +1,170 @@ + + +#import + +#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)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 + + diff --git a/Command.m b/Command.m new file mode 100644 index 0000000..2194d50 --- /dev/null +++ b/Command.m @@ -0,0 +1,2436 @@ + +#import + +#import "EcProcess.h" +#import "EcAlarm.h" +#import "NSFileHandle+Printf.h" +#import "Client.h" + +#import "config.h" + +#ifdef HAVE_SYS_SIGNAL_H +#include +#endif + +#include +#include + +#define DLY 300.0 + +@class Command; + +static int tStatus = 0; +static Command *server = nil; + +static NSTimeInterval pingDelay = 240.0; + +static int comp_len = 0; + +static int comp(NSString *s0, NSString *s1) +{ + if ([s0 length] > [s1 length]) + { + comp_len = -1; + return -1; + } + if ([s1 compare: s0 + options: NSCaseInsensitiveSearch|NSLiteralSearch + range: NSMakeRange(0, [s0 length])] == NSOrderedSame) + { + comp_len = [s0 length]; + if (comp_len == (int)[s1 length]) + { + return 0; + } + else + { + return 1; + } + } + else + { + comp_len = -1; + return -1; + } +} + +static NSString* cmdWord(NSArray* a, unsigned int pos) +{ + if (a != nil && [a count] > pos) + { + return [a objectAtIndex: pos]; + } + else + { + return @""; + } +} + + +@interface Command : EcProcess +{ + NSString *host; + id control; + NSMutableArray *clients; + NSTimer *timer; + NSString *logname; + NSMutableDictionary *config; + NSDictionary *launchInfo; + NSDictionary *environment; + NSMutableDictionary *launches; + NSDate *past; + NSDate *future; + unsigned pingPosition; + NSTimer *terminating; + NSDate *lastUnanswered; + unsigned fwdSequence; + unsigned revSequence; + float nodesFree; + float spaceFree; +} +- (NSFileHandle*) openLog: (NSString*)lname; +- (void) cmdGnip: (id )from + sequence: (unsigned)num + extra: (NSData*)data; +- (void) cmdPing: (id )from + sequence: (unsigned)num + extra: (NSData*)data; +- (void) cmdQuit: (int)sig; +- (void) command: (NSData*)dat + to: (NSString*)t + from: (NSString*)f; +- (NSData *) configurationFor: (NSString *)name; +- (BOOL) connection: (NSConnection*)ancestor + shouldMakeNewConnection: (NSConnection*)newConn; +- (id) connectionBecameInvalid: (NSNotification*)notification; +- (NSArray*) findAll: (NSArray*)a + byAbbreviation: (NSString*)s; +- (ClientInfo*) findIn: (NSArray*)a + byAbbreviation: (NSString*)s; +- (ClientInfo*) findIn: (NSArray*)a + byName: (NSString*)s; +- (ClientInfo*) findIn: (NSArray*)a + byObject: (id)s; +- (void) information: (NSString*)inf + from: (NSString*)s + type: (EcLogType)t; +- (void) information: (NSString*)inf + from: (NSString*)s + to: (NSString*)d + type: (EcLogType)t; +- (void) launch; +- (void) logMessage: (NSString*)msg + type: (EcLogType)t + for: (id)o; +- (void) logMessage: (NSString*)msg + type: (EcLogType)t + name: (NSString*)c; +- (void) newConfig: (NSMutableDictionary*)newConfig; +- (void) pingControl; +- (void) quitAll; +- (void) requestConfigFor: (id)c; +- (NSData*) registerClient: (id)c + name: (NSString*)n; +- (NSData*) registerClient: (id)c + name: (NSString*)n + transient: (BOOL)t; +- (void) reply: (NSString*) msg to: (NSString*)n from: (NSString*)c; +- (void) terminate; +- (void) timedOut: (NSTimer*)t; +- (void) unregisterByObject: (id)obj; +- (void) unregisterByName: (NSString*)n; +- (void) update; +- (void) updateConfig: (NSData*)data; +@end + +@implementation Command + +- (oneway void) alarm: (in bycopy EcAlarm*)alarm +{ + NS_DURING + { + [control alarm: alarm]; + } + NS_HANDLER + { + NSLog(@"Exception sending alarm to Control: %@", localException); + } + NS_ENDHANDLER +} + +- (oneway void) domanage: (in bycopy NSString*)managedObject +{ + NS_DURING + { + [control domanage: managedObject]; + } + NS_HANDLER + { + NSLog(@"Exception sending domanage: to Control: %@", localException); + } + NS_ENDHANDLER +} + +- (oneway void) unmanage: (in bycopy NSString*)managedObject +{ + NS_DURING + { + [control unmanage: managedObject]; + } + NS_HANDLER + { + NSLog(@"Exception sending unmanage: to Control: %@", localException); + } + NS_ENDHANDLER +} + +- (NSFileHandle*) openLog: (NSString*)lname +{ + NSFileManager *mgr = [NSFileManager defaultManager]; + NSFileHandle *lf; + + if ([mgr isWritableFileAtPath: lname] == NO) + { + if ([mgr createFileAtPath: lname + contents: nil + attributes: nil] == NO) + { + NSLog(@"Log file '%@' is not writable and can't be created", lname); + return nil; + } + } + + lf = [NSFileHandle fileHandleForUpdatingAtPath: lname]; + if (lf == nil) + { + NSLog(@"Unable to log to %@", lname); + return nil; + } + [lf seekToEndOfFile]; + return lf; +} + +- (void) newConfig: (NSMutableDictionary*)newConfig +{ + NSString *diskCache; + NSData *data; + + diskCache = [[self cmdDataDirectory] + stringByAppendingPathComponent: @"CommandConfig.cache"]; + + if (NO == [newConfig isKindOfClass: [NSMutableDictionary class]] + || 0 == [newConfig count]) + { + /* If we are called with a nil argument, we must obtain the config + * from local disk cache (if available). + */ + if (nil != (data = [NSData dataWithContentsOfFile: diskCache])) + { + newConfig = [NSPropertyListSerialization + propertyListWithData: data + options: NSPropertyListMutableContainers + format: 0 + error: 0]; + } + if (NO == [newConfig isKindOfClass: [NSMutableDictionary class]] + || 0 == [newConfig count]) + { + return; + } + } + else + { + data = nil; + } + + if (nil == config || [config isEqual: newConfig] == NO) + { + NSDictionary *d; + NSArray *a; + unsigned i; + + ASSIGN(config, newConfig); + + d = [config objectForKey: [self cmdName]]; + DESTROY(launchInfo); + DESTROY(environment); + if ([d isKindOfClass: [NSDictionary class]] == YES) + { + NSString *k; + + launchInfo = [d objectForKey: @"Launch"]; + if ([launchInfo isKindOfClass: [NSDictionary class]] == NO) + { + NSLog(@"No 'Launch' information in latest config update"); + launchInfo = nil; + } + else + { + NSEnumerator *e = [launchInfo keyEnumerator]; + + while ((k = [e nextObject]) != nil) + { + NSDictionary *d = [launchInfo objectForKey: k]; + id o; + + if ([d isKindOfClass: [NSDictionary class]] == NO) + { + NSLog(@"bad 'Launch' information for %@", k); + launchInfo = nil; + break; + } + o = [d objectForKey: @"Auto"]; + if (o != nil && [o isKindOfClass: [NSString class]] == NO) + { + NSLog(@"bad 'Launch' Auto for %@", k); + launchInfo = nil; + break; + } + o = [d objectForKey: @"Disabled"]; + if (o != nil && [o isKindOfClass: [NSString class]] == NO) + { + NSLog(@"bad 'Launch' Disabled for %@", k); + launchInfo = nil; + break; + } + o = [d objectForKey: @"Args"]; + if (o != nil && [o isKindOfClass: [NSArray class]] == NO) + { + NSLog(@"bad 'Launch' Args for %@", k); + launchInfo = nil; + break; + } + o = [d objectForKey: @"Home"]; + if (o != nil && [o isKindOfClass: [NSString class]] == NO) + { + NSLog(@"bad 'Launch' Home for %@", k); + launchInfo = nil; + break; + } + o = [d objectForKey: @"Prog"]; + if (o == nil || [o isKindOfClass: [NSString class]] == NO) + { + NSLog(@"bad 'Launch' Prog for %@", k); + launchInfo = nil; + break; + } + o = [d objectForKey: @"AddE"]; + if (o != nil && [o isKindOfClass: [NSDictionary class]] == NO) + { + NSLog(@"bad 'Launch' AddE for %@", k); + launchInfo = nil; + break; + } + o = [d objectForKey: @"SetE"]; + if (o != nil && [o isKindOfClass: [NSDictionary class]] == NO) + { + NSLog(@"bad 'Launch' SetE for %@", k); + launchInfo = nil; + break; + } + } + } + RETAIN(launchInfo); + environment = [d objectForKey: @"Environment"]; + if ([environment isKindOfClass: [NSDictionary class]] == NO) + { + NSLog(@"No 'Environment' information in latest config update"); + environment = nil; + } + RETAIN(environment); + + k = [d objectForKey: @"NodesFree"]; + if (YES == [k isKindOfClass: [NSString class]]) + { + nodesFree = [k floatValue]; + nodesFree /= 100.0; + } + else + { + nodesFree = 0.0; + } + if (nodesFree < 0.02 || nodesFree > 0.9) + { + NSLog(@"bad or missing minimum disk 'NodesFree' ... using 10%%"); + nodesFree = 0.1; + } + k = [d objectForKey: @"SpaceFree"]; + if (YES == [k isKindOfClass: [NSString class]]) + { + spaceFree = [k floatValue]; + spaceFree /= 100.0; + } + else + { + spaceFree = 0.0; + } + if (spaceFree < 0.02 || spaceFree > 0.9) + { + NSLog(@"bad or missing minimum disk 'SpaceFree' ... using 10%%"); + spaceFree = 0.1; + } + } + else + { + NSLog(@"No '%@' information in latest config update", [self cmdName]); + } + + a = [NSArray arrayWithArray: clients]; + i = [a count]; + while (i-- > 0) + { + ClientInfo *c = [a objectAtIndex: i]; + + if ([clients indexOfObjectIdenticalTo: c] != NSNotFound) + { + NS_DURING + { + NSData *d = [self configurationFor: [c name]]; + + if (nil != d) + { + [c setConfig: d]; + [[c obj] updateConfig: d]; + } + } + NS_HANDLER + { + NSLog(@"Setting config for client: %@", localException); + } + NS_ENDHANDLER + } + } + if (nil == data) + { + /* Need to update on-disk cache + */ + data = [NSPropertyListSerialization + dataFromPropertyList: newConfig + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + [data writeToFile: diskCache atomically: YES]; + } + } +} + +- (void) pingControl +{ + if (control == nil) + { + return; + } + if (fwdSequence == revSequence) + { + lastUnanswered = RETAIN([NSDate date]); + NS_DURING + { + [control cmdPing: self sequence: ++fwdSequence extra: nil]; + } + NS_HANDLER + { + NSLog(@"Ping to control server - %@", localException); + } + NS_ENDHANDLER + } + else + { + NSLog(@"Ping to control server when one is already in progress."); + } +} + +- (void) cmdGnip: (id )from + sequence: (unsigned)num + extra: (NSData*)data +{ + if (from == control) + { + if (num != revSequence + 1 && revSequence != 0) + { + NSLog(@"Gnip from control server seq: %u when expecting %u", + num, revSequence); + if (num == 0) + { + fwdSequence = 0; // Reset + } + } + revSequence = num; + if (revSequence == fwdSequence) + { + DESTROY(lastUnanswered); + } + } + else + { + ClientInfo *r; + + /* + * See if we have a fitting client - and update records. + */ + r = [self findIn: clients byObject: (id)from]; + if (r != nil) + { + [r gnip: num]; + } + } +} + +- (BOOL) cmdIsClient +{ + return NO; // Not a client of the Command server. +} + +- (void) cmdPing: (id )from + sequence: (unsigned)num + extra: (NSData*)data +{ + /* + * Just send back a response to let the other party know we are alive. + */ + [from cmdGnip: self sequence: num extra: nil]; +} + +- (void) cmdQuit: (int)sig +{ + if (sig == tStatus && control != nil) + { + NS_DURING + { + [control unregister: self]; + } + NS_HANDLER + { + NSLog(@"Exception unregistering from Control: %@", localException); + } + NS_ENDHANDLER + } + exit(sig); +} + +- (void) command: (NSData*)dat + to: (NSString*)t + from: (NSString*)f +{ + NSMutableArray *cmd = [NSPropertyListSerialization + propertyListWithData: dat + options: NSPropertyListMutableContainers + format: 0 + error: 0]; + + if (cmd == nil || [cmd count] == 0) + { + [self information: cmdLogFormat(LT_ERROR, @"bad command array") + from: nil + to: f + type: LT_ERROR]; + } + else if (t == nil) + { + NSString *m = @""; + NSString *wd = cmdWord(cmd, 0); + + if ([wd length] == 0) + { + /* Quietly ignore. */ + } + else if (comp(wd, @"archive") >= 0) + { + NSCalendarDate *when = [NSCalendarDate date]; + NSString *sub; + int yy, mm, dd; + + yy = [when yearOfCommonEra]; + mm = [when monthOfYear]; + dd = [when dayOfMonth]; + + sub = [NSString stringWithFormat: @"%04d-%02d-%02d", yy, mm, dd]; + m = [NSString stringWithFormat: @"\n%@\n\n", [self cmdArchive: sub]]; + } + else if (comp(wd, @"help") >= 0) + { + wd = cmdWord(cmd, 1); + if ([wd length] == 0) + { + m = @"Commands are -\n" + @"Help\tArchive\tControl\tLaunch\tList\tMemory\tQuit\tTell\n\n" + @"Type 'help' followed by a command word for details.\n" + @"A command line consists of a sequence of words, " + @"the first of which is the command to be executed. " + @"A word can be a simple sequence of non-space characters, " + @"or it can be a 'quoted string'. " + @"Simple words are converted to lower case before " + @"matching them against commands and their parameters. " + @"Text in a 'quoted string' is NOT converted to lower case " + @"but a '\\' character is treated in a special manner -\n" + @" \\b is replaced by a backspace\n" + @" \\f is replaced by a formfeed\n" + @" \\n is replaced by a linefeed\n" + @" \\r is replaced by a carriage-return\n" + @" \\t is replaced by a tab\n" + @" \\0 followed by up to 3 octal digits is replaced" + @" by the octal value\n" + @" \\x followed by up to 2 hex digits is replaced" + @" by the hex value\n" + @" \\ followed by any other character is replaced by" + @" the second character.\n" + @" This permits use of quotes and backslashes inside" + @" a quoted string.\n"; + } + else + { + if (comp(wd, @"Archive") >= 0) + { + m = @"Archive\nArchives the log file. The archived log " + @"file is stored in a subdirectory whose name is of " + @"the form YYYYMMDDhhmmss being the date and time at " + @"which the archive was created.\n"; + } + else if (comp(wd, @"Connect") >= 0) + { + m = @"Connect ...\nPasses the command to the Control " + @"process. You may disconnect from this host by " + @"typing 'control host'\n"; + } + else if (comp(wd, @"Launch") >= 0) + { + m = @"Launch \nAdds the named program to the list " + @"of programs to be launched as soon as possible.\n"; + } + else if (comp(wd, @"List") >= 0) + { + m = @"List\nLists all the connected clients.\n" + @"List launches\nLists the programs we can launch.\n"; + } + else if (comp(wd, @"Memory") >= 0) + { + m = @"Memory\nDisplays recent memory allocation stats.\n" + @"Memory all\nDisplays all memory allocation stats.\n"; + } + else if (comp(wd, @"Quit") >= 0) + { + m = @"Quit 'name'\n" + @"Shuts down the named client process(es).\n" + @"Quit all\n" + @"Shuts down all client processes.\n" + @"Quit self\n" + @"Shuts down the command server for this host.\n"; + } + else if (comp(wd, @"Tell") >= 0) + { + m = @"Tell 'name' 'command'\n" + @"Sends the command to the named client(s).\n" + @"You may use 'tell all ...' to send to all clients.\n"; + } + } + } + else if (comp(wd, @"launch") >= 0) + { + if ([cmd count] > 1) + { + if (launchInfo != nil) + { + NSEnumerator *enumerator; + NSString *key; + NSString *nam = [cmd objectAtIndex: 1]; + BOOL found = NO; + + enumerator = [launchInfo keyEnumerator]; + while ((key = [enumerator nextObject]) != nil) + { + if (comp(nam, key) >= 0) + { + ClientInfo *r; + + found = YES; + r = [self findIn: clients byName: key]; + if (r == nil) + { + [launches setObject: past forKey: key]; + m = @"Ok - I will launch that program " + @"when I get a chance.\n"; + } + else + { + m = @"That program is already running\n"; + } + } + } + if (found == NO) + { + m = @"I don't know how to launch that program.\n"; + } + } + else + { + m = @"There are no programs we can launch.\n"; + } + } + else + { + m = @"I need the name of a program to launch.\n"; + } + } + else if (comp(wd, @"list") >= 0) + { + wd = cmdWord(cmd, 1); + if ([wd length] == 0 || comp(wd, @"clients") >= 0) + { + if ([clients count] == 0) + { + m = @"No clients currently connected.\n"; + } + else + { + unsigned i; + + m = @"Current client processes -\n"; + for (i = 0; i < [clients count]; i++) + { + ClientInfo* c = [clients objectAtIndex: i]; + + m = [NSString stringWithFormat: + @"%@%2d. %-32.32s\n", m, i, [[c name] cString]]; + } + } + } + else if (comp(wd, @"launches") >= 0) + { + if (launchInfo != nil) + { + NSEnumerator *enumerator; + NSString *key; + NSDate *date; + NSDate *now = [NSDate date]; + + m = @"Programs we can launch -\n"; + enumerator = [[[launchInfo allKeys] sortedArrayUsingSelector: + @selector(compare:)] objectEnumerator]; + while ((key = [enumerator nextObject]) != nil) + { + ClientInfo *r; + NSDictionary *inf = [launchInfo objectForKey: key]; + + m = [m stringByAppendingFormat: @" %-32.32s ", + [key cString]]; + r = [self findIn: clients byName: key]; + if (r == nil) + { + if ([[inf objectForKey: @"Disabled"] boolValue]==YES) + { + m = [m stringByAppendingString: + @"disabled in config\n"]; + } + else if ([[inf objectForKey: @"Auto"] boolValue]==NO) + { + date = [launches objectForKey: key]; + if (date == nil || date == future) + { + m = [m stringByAppendingString: + @"may be launched manually\n"]; + } + else if ([now timeIntervalSinceDate: date] > DLY) + { + m = [m stringByAppendingString: + @"ready to autolaunch now\n"]; + } + else + { + m = [m stringByAppendingString: + @"autolaunch in a few minutes\n"]; + } + } + else + { + date = [launches objectForKey: key]; + if (date == nil) + { + date = now; + [launches setObject: date forKey: key]; + } + if (date == future) + { + m = [m stringByAppendingString: + @"manually suspended\n"]; + } + else + { + if ([now timeIntervalSinceDate: date] > DLY) + { + m = [m stringByAppendingString: + @"ready to autolaunch now\n"]; + } + else + { + m = [m stringByAppendingString: + @"autolaunch in a few minutes\n"]; + } + } + } + } + else + { + m = [m stringByAppendingString: @"running\n"]; + } + } + if ([launchInfo count] == 0) + { + m = [m stringByAppendingString: @"nothing\n"]; + } + } + else + { + m = @"There are no programs we can launch.\n"; + } + } + } + else if (comp(wd, @"memory") >= 0) + { + if (GSDebugAllocationActive(YES) == NO) + { + m = @"Memory statistics were not being gathered.\n" + @"Statistics Will start from NOW.\n"; + } + else + { + const char* list; + + wd = cmdWord(cmd, 1); + if ([wd length] > 0 && comp(wd, @"all") >= 0) + { + list = GSDebugAllocationList(NO); + } + else + { + list = GSDebugAllocationList(YES); + } + m = [NSString stringWithCString: list]; + } + } + else if (comp(wd, @"quit") >= 0) + { + wd = cmdWord(cmd, 1); + if ([wd length] > 0) + { + if (comp(wd, @"self") == 0) + { + if (terminating == nil) + { + NS_DURING + { + [control unregister: self]; + } + NS_HANDLER + { + NSLog(@"Exception unregistering from Control: %@", + localException); + } + NS_ENDHANDLER + exit(0); + } + else + { + m = @"Already terminating!\n"; + } + } + else if (comp(wd, @"all") == 0) + { + [self quitAll]; + + if ([clients count] == 0) + { + m = @"All clients have been shut down.\n"; + } + else if ([clients count] == 1) + { + m = @"One client did not shut down.\n"; + } + else + { + m = @"Some clients did not been shut down.\n"; + } + } + else + { + NSArray *a = [self findAll: clients byAbbreviation: wd]; + unsigned i; + BOOL found = NO; + + for (i = 0; i < [a count]; i++) + { + ClientInfo *c = [a objectAtIndex: i]; + BOOL found = NO; + + NS_DURING + { + [launches setObject: future forKey: [c name]]; + m = [m stringByAppendingFormat: + @"Sent 'quit' to '%@'\n", [c name]]; + m = [m stringByAppendingString: + @" Please wait for this to be 'removed' before " + @"proceeding.\n"]; + [[c obj] cmdQuit: 0]; + found = YES; + } + NS_HANDLER + { + NSLog(@"Caught exception: %@", localException); + } + NS_ENDHANDLER + } + if (launchInfo != nil) + { + NSEnumerator *enumerator; + NSString *key; + + enumerator = [launchInfo keyEnumerator]; + while ((key = [enumerator nextObject]) != nil) + { + if (comp(wd, key) >= 0) + { + NSDate *when = [launches objectForKey: key]; + + found = YES; + [launches setObject: future forKey: key]; + if (when != future) + { + m = [m stringByAppendingFormat: + @"Suspended %@\n", key]; + } + } + } + } + if (found == NO) + { + m = [NSString stringWithFormat: + @"Nothing to shut down as '%@'\n", wd]; + } + } + } + else + { + m = @"Quit what?.\n"; + } + } + else if (comp(wd, @"tell") >= 0) + { + wd = cmdWord(cmd, 1); + if ([wd length] > 0) + { + NSString *dest = AUTORELEASE(RETAIN(wd)); + + [cmd removeObjectAtIndex: 0]; + [cmd removeObjectAtIndex: 0]; + if (comp(dest, @"all") == 0) + { + unsigned i; + NSArray *a = [[NSArray alloc] initWithArray: clients]; + + for (i = 0; i < [a count]; i++) + { + ClientInfo* c = [a objectAtIndex: i]; + + if ([clients indexOfObjectIdenticalTo: c]!=NSNotFound) + { + NS_DURING + { + NSData *dat = [NSPropertyListSerialization + dataFromPropertyList: cmd + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + [[c obj] cmdMesgData: dat from: f]; + m = @"Sent message.\n"; + } + NS_HANDLER + { + NSLog(@"Caught exception: %@", localException); + } + NS_ENDHANDLER + } + } + } + else + { + NSArray *a; + + a = [self findAll: clients byAbbreviation: dest]; + if ([a count] == 0) + { + m = [NSString stringWithFormat: + @"No such client as '%@'\n", dest]; + } + else + { + unsigned i; + + m = nil; + + for (i = 0; i < [a count]; i++) + { + ClientInfo *c = [a objectAtIndex: i]; + + NS_DURING + { + NSData *dat = [NSPropertyListSerialization + dataFromPropertyList: cmd + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + + [[c obj] cmdMesgData: dat from: f]; + if (m == nil) + { + m = [NSString stringWithFormat: + @"Sent message to %@", [c name]]; + } + else + { + m = [m stringByAppendingFormat: + @", %@", [c name]]; + } + } + NS_HANDLER + { + NSLog(@"Caught exception: %@", localException); + if (m == nil) + { + m = @"Failed to send message!"; + } + else + { + m = [m stringByAppendingFormat: + @", failed to send to %@", [c name]]; + } + } + NS_ENDHANDLER + } + if (m != nil) + m = [m stringByAppendingString: @"\n"]; + } + } + } + else + { + m = @"Tell where?.\n"; + } + } + else + { + m = [NSString stringWithFormat: @"Unknown command - '%@'\n", wd]; + } + + [self information: m from: t to: f type: LT_AUDIT]; + } + else + { + ClientInfo *client = [self findIn: clients byName: t]; + + if (client) + { + NS_DURING + { + NSData *dat = [NSPropertyListSerialization + dataFromPropertyList: cmd + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + [[client obj] cmdMesgData: dat from: f]; + } + NS_HANDLER + { + NSLog(@"Caught exception: %@", localException); + } + NS_ENDHANDLER + } + else + { + NSString *m; + + m = cmdLogFormat(LT_ERROR, @"command to unregistered client"); + [self information: m + from: nil + to: f + type: LT_ERROR]; + } + } +} + +- (NSData *) configurationFor: (NSString *)name +{ + NSMutableDictionary *dict; + NSString *base; + NSRange r; + id o; + + if (nil == config) + { + return nil; // Not available + } + + r = [name rangeOfString: @"-" + options: NSBackwardsSearch | NSLiteralSearch]; + if (r.length > 0) + { + base = [name substringToIndex: r.location]; + } + else + { + base = nil; + } + + dict = [NSMutableDictionary dictionaryWithCapacity: 2]; + o = [config objectForKey: @"*"]; + if (o != nil) + { + [dict setObject: o forKey: @"*"]; + } + + o = [config objectForKey: name]; // Lookup config + if (base != nil) + { + if (nil == o) + { + /* No instance specific config found for server, + * try using the base server name without instance ID. + */ + o = [config objectForKey: base]; + } + else + { + id tmp; + + /* We found instance specific configuration for the server, + * so we merge by taking values from generic server config + * (if any) and overwriting them with instance specific values. + */ + tmp = [config objectForKey: base]; + if ([tmp isKindOfClass: [NSDictionary class]] + && [o isKindOfClass: [NSDictionary class]]) + { + tmp = [[tmp mutableCopy] autorelease]; + [tmp addEntriesFromDictionary: o]; + o = tmp; + } + } + } + if (o != nil) + { + [dict setObject: o forKey: name]; + } + + o = [config objectForKey: @"Operators"]; + if (o != nil) + { + [dict setObject: o forKey: @"Operators"]; + } + + return [NSPropertyListSerialization + dataFromPropertyList: dict + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; +} + +- (BOOL) connection: (NSConnection*)ancestor + shouldMakeNewConnection: (NSConnection*)newConn +{ + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(connectionBecameInvalid:) + name: NSConnectionDidDieNotification + object: (id)newConn]; + [newConn setDelegate: self]; + return YES; +} + +- (id) connectionBecameInvalid: (NSNotification*)notification +{ + id conn = [notification object]; + + [[NSNotificationCenter defaultCenter] + removeObserver: self + name: NSConnectionDidDieNotification + object: conn]; + if ([conn isKindOfClass: [NSConnection class]]) + { + NSMutableArray *a = [NSMutableArray arrayWithCapacity: 2]; + NSMutableString *l = [NSMutableString stringWithCapacity: 20]; + NSMutableString *e = [NSMutableString stringWithCapacity: 20]; + NSMutableString *m = [NSMutableString stringWithCapacity: 20]; + BOOL lostClients = NO; + unsigned i; + + if (control && [(NSDistantObject*)control connectionForProxy] == conn) + { + [[self cmdLogFile: logname] + puts: @"Lost connection to control server.\n"]; + DESTROY(control); + } + + /* + * Now remove the clients from the active list. + */ + i = [clients count]; + while (i-- > 0) + { + ClientInfo* o = [clients objectAtIndex: i]; + + if ([(id)[o obj] connectionForProxy] == conn) + { + NSString *name = [o name]; + NSString *s; + + lostClients = YES; + [a addObject: o]; + [clients removeObjectAtIndex: i]; + if (i <= pingPosition && pingPosition > 0) + { + pingPosition--; + } + if ([o transient] == NO) + { + EcAlarm *a; + + s = EcMakeManagedObject(host, name, nil); + a = [EcAlarm alarmForManagedObject: s + at: nil + withEventType: EcAlarmEventTypeProcessingError + probableCause: EcAlarmSoftwareProgramAbnormallyTerminated + specificProblem: @"Process availability" + perceivedSeverity: EcAlarmSeverityCritical + proposedRepairAction: @"Check system status" + additionalText: @"removed (lost) server"]; + [control alarm: a]; + } + else + { + s = [NSString stringWithFormat: cmdLogFormat(LT_DEBUG, + @"removed (lost) server - '%@' on %@"), name, host]; + [l appendString: s]; + } + } + } + + [a removeAllObjects]; + + if ([l length] > 0) + { + [[self cmdLogFile: logname] puts: l]; + } + if ([m length] > 0) + { + [self information: m from: nil to: nil type: LT_ALERT]; + } + if ([e length] > 0) + { + [self information: e from: nil to: nil type: LT_ERROR]; + } + if (lostClients) + { + [self update]; + } + } + else + { + [self error: "non-Connection sent invalidation"]; + } + return self; +} + +- (void) dealloc +{ + [self cmdLogEnd: logname]; + if (timer != nil) + { + [timer invalidate]; + } + RELEASE(past); + RELEASE(future); + RELEASE(launches); + DESTROY(control); + RELEASE(host); + RELEASE(clients); + RELEASE(launchInfo); + RELEASE(environment); + RELEASE(lastUnanswered); + [super dealloc]; +} + +- (NSArray*) findAll: (NSArray*)a + byAbbreviation: (NSString*)s +{ + NSMutableArray *r = [NSMutableArray arrayWithCapacity: 4]; + int i; + + /* + * Special case - a numeric value is used as an index into the array. + */ + if (isdigit(*[s cString])) + { + i = [s intValue]; + if (i >= 0 && i < (int)[a count]) + { + [r addObject: [a objectAtIndex: i]]; + } + } + else + { + ClientInfo *o; + + for (i = 0; i < (int)[a count]; i++) + { + o = (ClientInfo*)[a objectAtIndex: i]; + if (comp(s, [o name]) == 0 || comp_len == (int)[s length]) + { + [r addObject: o]; + } + } + } + return r; +} + +- (ClientInfo*) findIn: (NSArray*)a + byAbbreviation: (NSString*)s +{ + ClientInfo *o; + int i; + int best_pos = -1; + int best_len = 0; + + /* + * Special case - a numeric value is used as an index into the array. + */ + if (isdigit(*[s cString])) + { + i = [s intValue]; + if (i >= 0 && i < (int)[a count]) + { + return (ClientInfo*)[a objectAtIndex: i]; + } + } + + for (i = 0; i < (int)[a count]; i++) + { + o = (ClientInfo*)[a objectAtIndex: i]; + if (comp(s, [o name]) == 0) + { + return o; + } + if (comp_len > best_len) + { + best_len = comp_len; + best_pos = i; + } + } + if (best_pos >= 0) + { + return (ClientInfo*)[a objectAtIndex: best_pos]; + } + return nil; +} + +- (ClientInfo*) findIn: (NSArray*)a + byName: (NSString*)s +{ + ClientInfo *o; + int i; + + for (i = 0; i < (int)[a count]; i++) + { + o = (ClientInfo*)[a objectAtIndex: i]; + + if (comp([o name], s) == 0) + { + return o; + } + } + return nil; +} + +- (ClientInfo*) findIn: (NSArray*)a + byObject: (id)s +{ + ClientInfo *o; + int i; + + for (i = 0; i < (int)[a count]; i++) + { + o = (ClientInfo*)[a objectAtIndex: i]; + + if ([o obj] == s) + { + return o; + } + } + return nil; +} + +- (void) flush +{ + /* + * Flush logs to disk ... dummy method as we don't cache them at present. + */ +} + +- (void) information: (NSString*)inf + from: (NSString*)s + type: (EcLogType)t +{ + [self information: inf from: s to: nil type: t]; +} + +- (void) information: (NSString*)inf + from: (NSString*)s + to: (NSString*)d + type: (EcLogType)t +{ + if (t != LT_DEBUG && inf != nil && [inf length] > 0) + { + if (control == nil) + { + [self timedOut: nil]; + } + if (control == nil) + { + NSLog(@"Information (from:%@ to:%@ type:%d) with no Control -\n%@", + s, d, t, inf); + } + else + { + NS_DURING + { + [control information: inf type: t to: d from: s]; + } + NS_HANDLER + { + NSLog(@"Sending %@ from %@ to %@ type %x exception: %@", + inf, s, d, t, localException); + } + NS_ENDHANDLER + } + } +} + +- (id) initWithDefaults: (NSDictionary*)defs +{ + if (nil != (self = [super initWithDefaults: defs])) + { + nodesFree = 0.1; + spaceFree = 0.1; + + logname = [[self cmdName] stringByAppendingPathExtension: @"log"]; + RETAIN(logname); + if ([self cmdLogFile: logname] == nil) + { + exit(0); + } + host = RETAIN([[NSHost currentHost] name]); + clients = [[NSMutableArray alloc] initWithCapacity: 10]; + launches = [[NSMutableDictionary alloc] initWithCapacity: 10]; + past = RETAIN([NSDate distantPast]); + future = RETAIN([NSDate distantFuture]); + + timer = [NSTimer scheduledTimerWithTimeInterval: 5.0 + target: self + selector: @selector(timedOut:) + userInfo: nil + repeats: YES]; + [self timedOut: nil]; + } + return self; +} + +- (void) launch +{ + if (launchInfo != nil) + { + NSEnumerator *enumerator; + NSString *key; + NSDate *date; + NSString *firstKey = nil; + NSDate *firstDate = nil; + NSDate *now = [NSDate date]; + + enumerator = [launchInfo keyEnumerator]; + while ((key = [enumerator nextObject]) != nil) + { + ClientInfo *r = [self findIn: clients byName: key]; + + if (r == nil) + { + NSDictionary *taskInfo = [launchInfo objectForKey: key]; + BOOL disabled; + BOOL autoLaunch; + + autoLaunch = [[taskInfo objectForKey: @"Auto"] boolValue]; + disabled = [[taskInfo objectForKey: @"Disabled"] boolValue]; + + if (disabled == NO) + { + date = [launches objectForKey: key]; + if (date == nil) + { + if (autoLaunch == YES) + { + NSDate *start; + NSTimeInterval offset = -(DLY - 5.0); + + /* If there is no launch date, we set launch + * dates so that we can try this in 5 seconds. + */ + start = [NSDate dateWithTimeIntervalSinceNow: offset]; + [launches setObject: start forKey: key]; + date = start; + } + } + if (date != nil) + { + if (firstDate == nil + || [date earlierDate: firstDate] == date) + { + firstDate = date; + firstKey = key; + } + } + } + } + } + + key = firstKey; + date = firstDate; + + if (date != nil && [now timeIntervalSinceDate: date] > DLY) + { + NSDictionary *taskInfo = [launchInfo objectForKey: key]; + NSDictionary *env = environment; + NSArray *args = [taskInfo objectForKey: @"Args"]; + NSString *home = [taskInfo objectForKey: @"Home"]; + NSString *prog = [taskInfo objectForKey: @"Prog"]; + NSDictionary *addE = [taskInfo objectForKey: @"AddE"]; + NSDictionary *setE = [taskInfo objectForKey: @"SetE"]; + + [launches setObject: now forKey: key]; + + if (prog != nil && [prog length] > 0) + { + NSTask *task; + NSFileHandle *hdl; + + if (setE != nil) + { + env = setE; + } + else if (env == nil) + { + env = [[NSProcessInfo processInfo] environment]; + } + if (addE != nil) + { + NSMutableDictionary *e = [env mutableCopy]; + + [e addEntriesFromDictionary: addE]; + env = AUTORELEASE(e); + } + task = [NSTask new]; + [task setEnvironment: env]; + hdl = [NSFileHandle fileHandleWithNullDevice]; + NS_DURING + { + [task setLaunchPath: prog]; + + if ([task validatedLaunchPath] == nil) + { + NSString *m; + + m = [NSString stringWithFormat: + cmdLogFormat(LT_ERROR, + @"failed to launch %@ - not executable"), prog]; + [self information: m + from: nil + to: nil + type: LT_ERROR]; + prog = nil; + } + if (prog != nil) + { + [task setStandardInput: hdl]; + [task setStandardOutput: hdl]; + [task setStandardError: hdl]; + if (home != nil && [home length] > 0) + { + [task setCurrentDirectoryPath: home]; + } + if (args != nil) + { + [task setArguments: args]; + } + [task launch]; + [[self cmdLogFile: logname] + printf: @"%@ launched %@\n", [NSDate date], prog]; + } + } + NS_HANDLER + { + NSString *m; + + m = [NSString stringWithFormat: cmdLogFormat(LT_ERROR, + @"failed to launch %@ - %@"), prog, localException]; + [self information: m from: nil to: nil type: LT_ERROR]; + } + NS_ENDHANDLER + RELEASE(task); + } + else + { + NSString *m; + + m = [NSString stringWithFormat: cmdLogFormat(LT_ERROR, + @"bad program name to launch %@"), key]; + [self information: m from: nil to: nil type: LT_ERROR]; + } + } + } +} + +- (void) logMessage: (NSString*)msg + type: (EcLogType)t + for: (id)o +{ + ClientInfo* r = [self findIn: clients byObject: o]; + NSString *c; + + if (r == nil) + { + c = @"unregistered client"; + } + else + { + c = [r name]; + } + [self logMessage: msg + type: t + name: c]; +} + +- (void) logMessage: (NSString*)msg + type: (EcLogType)t + name: (NSString*)c +{ + NSString *m; + + switch (t) + { + case LT_DEBUG: + m = msg; + break; + + case LT_WARNING: + m = msg; + break; + + case LT_ERROR: + m = msg; + break; + + case LT_AUDIT: + m = msg; + break; + + case LT_ALERT: + m = msg; + break; + + default: + m = [NSString stringWithFormat: @"%@: Message of unknown type - %@", + c, msg]; + break; + } + + [[self cmdLogFile: logname] puts: m]; + [self information: m from: c to: nil type: t]; +} + +- (void) quitAll +{ + /* + * Suspend any task that might potentially be started. + */ + if (launchInfo != nil) + { + NSEnumerator *enumerator; + NSString *key; + + enumerator = [launchInfo keyEnumerator]; + while ((key = [enumerator nextObject]) != nil) + { + [launches setObject: future forKey: key]; + } + } + + if ([clients count] > 0) + { + unsigned i; + unsigned j; + NSMutableArray *a; + + /* Now we tell all connected clients to quit. + */ + i = [clients count]; + a = [NSMutableArray arrayWithCapacity: i]; + while (i-- > 0) + { + [a addObject: [[clients objectAtIndex: i] name]]; + } + for (i = 0; i < [a count]; i++) + { + ClientInfo *c; + NSString *n; + + n = [a objectAtIndex: i]; + c = [self findIn: clients byName: n]; + if (nil != c) + { + NS_DURING + { + [launches setObject: future forKey: n]; + [[c obj] cmdQuit: 0]; + } + NS_HANDLER + { + NSLog(@"Caught exception: %@", localException); + } + NS_ENDHANDLER + } + } + + /* Give the clients a short time to quit, and re-send + * the instruction to any which haven't budged. + */ + for (j = 0; j < 15; j++) + { + NSDate *next = [NSDate dateWithTimeIntervalSinceNow: 2.0]; + + while ([clients count] > 0 && [next timeIntervalSinceNow] > 0.0) + { + [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode + beforeDate: next]; + } + for (i = 0; i < [a count] && [clients count] > 0; i++) + { + ClientInfo *c; + NSString *n; + + n = [a objectAtIndex: i]; + c = [self findIn: clients byName: n]; + if (nil != c) + { + NS_DURING + { + [launches setObject: future forKey: n]; + [[c obj] cmdQuit: 0]; + } + NS_HANDLER + { + NSLog(@"Caught exception: %@", localException); + } + NS_ENDHANDLER + } + } + } + } +} + +/* + * Handle a request for re-config from a client. + */ +- (void) requestConfigFor: (id)c +{ + ClientInfo *info = [self findIn: clients byObject: (id)c]; + NSData *conf = [info config]; + + if (nil != conf) + { + NS_DURING + { + [[info obj] updateConfig: conf]; + } + NS_HANDLER + { + NSLog(@"Sending config to client: %@", localException); + } + NS_ENDHANDLER + } +} + +- (NSData*) registerClient: (id)c + name: (NSString*)n +{ + return [self registerClient: c name: n transient: NO]; +} + +- (NSData*) registerClient: (id)c + name: (NSString*)n + transient: (BOOL)t +{ + NSMutableDictionary *dict; + ClientInfo *obj; + ClientInfo *old; + NSString *m; + + [(NSDistantObject*)c setProtocolForProxy: @protocol(CmdClient)]; + + if (nil == config) + { + m = [NSString stringWithFormat: + @"%@ back-off new server with name '%@' on %@\n", + [NSDate date], n, host]; + [[self cmdLogFile: logname] puts: m]; + [self information: m from: nil to: nil type: LT_AUDIT]; + dict = [NSMutableDictionary dictionaryWithCapacity: 1]; + [dict setObject: @"configuration data not yet available." + forKey: @"back-off"]; + return [NSPropertyListSerialization + dataFromPropertyList: dict + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + } + + /* + * Create a new reference for this client. + */ + obj = [[ClientInfo alloc] initFor: c name: n with: self]; + + if ((old = [self findIn: clients byName: n]) == nil) + { + NSData *d; + + [clients addObject: obj]; + RELEASE(obj); + [clients sortUsingSelector: @selector(compare:)]; + /* + * If this client is in the list of launchable clients, set + * it's last launch time to now so that if it dies it will + * be restarted. This overrides any previous shutdown - we + * assume that if it has been started by some process + * external to the Command server then we really don't want + * it shut down. + */ + if ([launches objectForKey: n] != nil) + { + [launches setObject: [NSDate date] forKey: n]; + } + m = [NSString stringWithFormat: + @"%@ registered new server with name '%@' on %@\n", + [NSDate date], n, host]; + [[self cmdLogFile: logname] puts: m]; + if (t == YES) + { + [obj setTransient: YES]; + } + else + { + NSString *s; + EcAlarm *a; + + [obj setTransient: NO]; + s = EcMakeManagedObject(host, n, nil); + a = [EcAlarm alarmForManagedObject: s + at: nil + withEventType: EcAlarmEventTypeProcessingError + probableCause: EcAlarmSoftwareProgramAbnormallyTerminated + specificProblem: @"Process availability" + perceivedSeverity: EcAlarmSeverityCleared + proposedRepairAction: nil + additionalText: nil]; + [control alarm: a]; + [self information: m from: nil to: nil type: LT_AUDIT]; + } + [self update]; + d = [self configurationFor: n]; + if (nil != d) + { + [obj setConfig: d]; + } + return [obj config]; + } + else + { + RELEASE(obj); + m = [NSString stringWithFormat: + @"%@ rejected new server with name '%@' on %@\n", + [NSDate date], n, host]; + [[self cmdLogFile: logname] puts: m]; + [self information: m from: nil to: nil type: LT_AUDIT]; + dict = [NSMutableDictionary dictionaryWithCapacity: 1]; + [dict setObject: @"client with that name already registered." + forKey: @"rejected"]; + return [NSPropertyListSerialization + dataFromPropertyList: dict + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + } +} + +- (void) reply: (NSString*) msg to: (NSString*)n from: (NSString*)c +{ + if (control == nil) + { + [self timedOut: nil]; + } + if (control != nil) + { + NS_DURING + { + [control reply: msg to: n from: c]; + } + NS_HANDLER + { + NSLog(@"reply: %@ to: %@ from: %@ - %@", msg, n, c, localException); + } + NS_ENDHANDLER + } + else + { + } +} + +/* + * Tell all our clients to quit, and wait for them to do so. + * If called while already terminating ... force immediate shutdown. + */ +- (void) terminate: (NSTimer*)t +{ + if (terminating == nil) + { + [self information: @"Handling shutdown." + from: nil + to: nil + type: LT_AUDIT]; + } + + if (nil == terminating) + { + terminating = [NSTimer scheduledTimerWithTimeInterval: 10.0 + target: self selector: @selector(terminate:) + userInfo: [NSDate new] + repeats: YES]; + } + + [self quitAll]; + + if (t != nil) + { + NSDate *when = (NSDate*)[t userInfo]; + + if ([when timeIntervalSinceNow] < -60.0) + { + [[self cmdLogFile: logname] + puts: @"Final shutdown.\n"]; + [terminating invalidate]; + terminating = nil; + [self cmdQuit: tStatus]; + } + } +} + +- (void) terminate +{ + [self terminate: nil]; +} + +- (void) timedOut: (NSTimer*)t +{ + static BOOL inTimeout = NO; + NSDate *now = [t fireDate]; + + if (now == nil) + { + now = [NSDate date]; + } + + [[self cmdLogFile: logname] synchronizeFile]; + if (inTimeout == NO) + { + static unsigned pingControlCount = 0; + NSFileManager *mgr; + NSDictionary *a; + float f; + unsigned count; + BOOL lost = NO; + + inTimeout = YES; + if (control == nil) + { + NSUserDefaults *defs; + NSString *ctlName; + NSString *ctlHost; + id c; + + defs = [self cmdDefaults]; + ctlName = [defs stringForKey: @"ControlName"]; + if (ctlName == nil) + { + ctlName = @"Control"; + } + ctlHost = [defs stringForKey: @"ControlHost"]; + if (ctlHost == nil) + { + ctlHost = @"*"; + } + + NS_DURING + { + NSLog(@"Connecting to %@ on %@", ctlName, ctlHost); + control = (id)[NSConnection + rootProxyForConnectionWithRegisteredName: ctlName + host: ctlHost + usingNameServer: [NSSocketPortNameServer sharedInstance] ]; + } + NS_HANDLER + { + NSLog(@"Connecting to control server: %@", localException); + control = nil; + } + NS_ENDHANDLER + c = control; + if (RETAIN(c) != nil) + { + /* Re-initialise control server ping */ + DESTROY(lastUnanswered); + fwdSequence = 0; + revSequence = 0; + + [(NSDistantObject*)c setProtocolForProxy: @protocol(Control)]; + c = [(NSDistantObject*)c connectionForProxy]; + [c setDelegate: self]; + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(connectionBecameInvalid:) + name: NSConnectionDidDieNotification + object: c]; + NS_DURING + { + NSData *dat; + + dat = [control registerCommand: self + name: host]; + if (nil == dat) + { + // Control server not yet ready. + DESTROY(control); + } + else + { + NSMutableDictionary *conf; + + conf = [NSPropertyListSerialization + propertyListWithData: dat + options: NSPropertyListMutableContainers + format: 0 + error: 0]; + if ([conf objectForKey: @"rejected"] == nil) + { + [self updateConfig: dat]; + } + else + { + [[self cmdLogFile: logname] + printf: @"registration attempt rejected - %@\n", + [conf objectForKey: @"rejected"]]; + DESTROY(control); + } + } + } + NS_HANDLER + { + NSLog(@"Registering with control server: %@", localException); + DESTROY(control); + } + NS_ENDHANDLER + if (control != nil) + { + [self update]; + } + } + } + [self launch]; + + count = [clients count]; + while (count-- > 0) + { + ClientInfo *r = [clients objectAtIndex: count]; + NSDate *d = [r lastUnanswered]; + + if (d != nil && [d timeIntervalSinceDate: now] < -pingDelay) + { + NSString *m; + + m = [NSString stringWithFormat: cmdLogFormat(LT_ALERT, + @"Client '%@' failed to respond for over %d seconds"), + [r name], (int)pingDelay]; + [[[[r obj] connectionForProxy] sendPort] invalidate]; + [self information: m from: nil to: nil type: LT_ALERT]; + lost = YES; + } + } + if (control != nil && lastUnanswered != nil + && [lastUnanswered timeIntervalSinceDate: now] < -pingDelay) + { + NSString *m; + + m = [NSString stringWithFormat: cmdLogFormat(LT_ALERT, + @"Control server failed to respond for over %d seconds"), + (int)pingDelay]; + [[(NSDistantObject*)control connectionForProxy] invalidate]; + [self information: m from: nil to: nil type: LT_ALERT]; + lost = YES; + } + if (lost == YES) + { + [self update]; + } + /* + * We ping each client in turn. If there are fewer than 4 clients, + * we skip timeouts so that clients get pinged no more frequently + * than one per 4 timeouts. + */ + count = [clients count]; + if (pingPosition >= 4 && pingPosition >= count) + { + pingPosition = 0; + } + if (pingPosition < count) + { + [[clients objectAtIndex: pingPosition++] ping]; + } + // Ping the control server too - once every four times. + pingControlCount++; + if (pingControlCount >= 4) + { + pingControlCount = 0; + } + if (pingControlCount == 0) + { + [self pingControl]; + } + + /* See if the filesystem containing our logging directory has enough + * space. + */ + mgr = [NSFileManager defaultManager]; + a = [mgr fileSystemAttributesAtPath: cmdLogsDir(nil)]; + f = [[a objectForKey: NSFileSystemFreeSize] floatValue] + / [[a objectForKey: NSFileSystemSize] floatValue]; + if (f <= spaceFree) + { + static NSDate *last = nil; + + if (nil == last || [last timeIntervalSinceNow] < -DLY) + { + NSString *m; + + ASSIGN(last, [NSDate date]); + m = [NSString stringWithFormat: cmdLogFormat(LT_ALERT, + @"Disk space at %02.1f percent"), f * 100.0]; + [self information: m from: nil to: nil type: LT_ALERT]; + } + } + f = [[a objectForKey: NSFileSystemFreeNodes] floatValue] + / [[a objectForKey: NSFileSystemNodes] floatValue]; + if (f <= nodesFree) + { + static NSDate *last = nil; + + if (nil == last || [last timeIntervalSinceNow] < -DLY) + { + NSString *m; + + ASSIGN(last, [NSDate date]); + m = [NSString stringWithFormat: cmdLogFormat(LT_ALERT, + @"Disk nodes at %02.1f percent"), f * 100.0]; + [self information: m from: nil to: nil type: LT_ALERT]; + } + } + } + inTimeout = NO; +} + +- (void) unregisterByObject: (id)obj +{ + ClientInfo *o = [self findIn: clients byObject: obj]; + + if (o != nil) + { + NSString *m; + unsigned i; + BOOL transient = [o transient]; + NSString *name = [[[o name] retain] autorelease]; + + m = [NSString stringWithFormat: + @"\n%@ removed (unregistered) server -\n '%@' on %@\n", + [NSDate date], name, host]; + [[self cmdLogFile: logname] puts: m]; + [o setUnregistered: YES]; + i = [clients indexOfObjectIdenticalTo: o]; + if (i != NSNotFound) + { + [clients removeObjectAtIndex: i]; + if (i <= pingPosition && pingPosition > 0) + { + pingPosition--; + } + } + if (transient == NO) + { + [control unmanage: EcMakeManagedObject(host, name, nil)]; + [self information: m from: nil to: nil type: LT_AUDIT]; + } + [self update]; + } +} + +- (void) unregisterByName: (NSString*)n +{ + ClientInfo *o = [self findIn: clients byName: n]; + + if (o) + { + NSString *m; + unsigned i; + BOOL transient = [o transient]; + NSString *name = [[[o name] retain] autorelease]; + + m = [NSString stringWithFormat: + @"\n%@ removed (unregistered) server -\n '%@' on %@\n", + [NSDate date], name, host]; + [[self cmdLogFile: logname] puts: m]; + [o setUnregistered: YES]; + i = [clients indexOfObjectIdenticalTo: o]; + if (i != NSNotFound) + { + [clients removeObjectAtIndex: i]; + if (i <= pingPosition && pingPosition > 0) + { + pingPosition--; + } + } + if (transient == NO) + { + [control unmanage: EcMakeManagedObject(host, name, nil)]; + [self information: m from: nil to: nil type: LT_AUDIT]; + } + [self update]; + } +} + +- (void) update +{ + if (control == nil) + { + [self timedOut: nil]; + } + if (control) + { + NS_DURING + { + NSMutableArray *a; + int i; + + a = [NSMutableArray arrayWithCapacity: [clients count]]; + for (i = 0; i < (int)[clients count]; i++) + { + ClientInfo *c; + + c = [clients objectAtIndex: i]; + [a addObject: [c name]]; + } + [control servers: [NSPropertyListSerialization + dataFromPropertyList: a + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0] on: self]; + } + NS_HANDLER + { + NSLog(@"Exception sending servers to Control: %@", localException); + } + NS_ENDHANDLER + } + if (terminating != nil && [clients count] == 0) + { + [self information: @"Final shutdown." + from: nil + to: nil + type: LT_AUDIT]; + [terminating invalidate]; + terminating = nil; + [self cmdQuit: tStatus]; + } +} + +- (void) updateConfig: (NSData*)data +{ + NSMutableDictionary *info; + NSMutableDictionary *dict; + NSMutableDictionary *newConfig; + NSDictionary *operators; + NSEnumerator *enumerator; + NSString *key; + + /* Ignore invalid/empty configuration + */ + if (nil == data) + { + return; + } + info = [NSPropertyListSerialization + propertyListWithData: data + options: NSPropertyListMutableContainers + format: 0 + error: 0]; + if (NO == [info isKindOfClass: [NSMutableDictionary class]] + || 0 == [info count]) + { + return; + } + + newConfig = [NSMutableDictionary dictionaryWithCapacity: 32]; + /* + * Put all values for this host in the config dictionary. + */ + dict = [info objectForKey: host]; + if (dict) + { + [newConfig addEntriesFromDictionary: dict]; + } + /* + * Add any default values to the config dictionary where we don't have + * host specific values. + */ + dict = [info objectForKey: @"*"]; + if (dict) + { + enumerator = [dict keyEnumerator]; + while ((key = [enumerator nextObject]) != nil) + { + NSMutableDictionary *partial = [newConfig objectForKey: key]; + NSMutableDictionary *general = [dict objectForKey: key]; + NSString *app = key; + + if (partial == nil) + { + /* + * No host-specific info for this application - + * Use the general stuff for the application. + */ + [newConfig setObject: general forKey: key]; + } + else + { + NSEnumerator *another = [general keyEnumerator]; + + /* + * Merge in any values for this application which + * exist in the general stuff, but not in the host + * specific area. + */ + while ((key = [another nextObject]) != nil) + { + if ([partial objectForKey: key] == nil) + { + id obj = [general objectForKey: key]; + + [partial setObject: obj forKey: key]; + } + else + { + [[self cmdLogFile: logname] + printf: @"General config for %@/%@ overridden by" + @" host-specific version\n", app, key]; + } + } + } + } + } + + /* + * Add the list of operators to the config. + */ + operators = [info objectForKey: @"Operators"]; + if (operators != nil) + { + [newConfig setObject: operators forKey: @"Operators"]; + } + + /* Finally, replace old config with new if they differ. + */ + [self newConfig: newConfig]; +} + +@end + +static RETSIGTYPE +thandler(int sig) +{ + tStatus = SIGTERM; + [server terminate]; +#if RETSIGTYPE != void + return 0; +#endif +} + +void +inner_main() +{ + extern NSString *cmdVersion(NSString*); + NSAutoreleasePool *arp; + struct rlimit rlim; + NSDictionary *defs; + + arp = [NSAutoreleasePool new]; + + cmdVersion(@"$Date: 2012-02-13 08:11:49 +0000 (Mon, 13 Feb 2012) $ $Revision: 65934 $"); + + defs = [NSDictionary dictionaryWithObjectsAndKeys: + @"Command", @"HomeDirectory", + @"YES", @"Daemon", + nil]; + +#define CORESIZE 250000000 + if (getrlimit(RLIMIT_CORE, &rlim) < 0) + { + NSLog(@"Unable to get core file size limit: %d", errno); + } + else + { + if (rlim.rlim_max < CORESIZE) + { + NSLog(@"Hard limit for core file size (%lu) less than desired", + rlim.rlim_max); + } + else + { + rlim.rlim_cur = CORESIZE; + if (setrlimit(RLIMIT_CORE, &rlim) < 0) + { + NSLog(@"Unable to set core file size limit: %d", errno); + } + } + } + + server = [[Command alloc] initWithDefaults: defs]; + if (server == nil) + { + NSLog(@"Unable to create command object."); + exit(1); + } + + signal(SIGTERM, thandler); + + [server prcRun]; + + [arp release]; + + exit(0); +} + +int +main(int argc, char *argv[]) +{ + inner_main(); + return 0; +} diff --git a/Console.m b/Console.m new file mode 100644 index 0000000..bb644b5 --- /dev/null +++ b/Console.m @@ -0,0 +1,900 @@ + +#include + +#include "EcProcess.h" +#include "NSFileHandle+Printf.h" + +#include + +#if WITH_READLINE +# include +# include +# include +#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 +{ + 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; +} diff --git a/Control.m b/Control.m new file mode 100644 index 0000000..ea7592e --- /dev/null +++ b/Control.m @@ -0,0 +1,2548 @@ + +#import + +#import "EcAlarm.h" +#import "EcAlarmSinkSNMP.h" +#import "EcAlerter.h" +#import "EcHost.h" +#import "EcProcess.h" +#import "EcUserDefaults.h" +#import "NSFileHandle+Printf.h" +#import "Client.h" + +/* + * Catagory so that NSHost objects can be safely used as dictionary keys. + */ +@implementation NSHost (ControlExtension) +- (id) copyWithZone: (NSZone)z +{ + return RETAIN(self); +} +@end + +static EcAlarmSinkSNMP *sink = nil; + +static NSTimeInterval pingDelay = 240.0; + +static int comp_len = 0; + +static int comp(NSString *s0, NSString *s1) +{ + if ([s0 length] > [s1 length]) + { + comp_len = -1; + return -1; + } + if ([s1 compare: s0 + options: NSCaseInsensitiveSearch|NSLiteralSearch + range: NSMakeRange(0, [s0 length])] == NSOrderedSame) + { + comp_len = [s0 length]; + if (comp_len == [s1 length]) + { + return 0; + } + else + { + return 1; + } + } + else + { + comp_len = -1; + return -1; + } +} + +static NSString* cmdWord(NSArray* a, unsigned int pos) +{ + if (a != nil && [a count] > pos) + { + return [a objectAtIndex: pos]; + } + else + { + return @""; + } +} + + +@interface CommandInfo : ClientInfo +{ + NSArray *servers; +} +- (NSString*) serverByAbbreviation: (NSString*)s; +- (NSArray*) servers; +- (void) setServers: (NSArray*)s; +@end + +@implementation CommandInfo + +- (void) dealloc +{ + DESTROY(servers); + [super dealloc]; +} + +- (NSString*) serverByAbbreviation: (NSString*)s +{ + NSString *svr; + int i; + int best_pos = -1; + int best_len = 0; + + /* + * Special case - a numeric value is used as an index into the array. + */ + if (isdigit(*[s cString])) + { + i = [s intValue]; + if (i >= 0 && i < [servers count]) + { + svr = [servers objectAtIndex: i]; + return svr; + } + } + + for (i = 0; i < [servers count]; i++) + { + svr = [servers objectAtIndex: i]; + if (comp(s, svr) == 0) + { + return svr; + } + if (comp_len > best_len) + { + best_len = comp_len; + best_pos = i; + } + } + if (best_pos >= 0) + { + svr = [servers objectAtIndex: best_pos]; + return svr; + } + return nil; +} + +- (NSArray*) servers +{ + return servers; +} + +- (void) setServers: (NSArray*)s +{ + ASSIGN(servers, s); +} +@end + + +@interface ConsoleInfo : ClientInfo +{ + NSString *cserv; + NSString *chost; + NSString *pass; + BOOL general; + BOOL warnings; + BOOL errors; + BOOL alerts; +} +- (id) initFor: (id)o + name: (NSString*)n + with: (id)s + pass: (NSString*)p; +- (NSString*) chost; +- (NSString*) cserv; +- (BOOL) getAlerts; +- (BOOL) getErrors; +- (BOOL) getGeneral; +- (BOOL) getWarnings; +- (NSString*) pass; +- (NSString*) promptAfter: (NSString*)msg; +- (void) setAlerts: (BOOL)flag; +- (void) setConnectedHost: (NSString*)c; +- (void) setConnectedServ: (NSString*)c; +- (void) setErrors: (BOOL)flag; +- (void) setGeneral: (BOOL)flag; +- (void) setWarnings: (BOOL)flag; +@end + +@implementation ConsoleInfo + +- (NSString*) chost +{ + return chost; +} + +- (NSString*) cserv +{ + return cserv; +} + +- (void) dealloc +{ + DESTROY(chost); + DESTROY(cserv); + DESTROY(pass); + [super dealloc]; +} + +- (BOOL) getAlerts +{ + return alerts; +} + +- (BOOL) getErrors +{ + return errors; +} + +- (BOOL) getGeneral +{ + return general; +} + +- (BOOL) getWarnings +{ + return warnings; +} + +- (id) initFor: (id)o + name: (NSString*)n + with: (id)s + pass: (NSString*)p +{ + self = [super initFor: o name: n with: s]; + if (self != nil) + { + pass = [p copy]; + chost = nil; + cserv = nil; + general = NO; + warnings = NO; + alerts = YES; + errors = YES; + } + return self; +} + +- (NSString*) pass +{ + return pass; +} + +- (NSString*) promptAfter: (NSString*)msg +{ + if (chost && cserv) + { + return [NSString stringWithFormat: @"%@\n%@:%@> ", msg, chost, cserv]; + } + else if (chost) + { + return [NSString stringWithFormat: @"%@\n%@:> ", msg, chost]; + } + else if (cserv) + { + return [NSString stringWithFormat: @"%@\n:%@> ", msg, cserv]; + } + else + { + return [NSString stringWithFormat: @"%@\nControl> ", msg]; + } +} + +- (void) setAlerts: (BOOL)flag +{ + alerts = flag; +} + +- (void) setConnectedHost: (NSString*)c +{ + ASSIGNCOPY(chost, c); +} + +- (void) setConnectedServ: (NSString*)c +{ + ASSIGNCOPY(cserv, c); +} + +- (void) setErrors: (BOOL)flag +{ + errors = flag; +} + +- (void) setGeneral: (BOOL)flag +{ + general = flag; +} + +- (void) setWarnings: (BOOL)flag +{ + warnings = flag; +} + +@end + + +@interface Control : EcProcess +{ + NSFileManager *mgr; + NSMutableArray *commands; + NSMutableArray *consoles; + NSString *logname; + NSDictionary *config; + NSMutableDictionary *operators; + NSMutableDictionary *fileBodies; + NSMutableDictionary *fileDates; + NSTimer *timer; + unsigned commandPingPosition; + unsigned consolePingPosition; + NSString *configFailed; + NSString *configIncludeFailed; + EcAlerter *alerter; +} +- (NSFileHandle*) openLog: (NSString*)lname; +- (void) cmdGnip: (id )from + sequence: (unsigned)num + extra: (NSData*)data; +- (void) cmdPing: (id )from + sequence: (unsigned)num + extra: (NSData*)data; +- (void) cmdQuit: (int)status; +- (void) command: (NSData*)dat + from: (NSString*)f; +- (BOOL) connection: (NSConnection*)ancestor + shouldMakeNewConnection: (NSConnection*)newConn; +- (id) connectionBecameInvalid: (NSNotification*)notification; +- (ClientInfo*) findIn: (NSArray*)a + byAbbreviation: (NSString*)s; +- (ClientInfo*) findIn: (NSArray*)a + byName: (NSString*)s; +- (ClientInfo*) findIn: (NSArray*)a + byObject: (id)s; +- (void) information: (NSString*)inf + type: (EcLogType)t + to: (NSString*)to + from: (NSString*)from; +- (NSData*) registerCommand: (id)c + name: (NSString*)n; +- (NSString*) registerConsole: (id)c + name: (NSString*)n + pass: (NSString*)p; +- (void) reply: (NSString*) msg to: (NSString*)n from: (NSString*)c; +- (void) servers: (NSData*)d + on: (id)s; +- (void) timedOut: (NSTimer*)t; +- (void) unregister: (id)obj; +- (BOOL) update; +- (void) updateConfig: (NSData*)dummy; +- (id) getInclude: (id)s; +- (id) recursiveInclude: (id)o; +- (id) tryInclude: (id)s multi: (BOOL*)flag; +@end + +@implementation Control + +- (oneway void) alarm: (in bycopy EcAlarm*)alarm +{ + EcAlarmSeverity severity; + + /* First, send the alarm to the alarm sink. + */ + [sink alarm: alarm]; + + /* Now, for critical and major alarms, generate a corresponding old style + * alert. + */ + severity = [alarm perceivedSeverity]; + if (EcAlarmSeverityCritical == severity + || EcAlarmSeverityMajor == severity + || EcAlarmSeverityMinor == severity) + { + NSString *additional; + NSString *connector; + NSString *instance; + NSString *message; + NSString *repair; + NSString *spacing1; + NSString *spacing2; + + instance = [alarm moInstance]; + if ([instance length] == 0) + { + instance = @""; + connector = @""; + } + else + { + connector = @"-"; + } + + additional = [alarm additionalText]; + if ([additional length] == 0) + { + additional = @""; + spacing1 = @""; + } + else + { + spacing1 = @": "; + } + + repair = [alarm proposedRepairAction]; + if ([repair length] == 0) + { + repair = @""; + spacing2 = @""; + } + else + { + spacing2 = @", "; + } + + severity = [alarm perceivedSeverity]; + if (EcAlarmSeverityCritical == severity) + { + message = [NSString stringWithFormat: cmdLogFormat(LT_ALERT, + @"%@%@%@%@%@ - '%@%@%@' on %@"), + [alarm specificProblem], spacing1, additional, spacing2, repair, + [alarm moProcess], connector, instance, [alarm moHost]]; + [self information: message type: LT_ALERT to: nil from: nil]; + } + else if (EcAlarmSeverityMajor == severity) + { + message = [NSString stringWithFormat: cmdLogFormat(LT_ERROR, + @"%@%@%@%@%@ - '%@%@%@' on %@"), + [alarm specificProblem], spacing1, additional, spacing2, repair, + [alarm moProcess], connector, instance, [alarm moHost]]; + [self information: message type: LT_ERROR to: nil from: nil]; + } + } +} + +- (NSString*) description +{ + return [NSString stringWithFormat: @"Control server\n%@", alerter]; +} + +- (oneway void) domanage: (NSString*)name +{ + [sink domanage: name]; +} + +- (NSFileHandle*) openLog: (NSString*)lname +{ + NSFileHandle *lf; + + if ([mgr isWritableFileAtPath: lname] == NO) + { + if ([mgr createFileAtPath: lname + contents: nil + attributes: nil] == NO) + { + NSLog(@"Log file '%@' is not writable and can't be created", lname); + return nil; + } + } + + lf = [NSFileHandle fileHandleForUpdatingAtPath: lname]; + if (lf == nil) + { + NSLog(@"Unable to log to %@", lname); + return nil; + } + [lf seekToEndOfFile]; + return lf; +} + +- (void) cmdGnip: (id )from + sequence: (unsigned)num + extra: (NSData*)data +{ + ClientInfo *r; + + /* + * See if we have a fitting client - and update records. + */ + r = [self findIn: commands byObject: (id)from]; + if (r == nil) + { + r = [self findIn: consoles byObject: (id)from]; + } + if (r != nil) + { + [r gnip: num]; + } +} + +- (BOOL) cmdIsClient +{ + return NO; // Control is not a client of Command +} + +- (void) cmdPing: (id )from + sequence: (unsigned)num + extra: (NSData*)data +{ + [from cmdGnip: self sequence: num extra: nil]; +} + +- (void) cmdQuit: (int)status +{ + [sink shutdown]; + exit (status); +} + +- (void) command: (NSData*)dat + from: (NSString*)f +{ + NSMutableArray *cmd; + ConsoleInfo *console; + + cmd = [NSPropertyListSerialization + propertyListWithData: dat + options: NSPropertyListMutableContainers + format: 0 + error: 0]; + console = (ConsoleInfo*)[self findIn: consoles byName: f]; + if (console == nil) + { + NSString *m; + + m = [NSString stringWithFormat: cmdLogFormat(LT_ERROR, + @"command from unregistered console (from %@)"), f]; + [self information: m + type: LT_ERROR + to: nil + from: nil]; + } + else if (cmd == nil || [cmd count] == 0) + { + NSString *m; + + m = [NSString stringWithFormat: cmdLogFormat(LT_AUDIT, + @"no command entered (from %@ %@)"), f, [console name]]; + [self information: m + type: LT_AUDIT + to: [console name] + from: nil]; + } + else + { + NSMutableString *full; + NSString *hname = nil; + NSString *m = @""; + NSString *wd = cmdWord(cmd, 0); + unsigned count = [cmd count]; + unsigned i; + BOOL connected = NO; + + /* + * Log this command! + */ + full = [[NSMutableString alloc] initWithCapacity: 1024]; + for (i = 0; i < count; i++) + { + [full appendFormat: @" %@", [cmd objectAtIndex: i]]; + } + [[self cmdLogFile: logname] + printf: @"%@ %@> %@\n", [NSDate date], [console name], full]; + RELEASE(full); + + if ([console chost] != nil || [console cserv] != nil) + { + connected = YES; + } + /* + * If we have a 'control' command - act as if the console was not + * connected to a host or server. + */ + if (comp(wd, @"control") == 0) + { + [cmd removeObjectAtIndex: 0]; + wd = cmdWord(cmd, 0); + connected = NO; + } + if (comp(wd, @"password") == 0) + { + NSRange r = [[console name] rangeOfString: @":"]; + NSString *s = [[console name] substringToIndex: r.location]; + NSMutableDictionary *d; + NSString *p; + NSString *path; + + if ([cmd count] != 3) + { + [self information: @"parameters are old password and new one.\n" + type: LT_AUDIT + to: [console name] + from: nil]; + return; + } + if (operators == nil) + { + operators = [NSMutableDictionary dictionaryWithCapacity: 1]; + RETAIN(operators); + [operators setObject: @"" forKey: s]; + } + d = AUTORELEASE([[operators objectForKey: s] mutableCopy]); + p = [d objectForKey: @"Password"]; + if ([p length] == 0 || [p isEqual: [cmd objectAtIndex: 1]]) + { + [d setObject: [cmd objectAtIndex: 2] forKey: @"Password"]; + [operators setObject: d forKey: s]; + p = [operators description]; + path = [[self cmdDataDirectory] stringByAppendingPathComponent: + @"Operators.plist"]; + [p writeToFile: path atomically: YES]; + [self information: @"new password set.\n" + type: LT_AUDIT + to: [console name] + from: nil]; + return; + } + else + { + [self information: @"old password is incorrect.\n" + type: LT_AUDIT + to: [console name] + from: nil]; + return; + } + } + + /* + * If we are not connected, but have an 'on ....' at the start of + * the command line, then we send to the specified host as if we + * were connected to it. + */ + if (connected) + { + hname = [console chost]; + } + else if (comp(wd, @"on") == 0) + { + [cmd removeObjectAtIndex: 0]; + wd = cmdWord(cmd, 0); + hname = AUTORELEASE(RETAIN(wd)); + if ([hname length] > 0) + { + CommandInfo *c; + + c = (CommandInfo*)[self findIn: commands byAbbreviation: hname]; + if (c) + { + hname = [c name]; + [cmd removeObjectAtIndex: 0]; + } + else + { + [self information: @"Attempt to work on unknown host" + type: LT_AUDIT + to: [console name] + from: nil]; + return; + } + wd = cmdWord(cmd, 0); + } + else + { + hname = nil; + } + } + + if (connected == YES || hname != nil) + { + NSArray *hosts; + + /* + * Make an array of hosts to work with - the connected host + * or all hosts. If the connected host has gone away - disconnect. + */ + if (hname == nil) + { + hosts = [NSArray arrayWithArray: commands]; + } + else + { + CommandInfo *c; + + c = (CommandInfo*)[self findIn: commands byName: hname]; + if (c) + { + hosts = [NSArray arrayWithObject: c]; + } + else + { + if (connected) + { + [console setConnectedHost: nil]; + } + [self information: @"Host has gone away" + type: LT_ERROR + to: [console name] + from: nil]; + return; + } + } + + if ([wd length] == 0) + { + /* Quietly ignore. */ + } + else + { + BOOL foundServer = NO; + int i; + + m = nil; /* Let connected host generate messages. */ + /* + * Perform operation on connected host (or all hosts if + * not connected to a host). + */ + for (i = 0; i < [hosts count]; i++) + { + CommandInfo *c = [hosts objectAtIndex: i]; + + if ([commands indexOfObjectIdenticalTo: c] != NSNotFound) + { + if ([console cserv] == nil) + { + foundServer = YES; + NS_DURING + { + NSData *dat = [NSPropertyListSerialization + dataFromPropertyList: cmd + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + [[c obj] command: dat + to: nil + from: [console name]]; + } + NS_HANDLER + { + NSLog(@"Caught: %@", localException); + } + NS_ENDHANDLER + } + else + { + NSString *to; + + to = [c serverByAbbreviation: [console cserv]]; + if (to) + { + foundServer = YES; + NS_DURING + { + NSData *dat; + + dat = [NSPropertyListSerialization + dataFromPropertyList: cmd + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + [[c obj] command: dat + to: to + from: [console name]]; + } + NS_HANDLER + { + NSLog(@"Caught: %@", localException); + } + NS_ENDHANDLER + } + } + } + } + if (foundServer == NO) + { + [console setConnectedServ: nil]; + [self information: @"Server has gone away" + type: LT_AUDIT + to: [console name] + from: nil]; + } + } + } + else if ([wd length] == 0) + { + /* Quietly ignore. */ + } + else if (comp(wd, @"alarms") >= 0) + { + NSArray *a = [sink alarms]; + + if (0 == [a count]) + { + m = @"No alarms currently active.\n"; + } + else + { + int i; + + a = [a sortedArrayUsingSelector: @selector(compare:)]; + m = @"Current alarms -\n"; + for (i = 0; i < [a count]; i++) + { + EcAlarm *alarm = [a objectAtIndex: i]; + + m = [m stringByAppendingString: [alarm description]]; + m = [m stringByAppendingString: @"\n"]; + } + } + } + else if (comp(wd, @"archive") >= 0) + { + NSCalendarDate *when = [NSCalendarDate date]; + NSString *sub; + int yy, mm, dd; + + yy = [when yearOfCommonEra]; + mm = [when monthOfYear]; + dd = [when dayOfMonth]; + + sub = [NSString stringWithFormat: @"%04d-%02d-%02d", yy, mm, dd]; + m = [NSString stringWithFormat: @"\n%@\n\n", [self cmdArchive: sub]]; + } + else if (comp(wd, @"clear") >= 0) + { + NSArray *a = [sink alarms]; + + wd = cmdWord(cmd, 1); + if ([wd length] > 0 && [wd intValue] > 0) + { + EcAlarm *alarm = nil; + int n = [wd intValue]; + int i = [a count]; + + while (i-- > 0) + { + alarm = [a objectAtIndex: i]; + if ([alarm notificationID] == n) + { + break; + } + alarm = nil; + } + if (nil == alarm) + { + m = @"No alarm found with that notificationID\n"; + } + else + { + m = [NSString stringWithFormat: @"Clearing %@\n", alarm]; + alarm = [alarm clear]; + [sink alarm: alarm]; + } + } + else + { + m = @"The 'clear' command requires an alarm notificationID\n"; + } + } + else if (comp(wd, @"connect") >= 0) + { + wd = cmdWord(cmd, 1); + if ([wd length] == 0) + { + [console setConnectedServ: nil]; + } + else + { + [console setConnectedServ: wd]; + } + } + else if (comp(wd, @"config") >= 0) + { + BOOL changed; + + changed = [self update]; + if (configFailed != nil) + { + m = configFailed; + } + else + { + if (configIncludeFailed != nil) + { + m = [NSString stringWithFormat: + @"Configuration file re-read and loaded, but %@", + configIncludeFailed]; + } + else if (YES == changed) + { + m = @"Configuration file re-read ... updates handled.\n"; + } + else + { + m = @"Configuration file re-read ... UNCHANGED.\n"; + } + } + } + else if (comp(wd, @"flush") >= 0) + { + [alerter flushSms]; + [alerter flushEmail]; + m = @"Flushed alert messages\n"; + } + else if (comp(wd, @"help") >= 0) + { + wd = cmdWord(cmd, 1); + if ([wd length] == 0) + { + m = @"Commands are -\n" + @"Help\tAlarms\tArchive\tClear\tConfig\tConnect\t" + @"Flush\tHost\tList\tMemory\tOn\t" + @"Password\tRepeat\tQuit\tSet\tStatus\tTell\tUnset\n\n" + @"Type 'help' followed by a command word for details.\n" + @"A command line consists of a sequence of words, " + @"the first of which is the command to be executed. " + @"A word can be a simple sequence of non-space characters, " + @"or it can be a 'quoted string'. " + @"Simple words are converted to lower case before " + @"matching them against commands and their parameters. " + @"Text in a 'quoted string' is NOT converted to lower case " + @"but a '\\' character is treated in a special manner -\n" + @" \\b is replaced by a backspace\n" + @" \\f is replaced by a formfeed\n" + @" \\n is replaced by a linefeed\n" + @" \\r is replaced by a carriage-return\n" + @" \\t is replaced by a tab\n" + @" \\0 followed by up to 3 octal digits is replaced" + @" by the octal value\n" + @" \\x followed by up to 2 hex digits is replaced" + @" by the hex value\n" + @" \\ followed by any other character is replaced by" + @" the second character.\n" + @" This permits use of quotes and backslashes inside" + @" a quoted string.\n"; + } + else + { + if (comp(wd, @"Alarms") >= 0) + { + m = @"Alarms\nLists the currently active alarms ordered " + @"by their notificationIDs.\n"; + } + else if (comp(wd, @"Archive") >= 0) + { + m = @"Archive\nArchives the log file. The archived log " + @"file is stored in a subdirectory whose name is of " + @"the form YYYY-MM-DD being the date at " + @"which the archive was created.\n"; + } + else if (comp(wd, @"Clear") >= 0) + { + m = @"Clear\nInstructs the Control server to clear an " + @"alarm (identified by numeric notificationID).\n"; + } + else if (comp(wd, @"Config") >= 0) + { + m = @"Config\nInstructs the Control server to re-load " + @"it's configuration information and pass it on to " + @"all connected Command servers.\n"; + } + else if (comp(wd, @"Connect") >= 0) + { + m = @"Connect name\nInstructs the Control server that " + @"commands from your console should go to servers " + @"whose names match the value you give.\n" + @"To remove this association, you will need to type " + @"the command 'control connect'\n'"; + } + else if (comp(wd, @"Flush") >= 0) + { + m = @"Flush\nInstructs the Control server that messages " + @"about errors and alerts which are currently saved " + @"in the batching process should be sent out.\n"; + } + else if (comp(wd, @"Host") >= 0) + { + m = @"Host name\nInstructs the Control server that " + @"commands from your console should go to the host " + @"whose names match the value you give.\n" + @"To remove this association, you will need to type " + @"the command 'control host'\n'"; + } + else if (comp(wd, @"List") >= 0) + { + m = @"List\nLists all the connected commands.\n" + @"List consoles\nLists all the connected consoles.\n"; + } + else if (comp(wd, @"Memory") >= 0) + { + m = @"Memory\nDisplays recent memory allocation stats.\n" + @"Memory all\nDisplays all memory allocation stats.\n"; + } + else if (comp(wd, @"On") >= 0) + { + m = @"On host ...\nSends a command to the named host.\n"; + } + else if (comp(wd, @"Password") >= 0) + { + m = @"Password \nSets your password.\n"; + } + else if (comp(wd, @"Quit") >= 0) + { + m = @"Quit 'self'\n" + @"Shuts down the Control server process - don't do " + @"this lightly - all other servers are coordinated " + @"by the Control server\n" + @"\nUse 'on quit ' to shut down " + @"individual servers on the specified host.\n" + @"\nUse 'on quit all' to shut down all the " + @"servers on the specified host.\n" + @"\nUse 'on quit self' to shut down the " + @"Command server on the specified host - be careful " + @"using this - the other servers on a host are all " + @"coordinated (and log through) the Command server.\n"; + } + else if (comp(wd, @"Repeat") >= 0) + { + m = @"Repeat\nRepeat the last command.\n"; + } + else if (comp(wd, @"Set") >= 0) + { + m = @"Set\n" + @"Changes settings -\n" + @" set display warnings\n" + @" displays warning messages from the server to " + @"which you are connected.\n" + @" set display general\n" + @" display warning messages (if selected) " + @"from ALL servers, not just connected ones.\n" + @" set display errors\n" + @" displays error messages.\n" + @" set display alerts\n" + @" displays alert messages.\n" + @"\n"; + } + else if (comp(wd, @"Status") >= 0) + { + m = @"Status\nInstructs the Control server to report its " + @"current status ... mostly the buffered alert and " + @"error messages waiting to be sent out.\n"; + } + else if (comp(wd, @"Tell") >= 0) + { + m = @"Tell 'name' 'command'\n" + @"Sends the command to the named client.\n"; + } + else if (comp(wd, @"UnSet") >= 0) + { + m = @"UnSet\n" + @"Changes settings -\n" + @" unset display general\n" + @" unset display warnings\n" + @" unset display errors\n" + @" unset display alerts\n" + @"\n"; + } + } + } + else if (comp(wd, @"host") >= 0) + { + wd = cmdWord(cmd, 1); + if ([wd length] == 0) + { + [console setConnectedHost: nil]; + } + else + { + CommandInfo *c; + + c = (CommandInfo*)[self findIn: commands byAbbreviation: wd]; + if (c) + { + [console setConnectedHost: [c name]]; + } + } + } + else if (comp(wd, @"list") >= 0) + { + wd = cmdWord(cmd, 1); + if ([wd length] > 0 && comp(wd, @"consoles") >= 0) + { + if ([consoles count] == 1) + { + m = @"No other consoles currently connected.\n"; + } + else + { + int i; + + m = @"Current console processes -\n"; + for (i = 0; i < [consoles count]; i++) + { + ConsoleInfo *c; + + c = (ConsoleInfo*)[consoles objectAtIndex: i]; + if (c == console) + { + m = [m stringByAppendingFormat: + @"%2d. your console\n", i]; + } + else + { + m = [m stringByAppendingFormat: + @"%2d. %s\n", i, [[c name] cString]]; + } + } + } + } + else + { + if ([commands count] == 0) + { + if (nil == alerter) + { + m = @"No database/alerter available.\n"; + } + else + { + m = @"No hosts currently connected.\n"; + } + } + else + { + int i; + + m = @"Current server hosts -\n"; + for (i = 0; i < [commands count]; i++) + { + CommandInfo* c; + + c = (CommandInfo*)[commands objectAtIndex: i]; + m = [m stringByAppendingFormat: + @"%2d. %-32.32s\n", i, [[c name] cString]]; + if ([c servers] == nil || [[c servers] count] == 0) + { + m = [m stringByAppendingString: + @" no servers connected\n"]; + } + else + { + NSArray *svrs = [c servers]; + int j; + + for (j = 0; j < [svrs count]; j++) + { + m = [m stringByAppendingFormat: + @" %2d. %@\n", j, + [svrs objectAtIndex: j]]; + } + } + } + } + } + } + else if (comp(wd, @"memory") >= 0) + { + if (GSDebugAllocationActive(YES) == NO) + { + m = @"Memory statistics were not being gathered.\n" + @"Statistics Will start from NOW.\n"; + } + else + { + const char* list; + + wd = cmdWord(cmd, 1); + if ([wd length] > 0 && comp(wd, @"all") >= 0) + { + list = GSDebugAllocationList(NO); + } + else + { + list = GSDebugAllocationList(YES); + } + m = [NSString stringWithCString: list]; + } + } + else if (comp(wd, @"quit") >= 0) + { + m = @"Try 'help quit' for information about shutting down.\n"; + wd = cmdWord(cmd, 1); + if ([wd length] > 0 && comp(wd, @"self") == 0) + { + [alerter flushSms]; + [alerter flushEmail]; + DESTROY(alerter); + exit(0); + } + } + else if (comp(wd, @"set") >= 0) + { + m = @"ok - set confirmed.\n"; + wd = cmdWord(cmd, 1); + if ([wd length] == 0) + { + m = @"no parameter to 'set'\n"; + } + else if (comp(wd, @"display") >= 0) + { + wd = cmdWord(cmd, 2); + if ([wd length] == 0) + { + m = [NSString stringWithFormat: @"display settings -\n" + @"general: %d warnings: %d errors: %d alerts: %d\n", + [console getGeneral], [console getWarnings], + [console getErrors], [console getAlerts]]; + } + else if (comp(wd, @"alerts") >= 0) + { + [console setAlerts: YES]; + } + else if (comp(wd, @"errors") >= 0) + { + [console setErrors: YES]; + } + else if (comp(wd, @"general") >= 0) + { + [console setGeneral: YES]; + } + else if (comp(wd, @"warnings") >= 0) + { + [console setWarnings: YES]; + } + else + { + m = @"unknown parameter to 'set display'\n"; + } + } + else + { + m = @"unknown parameter to 'set'\n"; + } + } + else if (comp(wd, @"status") >= 0) + { + m = [self description]; + } + else if (comp(wd, @"tell") >= 0) + { + wd = cmdWord(cmd, 1); + if ([wd length] > 0) + { + NSString *dest = AUTORELEASE(RETAIN(wd)); + int i; + NSArray *a = [[NSArray alloc] initWithArray: commands]; + + [cmd removeObjectAtIndex: 0]; + [cmd removeObjectAtIndex: 0]; + if (comp(dest, @"all") == 0) + { + dest = nil; + } + + for (i = 0; i < [a count]; i++) + { + CommandInfo* c = (CommandInfo*)[a objectAtIndex: i]; + + if ([commands indexOfObjectIdenticalTo: c] != NSNotFound) + { + NSString *to; + + if (dest) + { + to = [c serverByAbbreviation: dest]; + if (to != nil) + { + NS_DURING + { + NSData *dat; + + dat = [NSPropertyListSerialization + dataFromPropertyList: cmd + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + [[c obj] command: dat + to: to + from: [console name]]; + } + NS_HANDLER + { + NSLog(@"Caught: %@", localException); + } + NS_ENDHANDLER + } + } + else + { + int j; + + for (j = 0; j < [[c servers] count]; j++) + { + to = [[c servers] objectAtIndex: j]; + NS_DURING + { + NSData *dat; + + dat = [NSPropertyListSerialization + dataFromPropertyList: cmd + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + [[c obj] command: dat + to: to + from: [console name]]; + } + NS_HANDLER + { + NSLog(@"Caught: %@", + localException); + } + NS_ENDHANDLER + } + } + } + } + m = nil; + } + else + { + m = @"Tell where?.\n"; + } + } + else if (comp(wd, @"unset") >= 0) + { + m = @"ok - unset confirmed.\n"; + wd = cmdWord(cmd, 1); + if ([wd length] == 0) + { + m = @"no parameter to 'set'\n"; + } + else if (comp(wd, @"display") >= 0) + { + wd = cmdWord(cmd, 2); + if ([wd length] == 0) + { + m = [NSString stringWithFormat: @"display settings -\n" + @"general: %d warnings: %d errors: %d alerts: %d\n", + [console getGeneral], [console getWarnings], + [console getErrors], [console getAlerts]]; + } + else if (comp(wd, @"alerts") >= 0) + { + [console setAlerts: NO]; + } + else if (comp(wd, @"errors") >= 0) + { + [console setErrors: NO]; + } + else if (comp(wd, @"general") >= 0) + { + [console setGeneral: NO]; + } + else if (comp(wd, @"warnings") >= 0) + { + [console setWarnings: NO]; + } + else + { + m = @"unknown parameter to 'unset display'\n"; + } + } + else + { + m = @"unknown parameter to 'unset'\n"; + } + } + else + { + m = [NSString stringWithFormat: @"Unknown command - '%@'\n", wd]; + } + + if (m != nil) + { + [self information: m + type: LT_AUDIT + to: [console name] + from: nil]; + } + } +} + +- (BOOL) connection: (NSConnection*)ancestor + shouldMakeNewConnection: (NSConnection*)newConn +{ + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(connectionBecameInvalid:) + name: NSConnectionDidDieNotification + object: (id)newConn]; + [newConn setDelegate: self]; + return YES; +} + +- (id) connectionBecameInvalid: (NSNotification*)notification +{ + id conn = [notification object]; + + [[NSNotificationCenter defaultCenter] removeObserver: self + name: NSConnectionDidDieNotification + object: conn]; + + if ([conn isKindOfClass: [NSConnection class]]) + { + NSMutableArray *a = [NSMutableArray arrayWithCapacity: 2]; + NSMutableString *m; + unsigned i; + + /* + * Now remove any consoles that have disconnected. + */ + m = [NSMutableString stringWithCapacity: 100]; + i = [consoles count]; + while (i-- > 0) + { + ConsoleInfo *o = (ConsoleInfo*)[consoles objectAtIndex: i]; + + if ([[o obj] connectionForProxy] == conn) + { + [a addObject: o]; + [m appendFormat: cmdLogFormat(LT_AUDIT, + @"removed (lost) console '%@'"), [o name]]; + [consoles removeObjectAtIndex: i]; + if (i <= consolePingPosition && consolePingPosition > 0) + { + consolePingPosition--; + } + } + } + + if ([m length] > 0) + { + [self information: m + type: LT_AUDIT + to: nil + from: nil]; + [m setString: @""]; + } + + /* + * Now remove the commands from the active list. + */ + i = [commands count]; + while (i-- > 0) + { + CommandInfo* o = (CommandInfo*)[commands objectAtIndex: i]; + + if ([[o obj] connectionForProxy] == conn) + { + EcAlarm *e; + NSString *s; + + [a addObject: o]; + s = EcMakeManagedObject([o name], @"Command", nil); + e = [EcAlarm alarmForManagedObject: s + at: nil + withEventType: EcAlarmEventTypeProcessingError + probableCause: EcAlarmSoftwareProgramAbnormallyTerminated + specificProblem: @"Host command/control availability" + perceivedSeverity: EcAlarmSeverityCritical + proposedRepairAction: @"Check network/host/process" + additionalText: @"remove (lost) host"]; + [self alarm: e]; + [commands removeObjectAtIndex: i]; + if (i <= commandPingPosition && commandPingPosition > 0) + { + commandPingPosition--; + } + } + } + + [a removeAllObjects]; + } + else + { + [self error: "non-Connection sent invalidation"]; + } + return self; +} + +- (void) dealloc +{ + DESTROY(alerter); + [self cmdLogEnd: logname]; + if (timer != nil) + { + [timer invalidate]; + } + DESTROY(mgr); + DESTROY(fileBodies); + DESTROY(fileDates); + DESTROY(operators); + DESTROY(config); + DESTROY(commands); + DESTROY(consoles); + DESTROY(configFailed); + DESTROY(configIncludeFailed); + [super dealloc]; +} + +- (ClientInfo*) findIn: (NSArray*)a + byAbbreviation: (NSString*)s +{ + ClientInfo *o; + int i; + int best_pos = -1; + int best_len = 0; + + /* + * Special case - a numeric value is used as an index into the array. + */ + if (isdigit(*[s cString])) + { + i = [s intValue]; + if (i >= 0 && i < [a count]) + { + return (ClientInfo*)[a objectAtIndex: i]; + } + } + + for (i = 0; i < [a count]; i++) + { + o = (ClientInfo*)[a objectAtIndex: i]; + if (comp(s, [o name]) == 0) + { + return o; + } + if (comp_len > best_len) + { + best_len = comp_len; + best_pos = i; + } + } + if (best_pos >= 0) + { + return (ClientInfo*)[a objectAtIndex: best_pos]; + } + return nil; +} + +- (ClientInfo*) findIn: (NSArray*)a + byName: (NSString*)s +{ + ClientInfo *o; + int i; + + for (i = 0; i < [a count]; i++) + { + o = (ClientInfo*)[a objectAtIndex: i]; + + if (comp([o name], s) == 0) + { + return o; + } + } + return nil; +} + +- (ClientInfo*) findIn: (NSArray*)a + byObject: (id)s +{ + ClientInfo *o; + int i; + + for (i = 0; i < [a count]; i++) + { + o = (ClientInfo*)[a objectAtIndex: i]; + + if ([o obj] == s) + { + return o; + } + } + return nil; +} + +- (void) information: (NSString*)inf + type: (EcLogType)t + to: (NSString*)to + from: (NSString*)from +{ + /* + * Send anything but debug or accounting info to consoles. + */ + if (t != LT_DEBUG) + { + NSArray *a; + unsigned i; + + /* + * Work with a copy of the consoles array in case one goes away + * or is added while we are doing this! + */ + a = [NSArray arrayWithArray: consoles]; + for (i = 0; i < [a count]; i++) + { + ConsoleInfo *c = [a objectAtIndex: i]; + NSString *name = [c name]; + + /* + * If this console has gone away - skip it. + */ + if ([consoles indexOfObjectIdenticalTo: c] == NSNotFound) + { + continue; + } + + /* + * If 'to' is nil - send to all consoles + */ + if (to != nil && [to isEqual: name] == NO) + { + continue; + } + + /* + * If this is a warning message and the console is not interested + * in warning then we skip this console. + */ + if (t == LT_WARNING) + { + if ([c getWarnings] == NO) + { + continue; + } + else if ([c getGeneral] == NO) + { + if (to != nil && [to isEqual: name] == NO) + { + continue; + } + } + } + else if (t == LT_ERROR && [c getErrors] == NO) + { + continue; + } + else if (t == LT_ALERT && [c getAlerts] == NO) + { + continue; + } + + NS_DURING + { + /* + * Finally - we send the message to the console along with + * a prompt. + */ + [[c obj] information: [c promptAfter: inf]]; + } + NS_HANDLER + { + NSLog(@"Caught: %@", localException); + } + NS_ENDHANDLER + } + } + + /* + * Log, alerts, and accounting get written to the log file too. + */ + if (t == LT_AUDIT || t == LT_ALERT) + { + [[self cmdLogFile: logname] puts: inf]; + } + /* + * Errors and alerts (severe errors) get passed to a handler. + */ + if (t == LT_ERROR || t == LT_ALERT) + { + if (alerter != nil) + { + [alerter handleInfo: inf]; + } + } +} + + +- (id) initWithDefaults: (NSDictionary*)defs +{ + self = [super initWithDefaults: defs]; + if (self != nil) + { + commands = [[NSMutableArray alloc] initWithCapacity: 10]; + consoles = [[NSMutableArray alloc] initWithCapacity: 2]; + logname = [[self cmdName] stringByAppendingPathExtension: @"log"]; + RETAIN(logname); + mgr = [NSFileManager defaultManager]; + RETAIN(mgr); + if ([self cmdLogFile: logname] == nil) + { + exit(0); + } + [self update]; + if (configFailed != nil) + { + [self cmdQuit: 1]; + return nil; + } + if (operators == nil) + { + [self cmdQuit: 1]; + return nil; + } + fileBodies = [[NSMutableDictionary alloc] initWithCapacity: 8]; + fileDates = [[NSMutableDictionary alloc] initWithCapacity: 8]; + + timer = [NSTimer scheduledTimerWithTimeInterval: 15.0 + target: self + selector: @selector(timedOut:) + userInfo: nil + repeats: YES]; + [self timedOut: nil]; + } + return self; +} + +- (NSData*) registerCommand: (id)c + name: (NSString*)n +{ + NSMutableDictionary *dict; + CommandInfo *obj; + CommandInfo *old; + NSHost *h; + NSString *m; + EcAlarm *a; + id o; + + if (nil == alerter) + { + [self update]; // Regenerate info from Control.plist + if (nil == alerter) + { + return nil; // Not fully configured yet + } + } + [(NSDistantObject*)c setProtocolForProxy: @protocol(Command)]; + dict = [NSMutableDictionary dictionaryWithCapacity: 3]; + + h = [NSHost hostWithName: n]; + if (h == nil) + { + m = [NSString stringWithFormat: + @"Rejected new host with bad name '%@' at %@\n", n, [NSDate date]]; + [self information: m + type: LT_AUDIT + to: nil + from: nil]; + [dict setObject: @"unable to handle given hostname." + forKey: @"rejected"]; + return [NSPropertyListSerialization + dataFromPropertyList: dict + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + } + n = [h wellKnownName]; + + old = (CommandInfo*)[self findIn: commands byObject: c]; + if (old != nil && [[old name] isEqual: n]) + { + obj = old; + m = [NSString stringWithFormat: + @"Re-registered new host with name '%@' at %@\n", + n, [NSDate date]]; + [self information: m + type: LT_AUDIT + to: nil + from: nil]; + } + else + { + /* + * Create a new reference for this client. + */ + obj = [[CommandInfo alloc] initFor: c + name: n + with: self]; + + old = (CommandInfo*)[self findIn: commands byName: n]; + if (old != nil) + { + NS_DURING + { + [[old obj] cmdPing: self sequence: 0 extra: nil]; + } + NS_HANDLER + { + NSLog(@"Ping %@ - Caught: %@", n, localException); + } + NS_ENDHANDLER + } + + if ((old = (CommandInfo*)[self findIn: commands byName: n]) != nil) + { + RELEASE(obj); + m = [NSString stringWithFormat: + @"Rejected new host with name '%@' at %@\n", + n, [NSDate date]]; + [self information: m + type: LT_AUDIT + to: nil + from: nil]; + [dict setObject: @"client with that name already registered." + forKey: @"rejected"]; + return [NSPropertyListSerialization + dataFromPropertyList: dict + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + } + else + { + [commands addObject: obj]; + RELEASE(obj); + [commands sortUsingSelector: @selector(compare:)]; + [self domanage: EcMakeManagedObject(n, @"Command", nil)]; + m = [NSString stringWithFormat: + @"Registered new host with name '%@' at %@\n", + n, [NSDate date]]; + [self information: m + type: LT_AUDIT + to: nil + from: nil]; + } + } + + /* Inform SNMP monitoring of new Command server. + */ + a = [EcAlarm alarmForManagedObject: EcMakeManagedObject(n, @"Command", nil) + at: nil + withEventType: EcAlarmEventTypeProcessingError + probableCause: EcAlarmSoftwareProgramAbnormallyTerminated + specificProblem: @"Host command/control availability" + perceivedSeverity: EcAlarmSeverityCleared + proposedRepairAction: nil + additionalText: nil]; + [self alarm: a]; + + /* + * Return configuration information - general stuff, plus + * host specific stuff. + */ + o = [config objectForKey: @"*"]; + if (o != nil) + { + [dict setObject: o forKey: @"*"]; + } + o = [config objectForKey: h]; + if (o != nil) + { + [dict setObject: o forKey: n]; + } + if (operators != nil) + { + [dict setObject: operators forKey: @"Operators"]; + } + return [NSPropertyListSerialization + dataFromPropertyList: dict + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; +} + +- (NSString*) registerConsole: (id)c + name: (NSString*)n + pass: (NSString*)p +{ + id obj; + NSString *m; + + [(NSDistantObject*)c setProtocolForProxy: @protocol(Console)]; + obj = [self findIn: consoles byName: n]; + if (obj != nil) + { + m = [NSString stringWithFormat: + @"%@ rejected console with info '%@' (already registered by name)\n", + [NSDate date], n]; + [self information: m + type: LT_AUDIT + to: nil + from: nil]; + return @"Already registered by that name"; + } + obj = [self findIn: consoles byObject: c]; + if (obj != nil) + { + m = [NSString stringWithFormat: + @"%@ rejected console with info '%@' (already registered)\n", + [NSDate date], n]; + [self information: m + type: LT_AUDIT + to: nil + from: nil]; + return @"Already registered"; /* Already registered. */ + } + if (operators != nil) + { + NSRange r = [n rangeOfString: @":"]; + NSString *s = [n substringToIndex: r.location]; + NSDictionary *info = [operators objectForKey: s]; + NSString *passwd = [info objectForKey: @"Password"]; + + if (info == nil) + { + m = [NSString stringWithFormat: + @"%@ rejected console with info '%@' (unknown operator)\n", + [NSDate date], n]; + [self information: m + type: LT_AUDIT + to: nil + from: nil]; + return @"Unknown user name"; + } + else if (passwd && [passwd length] && [passwd isEqual: p] == NO) + { + m = [NSString stringWithFormat: + @"%@ rejected console with info '%@' (bad password)\n", + [NSDate date], n]; + [self information: m + type: LT_AUDIT + to: nil + from: nil]; + return @"Bad username/password combination"; + } + } + obj = [[ConsoleInfo alloc] initFor: c name: n with: self pass: p]; + [consoles addObject: obj]; + [consoles sortUsingSelector: @selector(compare:)]; + RELEASE(obj); + m = [NSString stringWithFormat: @"%@ registered new console with info '%@'\n", + [NSDate date], n]; + [self information: m + type: LT_AUDIT + to: nil + from: nil]; + return nil; +} + +- (void) reply: (NSString*) msg to: (NSString*)n from: (NSString*)c +{ + [self information: msg type: LT_AUDIT to: n from: c]; +} + +- (void) requestConfigFor: (id)c +{ + return; +} + +- (void) servers: (NSData*)d + on: (id)s +{ + NSArray *a; + CommandInfo *o; + + a = [NSPropertyListSerialization propertyListWithData: d + options: NSPropertyListMutableContainers + format: 0 + error: 0]; + o = (CommandInfo*)[self findIn: commands byObject: s]; + if (o != nil) + { + [o setServers: a]; + } +} + +- (void) timedOut: (NSTimer*)t +{ + static BOOL inTimeout = NO; + NSDate *now = [t fireDate]; + + if (now == nil) + { + now = [NSDate date]; + } + + [[self cmdLogFile: logname] synchronizeFile]; + if (inTimeout == NO) + { + unsigned count; + + inTimeout = YES; + + count = [commands count]; + while (count-- > 0) + { + ClientInfo *r = [commands objectAtIndex: count]; + NSDate *d = [r lastUnanswered]; + + if (d != nil && [d timeIntervalSinceDate: now] < -pingDelay) + { + EcAlarm *a; + NSString *s; + NSString *t; + + s = EcMakeManagedObject([r name], @"Command", nil); + t = [NSString stringWithFormat: + @"failed to respond for over %d seconds", (int)pingDelay]; + a = [EcAlarm alarmForManagedObject: s + at: nil + withEventType: EcAlarmEventTypeProcessingError + probableCause: EcAlarmSoftwareProgramAbnormallyTerminated + specificProblem: @"Host command/control availability" + perceivedSeverity: EcAlarmSeverityCritical + proposedRepairAction: @"Check network/host/process" + additionalText: t]; + [self alarm: a]; + [[[r obj] connectionForProxy] invalidate]; + [commands removeObjectIdenticalTo: r]; + } + } + + count = [consoles count]; + while (count-- > 0) + { + ClientInfo *r = [consoles objectAtIndex: count]; + NSDate *d = [r lastUnanswered]; + + if (d != nil && [d timeIntervalSinceDate: now] < -pingDelay) + { + NSString *m; + + m = [NSString stringWithFormat: cmdLogFormat(LT_WARNING, + @"Operator '%@' failed to respond for over %d seconds"), + [r name], (int)pingDelay]; + [[[r obj] connectionForProxy] invalidate]; + [consoles removeObjectIdenticalTo: r]; + [self information: m type: LT_WARNING to: nil from: nil]; + } + } + + /* + * We ping each client in turn. If there are fewer than 4 clients, + * we skip timeouts so that clients get pinged no more frequently + * than one per 4 timeouts. + */ + count = [commands count]; + if (commandPingPosition >= 4 && commandPingPosition >= count) + { + commandPingPosition = 0; + } + if (commandPingPosition < count) + { + [[commands objectAtIndex: commandPingPosition++] ping]; + } + + count = [consoles count]; + if (consolePingPosition >= 4 && consolePingPosition >= count) + { + consolePingPosition = 0; + } + if (consolePingPosition < count) + { + [[consoles objectAtIndex: consolePingPosition++] ping]; + } + + /* + * Write heartbeat file to let external programs know we are alive. + */ + [[NSString stringWithFormat: @"Heartbeat: %@\n", now] + writeToFile: [NSString stringWithFormat: @"/tmp/%@.alive", + [self cmdName]] + atomically: YES]; + } + inTimeout = NO; +} + +- (id) recursiveInclude: (id)o +{ + id tmp; + + if ([o isKindOfClass: [NSArray class]] == YES) + { + NSMutableArray *n; + unsigned i; + unsigned c = [o count]; + + n = [[NSMutableArray alloc] initWithCapacity: c]; + for (i = 0; i < c; i++) + { + id tmp = [o objectAtIndex: i]; + + if ([tmp isKindOfClass: [NSString class]] == YES) + { + BOOL multi = NO; + + tmp = [self tryInclude: tmp multi: &multi]; + if (multi == YES) + { + [n addObjectsFromArray: tmp]; + } + else if (tmp != nil) + { + [n addObject: tmp]; + } + } + else + { + tmp = [self recursiveInclude: tmp]; + if (tmp != nil) + { + [n addObject: tmp]; + } + } + } + return AUTORELEASE(n); + } + else if ([o isKindOfClass: [NSDictionary class]] == YES) + { + NSMutableDictionary *n; + NSEnumerator *e = [o keyEnumerator]; + NSString *k; + unsigned c = [o count]; + + n = [[NSMutableDictionary alloc] initWithCapacity: c]; + while ((k = [e nextObject]) != nil) + { + id tmp = [self tryInclude: k multi: 0]; + + if ([tmp isKindOfClass: [NSDictionary class]] == YES) + { + [n addEntriesFromDictionary: tmp]; + } + else + { + tmp = [self recursiveInclude: + [(NSDictionary *)o objectForKey: k]]; + if (tmp != nil) + { + [n setObject: tmp forKey: k]; + } + } + } + return AUTORELEASE(n); + } + else + { + tmp = [self tryInclude: o multi: 0]; + return tmp; + } +} + +- (id) getInclude: (id)s +{ + NSString *base = [self cmdDataDirectory]; + NSString *file; + id info; + NSRange r; + + r = [s rangeOfCharacterFromSet: [NSCharacterSet whitespaceCharacterSet]]; + + if (r.length == 0) + { + return s; // Not an include. + } + + file = [s substringFromIndex: NSMaxRange(r)]; + if ([file isAbsolutePath] == NO) + { + file = [base stringByAppendingPathComponent: file]; + } + if ([mgr isReadableFileAtPath: file] == NO) + { + NSString *e; + + e = [NSString stringWithFormat: @"Unable to read file for '%@'\n", s]; + ASSIGN(configIncludeFailed, e); + [[self cmdLogFile: logname] printf: @"%@", configIncludeFailed]; + return nil; + } + info = [NSString stringWithContentsOfFile: file]; + if (info == nil) + { + NSString *e; + + e = [NSString stringWithFormat: @"Unable to load string for '%@'\n", s]; + ASSIGN(configIncludeFailed, e); + [[self cmdLogFile: logname] printf: @"%@", configIncludeFailed]; + return nil; + } + NS_DURING + { + info = [info propertyList]; + } + NS_HANDLER + { + NSString *e; + + e = [NSString stringWithFormat: @"Unable to parse for '%@' - %@\n", + s, localException]; + ASSIGN(configIncludeFailed, e); + [[self cmdLogFile: logname] printf: @"%@", configIncludeFailed]; + info = nil; + } + NS_ENDHANDLER + s = info; + return s; +} + +- (id) tryInclude: (id)s multi: (BOOL*)flag +{ + if (flag != 0) + { + *flag = NO; + } + if ([s isKindOfClass: [NSString class]] == YES) + { + if ([s hasPrefix: @"#include "] == YES) + { + s = [self getInclude: s]; + } + else if ([s hasPrefix: @"#includeKeys "] == YES) + { + s = [self getInclude: s]; + if ([s isKindOfClass: [NSDictionary class]] == YES) + { + s = [s allKeys]; + } + if (flag != 0 && [s isKindOfClass: [NSArray class]] == YES) + { + *flag = YES; + } + } + else if ([s hasPrefix: @"#includeValues "] == YES) + { + s = [self getInclude: s]; + if ([s isKindOfClass: [NSDictionary class]] == YES) + { + s = [s allValues]; + } + if (flag != 0 && [s isKindOfClass: [NSArray class]] == YES) + { + *flag = YES; + } + } + } + return s; +} + +- (oneway void) unmanage: (NSString*)name +{ + [sink unmanage: name]; +} + +- (void) unregister: (id)obj +{ + NSString *m; + ClientInfo *o = [self findIn: commands byObject: obj]; + + if (o == nil) + { + o = [self findIn: consoles byObject: obj]; + if (o == nil) + { + m = [NSString stringWithFormat: + @"%@ unregister by unknown host/console\n", [NSDate date]]; + } + else + { + m = [NSString stringWithFormat: + @"%@ removed (unregistered) console - '%@'\n", + [NSDate date], [o name]]; + [o setUnregistered: YES]; + [consoles removeObjectIdenticalTo: o]; + } + } + else + { + [self unmanage: EcMakeManagedObject([o name], @"Command", nil)]; + + m = [NSString stringWithFormat: + @"%@ removed (unregistered) host - '%@'\n", [NSDate date], [o name]]; + [o setUnregistered: YES]; + [commands removeObjectIdenticalTo: o]; + } + [self information: m + type: LT_AUDIT + to: nil + from: nil]; +} + +- (BOOL) update +{ + NSMutableDictionary *dict; + NSDictionary *conf; + NSDictionary *d; + NSArray *a; + NSString *base = [self cmdDataDirectory]; + NSString *path; + NSString *str; + unsigned count; + unsigned i; + BOOL changed = NO; + + path = [base stringByAppendingPathComponent: @"AlertConfig.plist"]; + if ([mgr isReadableFileAtPath: path] == YES + && (d = [NSDictionary dictionaryWithContentsOfFile: path]) != nil) + { + [[self cmdDefaults] setConfiguration: d]; + if (alerter == nil) + { + alerter = [EcAlerter new]; + } + } + else + { + DESTROY(alerter); + } + + path = [base stringByAppendingPathComponent: @"Operators.plist"]; + d = [NSDictionary dictionaryWithContentsOfFile: path]; + if (d == nil) + { + [[self cmdLogFile: logname] + printf: @"Failed to load %@\n", path]; + } + else + { + if (operators == nil || [operators isEqual: d] == NO) + { + changed = YES; + RELEASE(operators); + operators = [d mutableCopy]; + } + } + + DESTROY(configIncludeFailed); + path = [base stringByAppendingPathComponent: @"Control.plist"]; + str = [NSString stringWithContentsOfFile: path]; + if (str == nil) + { + NSString *e; + + e = [NSString stringWithFormat: @"Failed to load %@\n", path]; + ASSIGN(configFailed, e); + [[self cmdLogFile: logname] printf: @"%@", configFailed]; + return NO; + } + NS_DURING + { + conf = [str propertyList]; + if ([conf isKindOfClass: [NSDictionary class]] == NO) + { + [NSException raise: NSGenericException + format: @"Contents of file not a dictionary"]; + } + } + NS_HANDLER + { + NSString *e; + + e = [NSString stringWithFormat: @"Failed to load %@ - %@\n", + path, [localException reason]]; + ASSIGN(configFailed, e); + [[self cmdLogFile: logname] printf: @"%@", configFailed]; + return NO; + } + NS_ENDHANDLER + + if (nil != conf) + { + NSMutableDictionary *root; + NSEnumerator *rootEnum; + id hostKey; + + if ([conf isKindOfClass: [NSDictionary class]] == NO) + { + NSString *e; + + e = [NSString stringWithFormat: + @"%@ top-level is not a dictionary.\n", path]; + ASSIGN(configFailed, e); + [[self cmdLogFile: logname] printf: @"%n", configFailed]; + return NO; + } + + /* + * Build version with mutable dictionaries at the hosts and + * applications/classes levels of the configuration. + */ + conf = [self recursiveInclude: conf]; + [conf writeToFile: @"/tmp/control.log" atomically: YES]; + root = [NSMutableDictionary dictionaryWithCapacity: [conf count]]; + rootEnum = [conf keyEnumerator]; + while ((hostKey = [rootEnum nextObject]) != nil) + { + NSDictionary *rootObj; + NSMutableDictionary *host; + NSEnumerator *hostEnum; + NSString *appKey; + + rootObj = [conf objectForKey: hostKey]; + if ([rootObj isKindOfClass: [NSDictionary class]] == NO) + { + NSString *e; + + e = [NSString stringWithFormat: + @"%@ host-level is not a dictionary for '%@'.\n", + path, hostKey]; + ASSIGN(configFailed, e); + [[self cmdLogFile: logname] printf: @"%@", configFailed]; + return NO; + } + if ([hostKey isEqual: @"*"] == NO) + { + id o = hostKey; + + if ([o isEqual: @""] + || [o caseInsensitiveCompare: @"localhost"] == NSOrderedSame) + { + hostKey = [NSHost currentHost]; + } + else + { + hostKey = [NSHost hostWithName: o]; + } + if (hostKey == nil) + { + NSString *e; + + e = [NSString stringWithFormat: + @"%@ host '%@' unknown.\n", path, o]; + ASSIGN(configFailed, e); + [[self cmdLogFile: logname] printf: @"%@", configFailed]; + return NO; + } + } + + host = [NSMutableDictionary dictionaryWithCapacity: + [rootObj count]]; + hostEnum = [rootObj keyEnumerator]; + while ((appKey = [hostEnum nextObject]) != nil) + { + NSDictionary *hostObj; + NSMutableDictionary *app; + + hostObj = [rootObj objectForKey: appKey]; + if ([hostObj isKindOfClass: [NSDictionary class]] == NO) + { + NSString *e; + + e = [NSString stringWithFormat: + @"%@ app-level is not a dictionary for '%@'\n", + path, appKey]; + ASSIGN(configFailed, e); + [[self cmdLogFile: logname] printf: @"%@", configFailed]; + return NO; + } + + app = [self recursiveInclude: hostObj]; + [host setObject: app forKey: appKey]; + } + [root setObject: host forKey: hostKey]; + } + + if (config == nil || [config isEqual: root] == NO) + { + changed = YES; + ASSIGN(config, root); + } + } + + if (YES == changed) + { + dict = [NSMutableDictionary dictionaryWithCapacity: 3]; + + /* + * Now per-host config dictionaries consisting of general and + * host-specific dictionaries. + */ + a = [NSArray arrayWithArray: commands]; + count = [a count]; + for (i = 0; i < count; i++) + { + CommandInfo *c = [a objectAtIndex: i]; + + if ([commands indexOfObjectIdenticalTo: c] != NSNotFound) + { + id o; + NSHost *h; + + [dict removeAllObjects]; + o = [config objectForKey: @"*"]; + if (o != nil) + { + [dict setObject: o forKey: @"*"]; + } + h = [NSHost hostWithName: [c name]]; + o = [config objectForKey: h]; + if (o != nil) + { + [dict setObject: o forKey: [c name]]; + } + if (operators != nil) + { + [dict setObject: operators forKey: @"Operators"]; + } + NS_DURING + { + NSData *dat; + + dat = [NSPropertyListSerialization + dataFromPropertyList: dict + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: 0]; + [[c obj] updateConfig: dat]; + } + NS_HANDLER + { + [[self cmdLogFile: logname] + printf: @"Updating config for '%@':%@", + [c name], localException]; + } + NS_ENDHANDLER + } + } + } + DESTROY(configFailed); + + return changed; +} + +- (void) updateConfig: (NSData*)dummy +{ + [self update]; +} + +@end + + +void +inner_main() +{ + Control *server; + NSDictionary *defs; + NSAutoreleasePool *arp = [NSAutoreleasePool new]; + + cmdVersion(@"$Date: 2012-02-18 08:30:36 +0000 (Sat, 18 Feb 2012) $ $Revision: 66052 $"); + + defs = [NSDictionary dictionaryWithObjectsAndKeys: + @"Command", @"HomeDirectory", + @"YES", @"Daemon", + nil]; + + server = [[Control alloc] initWithDefaults: defs]; + if (server == nil) + { + NSLog(@"Unable to create control object.\n"); + exit(1); + } + + /* Start the SNMP alarm sink before entering run loop. + */ + sink = [[EcAlarmSinkSNMP alarmSinkSNMP] retain]; + + [server prcRun]; + + [sink shutdown]; + [sink release]; + [arp release]; + exit(0); +} + +int +main(int argc, char *argv[]) +{ + inner_main(); + return 0; +} diff --git a/EcAlarm.h b/EcAlarm.h new file mode 100644 index 0000000..24e6734 --- /dev/null +++ b/EcAlarm.h @@ -0,0 +1,589 @@ +#ifndef _ECALARM_H +#define _ECALARM_H + +#import + +@class NSCoder; +@class NSDate; +@class NSString; + +/** + * The EcAlarmEventType enumeration defines the different types of + * alarm we support.
+ * The enumerated values MUST be matched by those in your SNMP MIB if + * you wish to have your software interact with SNMP tools.
+ * NB. EcAlarmEventTypeUnknown must NOT 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. + * + * EcAlarmEventTypeUnknown + * Not used + * EcAlarmEventTypeCommunications + * A communications/networking/protocol issue + * EcAlarmEventTypeEnvironmental + * An external environmental issue (eg building on fire) + * EcAlarmEventTypeEquipment + * A hardware problem (eg disk failure) + * EcAlarmEventTypeProcessingError + * A software problem (a bug or misconfiguration) + * EcAlarmEventTypeQualityOfService + * A system not running as well as expected ... eg. overloaded + * + */ +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.
+ * These are taken from the CCITT X.733 specification with the numeric + * values from the CCITT X.721 specification.
+ * Enumeration values include a comment to specify which + * EcAlarmEventType they apply to. + * + * EcAlarmProbableCauseUnknown + * Category: Any + * EcAlarmAdapterError + * Category: Equipment + * EcAlarmApplicationSubsystemFailure + * Category: Processing + * EcAlarmBandwidthReduced + * Category: QoS + * EcAlarmCallEstablishmentError + * Category: Communications + * EcAlarmCommunicationsProtocolError + * Category: Communications + * EcAlarmCommunicationsSubsystemFailure + * Category: Communications + * EcAlarmConfigurationOrCustomizationError + * Category: Processing + * EcAlarmCongestion + * Category: QoS + * EcAlarmCorruptData + * Category: Processing + * EcAlarmCpuCyclesLimitExceeded + * Category: Processing + * EcAlarmDataSetOrModemError + * Category: Equipment + * EcAlarmDegradedSignal + * Category: Communications + * EcAlarmDTE_DCEInterfaceError + * Category: Communications + * EcAlarmEnclosureDoorOpen + * Category: Environmental + * EcAlarmEquipmentMalfunction + * Category: Equipment + * EcAlarmExcessiveVibration + * Category: Environmental + * EcAlarmFileError + * Category: Processing + * EcAlarmFireDetected + * Category: Environmental + * EcAlarmFloodDetected + * Category: Environmental + * EcAlarmFramingError + * Category: Communications + * EcAlarmHeatingOrVentilationOrCoolingSystemProblem + * Category: Environmental + * EcAlarmHumidityUnacceptable + * Category: Environmental + * EcAlarmInputOutputDeviceError + * Category: Equipment + * EcAlarmInputDeviceError + * Category: Equipment + * EcAlarmLANError + * Category: Communications + * EcAlarmLeakDetected + * Category: Environmental + * EcAlarmLocalNodeTransmissionError + * Category: Communications + * EcAlarmLossOfFrame + * Category: Communications + * EcAlarmLossOfSignal + * Category: Communications + * EcAlarmMaterialSupplyExhausted + * Category: Environmental + * EcAlarmMultiplexerProblem + * Category: Equipment + * EcAlarmOutOfMemory + * Category: Processing + * EcAlarmOuputDeviceError + * Category: Equipment + * EcAlarmPerformanceDegraded + * Category: QoS + * EcAlarmPowerProblem + * Category: Equipment + * EcAlarmPressureUnacceptable + * Category: Environmental + * EcAlarmProcessorProblem + * Category: Equipment + * EcAlarmPumpFailure + * Category: Environmental + * EcAlarmQueueSizeExceeded + * Category: QoS + * EcAlarmReceiveFailure + * Category: Equipment + * EcAlarmReceiverFailure + * Category: Equipment + * EcAlarmRemoteNodeTransmissionError + * Category: Communications + * EcAlarmResourceAtOrNearingCapacity + * Category: QoS + * EcAlarmResponseTimeExcessive + * Category: QoS + * EcAlarmRetransmissionRateExcessive + * Category: QoS + * EcAlarmSoftwareProgramAbnormallyTerminated + * Category: Processing + * EcAlarmSoftwareProgramError + * Category: Processing + * EcAlarmStorageCapacityProblem + * Category: Processing + * EcAlarmTemperatureUnacceptable + * Category: Environmental + * EcAlarmThresholdCrossed + * Category: QoS + * EcAlarmTimingProblem + * Category: Equipment + * EcAlarmToxicLeakDetected + * Category: Environmental + * EcAlarmTransmitFailure + * Category: Equipment + * EcAlarmTransmitterFailure + * Category: Equipment + * EcAlarmUnderlyingResourceUnavailable + * Category: Processing + * EcAlarmVersionMismatch + * Category: Processing + * + */ +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.
+ * The enumerated values MUST be matched by those in your SNMP MIB if + * you wish to have your software interact with SNMP tools.
+ * NB. The use of EcAlarmSeverityIndeterminate should be avoided. + * + * EcAlarmSeverityIndeterminateDo not use + * EcAlarmSeverityCritical + * Immediate intervention required to restore service + * EcAlarmSeverityMajor + * Severe but partial system failure or a problem which + * might recover without intervention + * EcAlarmSeverityMinor + * A failure, but one which is likely to recover or which is + * probably not urgent + * EcAlarmSeverityWarning + * An unusual event which may not indicate any problem, but + * which ought to be looked into + * EcAlarmSeverityCleared + * This indicates the resolution of an earlier issue + * + */ +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.
+ * The enumerated values MUST be matched by those in your SNMP MIB if + * you wish to have your software interact with SNMP tools.
+ * + * EcAlarmTrendNone + * This is not a change in severity of an earlier alarm + * EcAlarmTrendUp + * This is a more severe version of an earlier alarm + * EcAlarmTrendDown + * This is a less severe version of an earlier alarm + * + */ +typedef enum { + EcAlarmTrendNone = 0, + EcAlarmTrendUp = '+', + EcAlarmTrendDown = '-', +} EcAlarmTrend; + +/** This function builds a managed object name from host, process, + * and component.
+ * The host part may be nil ... for the current host.
+ * The process part may be nil ... for the current process.
+ * 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.
+ * 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.
+ * NB. The total length must not exceed 127 ASCII characters. + */ +NSString * +EcMakeManagedObject(NSString *host, NSString *process, NSString *component); + +/**

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. + *

+ *

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. + *

+ */ +@interface EcAlarm : NSObject +{ + 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.
+ * 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.
+ * This method is called during initialisation of an alarm instance to check + * that the supplied event type is legal.
+ * Returns nil if the value is unknown.
+ */ ++ (NSString*) stringFromEventType: (EcAlarmEventType)value; + +/** Provides a human readable string representation of a probable cause.
+ * Returns nil if the value is unknown. + */ ++ (NSString*) stringFromProbableCause: (EcAlarmProbableCause)value; + +/** Provides a human readable string representation of a severity.
+ * Returns nil if the value is unknown. + */ ++ (NSString*) stringFromSeverity: (EcAlarmSeverity)value; + +/** Provides a human readable string representation of a trend.
+ * 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.
+ * If both objects have a notificationID set then the result of the + * numeric comparison of those IDs is used.
+ * 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.
+ * A copy of an object does not 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.
+ * An encoded copy of an object does not 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.
+ */ +- (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; + +/** + * Initialises the receiver as an alarm for a particular event.
+ * The eventDate argument may be nil if the alarm should use the current + * timestamp.
+ * The managedObject, eventType, probableCause, and specificProblem + * arguments uniquely identify the issue for which an alarm is being + * produced.
+ * The perceivedSeverity indicates the importance of the problem, with a + * value of EcAlarmSeverityCleared indicating that the problem is over.
+ * 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:
+ * The two objects must have equal managedObject values and equal (non-zero) + * notificationID values or
+ * the two objects must have equal managedObject values, + * equal eventType values, equal probableCause values, + * and equal specificProblem values.
+ * 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.
+ * 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.
+ * 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.
+ * You should not normally call this as it is reserved for use by code + * which has matched the receiver to an existing alarm.
+ * 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.
+ * 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).
+ * 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.
+ * The componentName may be nil for a process-wide alarm.
+ * The probableCause must NOT be unknown ... it is used to infer + * the event type.
+ * 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.
+ * The componentName may be nil for a process-wide alarm.
+ * The probablCause must NOT be unknown ... it is used to infer + * the event type.
+ * The specificProblem is used to identify the event for which the + * alarm is raised.
+ * The perceivedSeverity must be one of EcAlarmSeverityWarning, + * EcAlarmSeverityMinor, EcAlarmSeverityMajor or EcAlarmSeverityCritical.
+ * 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 + diff --git a/EcAlarm.m b/EcAlarm.m new file mode 100644 index 0000000..f70c4aa --- /dev/null +++ b/EcAlarm.m @@ -0,0 +1,879 @@ +#import +#import +#import +#import +#import +#import +#import + +#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 + diff --git a/EcAlarmDestination.h b/EcAlarmDestination.h new file mode 100644 index 0000000..48673bc --- /dev/null +++ b/EcAlarmDestination.h @@ -0,0 +1,179 @@ + +#import + +@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.
+ *

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 not block + * for a long time or raise an exception. + *

+ */ +@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.
+ * 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.
+ * 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 + + + +/** + *

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). + *

+ *

The buffering and coalescing mechanism is important to prevent floods + * of alarms being sent over network connections. + *

+ *

An EcAlarmDestination instance can also be set to forward alarm + * information to a number of other instances as backups for the main + * destination. + *

+ */ +@interface EcAlarmDestination : NSObject +{ + NSRecursiveLock *_alarmLock; + NSMutableArray *_alarmQueue; + NSMutableSet *_alarmsActive; + NSMutableSet *_managedObjects; + NSTimer *_timer; + BOOL _isRunning; + BOOL _shouldStop; + BOOL _coalesceOff; + BOOL _inTimeout; + NSString *_host; + NSString *_name; + id _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).
+ * See -setBackups: for more information. + */ +- (NSArray*) backups; + +/** Inform the destination of the existence of a managed object.
+ * 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.
+ * 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; + +/* + * 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.
+ * 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.
+ * You may set nil or an empty array to turn off backups, and may use the + * -backups method to get the currently set values.
+ * 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.
+ * If nil this turns off forwarding until it is re-set to a non-nil + * destination.
+ * The destination object is retained by the receiver.
+ * Returns the previously set destination. + */ +- (id) setDestination: (id)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.
+ * 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.
+ * 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 + diff --git a/EcAlarmDestination.m b/EcAlarmDestination.m new file mode 100644 index 0000000..a049b14 --- /dev/null +++ b/EcAlarmDestination.m @@ -0,0 +1,531 @@ + +#import + +#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) setDestination: (id)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)[(id)destination retain]; + [_alarmLock unlock]; + return (id)[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)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 + diff --git a/EcAlarmSinkSNMP.h b/EcAlarmSinkSNMP.h new file mode 100644 index 0000000..7a98e6a --- /dev/null +++ b/EcAlarmSinkSNMP.h @@ -0,0 +1,174 @@ + +#import + +#import "EcAlarmDestination.h" + +/**

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. + *

+ *

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: + *

+ *

Alarms in the table are each identified by a unique numeric identifier, + * the 'notificationID' and traps concerning the alarms carry that ID.
+ * 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.
+ * A clear trap is never sent without a corresponding alarm trap having first + * been sent.
+ * Each trap carries a sequence number so that the SNMP manager is able to + * detect lost traps. + *

+ *

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.
+ * 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.
+ * 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. + *

+ *

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).
+ * The entries in the table contain all the same information as the alarm + * traps, apart from the trap sequence number. + *

+ *

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.
+ * 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.
+ * 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.
+ * 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. + *

+ *

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. + *

+ *

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).
+ * 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. + *

+ *

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.
+ * 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.
+ * 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). + *

+ *

To configure a net-snmp agent to work with this software you need to: + *

+ * Edit /etc/snmp/snmpd.conf to get it to send traps to snmptrapd ... + * + * rwcommunity public + * trap2sink localhost public + * + * and to accept agentx connections via tcp ... + * + * agentxsocket tcp:localhost:705 + * master agentx + * + * Then restart with '/etc/rc.d/init.d/snmpd restart' + * + *

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). + *

+ * + * AlarmsOID + * All SNMP alarm data is in a fixed structure relative to this + * OID in the MIB.
+ * The table of current alarms is at the OID 1 relative to AlarmsOID, + * so you can interrogate the table using; + * snmpwalk -v 1 -c public localhost {AlarmsOID}.1 + * + * The resync flag is at OID 2 relative to AlarmsOID and can be queried + * or set using: + * snmpget -v 1 -c public localhost {AlarmsOID}.2.0 + * + * snmpset -v 1 -c public localhost {AlarmsOID}.2.0 i 1 + * + * The current trap sequence number is at OID 3 relative to AlarmsOID + * and can be queried using: + * snmpget -v 1 -c public localhost {AlarmsOID}.3.0 + * + * The heartbeat poll interval is at OID 4 relative to AlarmsOID + * and can be queried or set using: + * snmpget -v 1 -c public localhost {AlarmsOID}.4.0 + * + * snmpset -v 1 -c public localhost {AlarmsOID}.4.0 i 5 + * + *
+ * ObjectsOID + * All SNMP managed object values are in a table relative to this + * OID in the MIB.
+ * SNMP tools are able to interrogate the table at this OID to see which + * managed objects are currently registered: + * snmpwalk -v 1 -c public localhost {ObjectsOID} + * + *
+ * TrapOID + * The definition of the trap which carries alarms to any monitoring + * system is at this OID. + * + *
+ *

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). + *

+ */ +@interface EcAlarmSinkSNMP : EcAlarmDestination + +/**

Returns the singleton alarm sink instance.
+ * 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. + *

+ *

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. + *

+ */ ++ (EcAlarmSinkSNMP*) alarmSinkSNMP; + +/**

Overrides the default behavior to specify a host and name for the + * SNMP agent this instance is to connect to.
+ * 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. + *

+ *

If an instance has already been created/initialized, this method returns + * the existing instance and its arguments are ignored. + *

+ */ +- (id) initWithHost: (NSString*)host name: (NSString*)name; + +@end + diff --git a/EcAlarmSinkSNMP.m b/EcAlarmSinkSNMP.m new file mode 100644 index 0000000..59e74ce --- /dev/null +++ b/EcAlarmSinkSNMP.m @@ -0,0 +1,1604 @@ + +#import + +#import "EcAlarm.h" +#import "EcAlarmDestination.h" +#import "EcAlarmSinkSNMP.h" +#import "EcProcess.h" + +static EcAlarmSinkSNMP *alarmSink = nil; // The singleton + +@interface EcAlarmSinkSNMP (Private) + +/* Archives the SNMP data to persistent storage so we can re-load it on + * startup. Also stores a description of the active alarms for readability. + */ +- (void) _store; + +/* Sends the alarm out as an SNMP trap. + * If forceClear is YES then sends the trap with a cleared severity + * irrespective of the actual severity stored in the alarm object. + */ +- (void) _trap: (EcAlarm*)alarm forceClear: (BOOL)forceClear; + +@end + +#include +#include +#include +#include + +#include + +static NSString *persistentStore = nil; +static int32_t notificationID = 0; +static NSUInteger managedObjectsCount = 0; +static NSMutableArray *managedObjects = nil; + +static netsnmp_tdata *alarmsTable = 0; +static netsnmp_tdata *objectsTable = 0; + +/* The following scalar variables are made available via SNMP OIDs. + * The agent will handle all GET and (if applicable) SET requests + * to these variables, changing the values as needed. + */ +static int32_t resyncFlag = 0; /* normally not re-syncing */ +static int resyncTimer = 0; /* seconds since started */ +static int32_t trapSequenceNumber = 0; /* XXX: set default value */ +static int32_t pollHeartBeat = 5; /* heartbeat every 5 minutes */ + +/* SNMP data structure for an alarm table row entry + */ +struct alarmsTable_entry +{ + /* Index + */ + int32_t notificationID; + + /* Column values + */ + int32_t perceivedSeverity; + char firstEventDate[32]; + size_t firstEventDate_len; + char eventDate[32]; + size_t eventDate_len; + char managedObject[128]; + size_t managedObject_len; + int32_t ideventType; + int32_t idprobableCause; + char specificProblem[256]; + size_t specificProblem_len; + char proposedRepairAction[256]; + size_t proposedRepairAction_len; + char additionalText[256]; + size_t additionalText_len; + char trendIndicator[2]; + size_t trendIndicator_len; + + int valid; +}; + +/* SNMP data structure for a managed objects table row entry + */ +struct objectsTable_entry +{ + /* Index + */ + char objectId[128]; + size_t objectId_len; + + int valid; +}; + +/* + * function declarations + */ + +static BOOL +heartbeat(time_t now); + +static void +init_EcTrapSink(void); + +static Netsnmp_Node_Handler alarmsTable_handler; + +static Netsnmp_Node_Handler objectsTable_handler; + +static netsnmp_tdata_row * +alarmsTable_createEntry(int32_t notificationID); + +static int +pollHeartBeat_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests); + +static netsnmp_tdata_row * +objectsTable_createEntry(NSString *objectId); + +/* + * column number definitions for table alarmsTable + */ +#define COLUMN_NOTIFICATIONID 1 +#define COLUMN_PERCEIVEDSEVERITY 2 +#define COLUMN_FIRSTEVENTDATE 3 +#define COLUMN_EVENTDATE 4 +#define COLUMN_MANAGEDOBJECT 5 +#define COLUMN_IDEVENTTYPE 6 +#define COLUMN_IDPROBABLECAUSE 7 +#define COLUMN_SPECIFICPROBLEM 8 +#define COLUMN_PROPOSEDREPAIRACTION 9 +#define COLUMN_ADDITIONALTEXT 10 +#define COLUMN_TRENDINDICATOR 11 + +/* + * column number definitions for table objectsTable + */ +#define COLUMN_OBJECTID 1 + + +@interface EcAlarmSinkSNMP (SNMP) + +- (BOOL) snmpClearAlarms: (NSString*)managed; + +- (void) snmpHousekeeping; + +@end + + + + +/* alarmTrap stuff + */ +static oid snmptrap_oid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; + +static oid *additionalText_oid = 0; +static size_t additionalText_len = 0; +static oid *alarmsTable_oid = 0; +static size_t alarmsTable_len = 0; +static oid *alarmTrap_oid = 0; +static size_t alarmTrap_len = 0; +static oid *eventDate_oid = 0; +static size_t eventDate_len = 0; +static oid *eventType_oid = 0; +static size_t eventType_len = 0; +static oid *firstEventDate_oid = 0; +static size_t firstEventDate_len = 0; +static oid *ideventType_oid = 0; +static size_t ideventType_len = 0; +static oid *idprobableCause_oid = 0; +static size_t idprobableCause_len = 0; +static oid *notificationID_oid = 0; +static size_t notificationID_len = 0; +static oid *objectId_oid = 0; +static size_t objectId_len = 0; +static oid *objectsTable_oid = 0; +static size_t objectsTable_len = 0; +static oid *perceivedSeverity_oid = 0; +static size_t perceivedSeverity_len = 0; +static oid *pollHeartBeat_oid = 0; +static size_t pollHeartBeat_len = 0; +static oid *proposedRepairAction_oid = 0; +static size_t proposedRepairAction_len = 0; +static oid *resyncFlag_oid = 0; +static size_t resyncFlag_len = 0; +static oid *specificProblem_oid = 0; +static size_t specificProblem_len = 0; +static oid *trapSequenceNumber_oid = 0; +static size_t trapSequenceNumber_len = 0; +static oid *trendIndicator_oid = 0; +static size_t trendIndicator_len = 0; + + +static const char * +stringFromDate(NSDate *d) +{ + NSCalendarDate *c; + + c = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: + [d timeIntervalSinceReferenceDate]]; + [c setCalendarFormat: @"%Y%m%d %H:%M:%S"]; + return [[c description] UTF8String]; +} + +/* Function to send heartbeat alarm iff the pollHeartBeat interval has passed. + */ +static BOOL +heartbeat(time_t now) +{ + static time_t last = 0; + struct tm *t; + char timestamp[18]; + netsnmp_variable_list *var_list = NULL; + const char *trapName = "HEARTBEAT TRAP"; + const int32_t cause = 0; + const int32_t notification = 0; + const int32_t severity = 4; + const int32_t eventType = 2; + + /* Build current timestamp and send a heartbeat + */ + now = time(0); + if (((now - last) / 60) < pollHeartBeat) + { + return NO; /* Not yet time for a heartbeat */ + } + last = now; + t = gmtime(&now); + sprintf(timestamp, "%04d%02d%02d %02d:%02d:%02d", + t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + + /* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable(&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (u_char*)alarmTrap_oid, + alarmTrap_len * sizeof(oid)); + + if (++trapSequenceNumber <= 0) trapSequenceNumber = 1; + snmp_varlist_add_variable(&var_list, + trapSequenceNumber_oid, + trapSequenceNumber_len, + ASN_INTEGER, + (u_char*)&trapSequenceNumber, + sizeof(trapSequenceNumber)); + + snmp_varlist_add_variable(&var_list, + notificationID_oid, + notificationID_len, ASN_INTEGER, + (u_char*)¬ification, /* Special for heartbeat */ + sizeof(notification)); + + snmp_varlist_add_variable(&var_list, + perceivedSeverity_oid, + perceivedSeverity_len, + ASN_INTEGER, + (u_char*)&severity, /* warning */ + sizeof(severity)); + + snmp_varlist_add_variable(&var_list, + firstEventDate_oid, + firstEventDate_len, + ASN_OCTET_STR, + 0, /* not required */ + 0); + + snmp_varlist_add_variable(&var_list, + eventDate_oid, eventDate_len, + ASN_OCTET_STR, + (u_char*)timestamp, + strlen(timestamp)); + + snmp_varlist_add_variable(&var_list, + objectId_oid, objectId_len, + ASN_OCTET_STR, + 0, /* not required */ + 0); + + snmp_varlist_add_variable(&var_list, + ideventType_oid, ideventType_len, + ASN_INTEGER, + (u_char*)&eventType, /* heartbeat */ + sizeof(eventType)); + + snmp_varlist_add_variable(&var_list, + idprobableCause_oid, + idprobableCause_len, ASN_INTEGER, + (u_char*)&cause, + sizeof(cause)); + + snmp_varlist_add_variable(&var_list, + specificProblem_oid, + specificProblem_len, + ASN_OCTET_STR, + 0, /* not required */ + 0); + + snmp_varlist_add_variable(&var_list, + proposedRepairAction_oid, + proposedRepairAction_len, + ASN_OCTET_STR, + 0, /* not required */ + 0); + + snmp_varlist_add_variable(&var_list, + additionalText_oid, + additionalText_len, + ASN_OCTET_STR, + (u_char*)trapName, + strlen(trapName)); + + snmp_varlist_add_variable(&var_list, + trendIndicator_oid, + trendIndicator_len, + ASN_OCTET_STR, + 0, /* not required */ + 0); + /* + * Send the trap to the list of configured destinations + * and clean up + */ + DEBUGMSGTL(("EcTrapSink", "Sending heartbeat trap.\n")); + send_v2trap(var_list); + snmp_free_varbind(var_list); + return YES; +} + +static void +setAlarmTableEntry(netsnmp_tdata_row *row, EcAlarm *alarm) +{ + struct alarmsTable_entry *e; + const char *s; + + [alarm setExtra: (void*)row]; + [alarm freeze]; // Once it's in the snmp table, no more change + e = (struct alarmsTable_entry *)row->data; + + e->notificationID = [alarm notificationID]; + + e->perceivedSeverity = [alarm perceivedSeverity]; + + s = stringFromDate([alarm firstEventDate]); + strcpy(e->firstEventDate, s); + e->firstEventDate_len = strlen(s); + + s = stringFromDate([alarm eventDate]); + strcpy(e->eventDate, s); + e->eventDate_len = strlen(s); + + s = [[alarm managedObject] UTF8String]; + strcpy(e->managedObject, s); + e->managedObject_len = strlen(s); + + e->ideventType = [alarm eventType]; + + e->idprobableCause = [alarm probableCause]; + + s = [[alarm specificProblem] UTF8String]; + strcpy(e->specificProblem, s); + e->specificProblem_len = strlen(s); + + s = [[alarm proposedRepairAction] UTF8String]; + strcpy(e->proposedRepairAction, s); + e->proposedRepairAction_len = strlen(s); + + s = [[alarm additionalText] UTF8String]; + strcpy(e->additionalText, s); + e->additionalText_len = strlen(s); + + e->trendIndicator[0] = [alarm trendIndicator]; + if (0 == e->trendIndicator[0]) + { + e->trendIndicator_len = 0; + } + else + { + e->trendIndicator_len = 1; + e->trendIndicator[1] = 0; + } +} + +/* Regular timer called at one second intervals to check for updates from + * alarm sources, update SNMP tables, generate alerts, and send heartbeats + */ +static void +housekeeping(unsigned int clientreg, void *clientarg) +{ + if (0 == resyncFlag) + { + resyncTimer = 0; + } + else + { + /* We automatically leave resync mode after five minutes (300 seconds) + * if resync mode has not been turned off via SNMP. + */ + if (300 <= ++resyncTimer) + { + resyncTimer = 0; + resyncFlag = 0; + } + } + [alarmSink snmpHousekeeping]; +} + + +static void +init_EcTrapSink(void) +{ + netsnmp_handler_registration *reg; + netsnmp_table_registration_info *tinfo; + netsnmp_watcher_info *winfo; + NSString *oidString; + NSUserDefaults *defaults; + NSArray *array; + oid *oids; + int len; + int i; + + defaults = [NSUserDefaults standardUserDefaults]; + /* First convert the trap OID from dotted integer format to an array + * of net-snmp oid values. + */ + oidString = [defaults stringForKey: @"TrapOID"]; + if (nil == oidString) oidString = @"1.3.6.1.4.1.37374.3.0.1"; + array = [oidString componentsSeparatedByString: @"."]; + alarmTrap_len = [array count]; + alarmTrap_oid = (oid*)malloc(sizeof(oid) * alarmTrap_len); + for (i = 0; i < alarmTrap_len; i++) + { + alarmTrap_oid[i] = [[array objectAtIndex: i] intValue]; + } + + /* Now use the dotted integer format 'alarms' OID as the basis to set up + * all the alarm data OIDs. + */ + oidString = [defaults stringForKey: @"AlarmsOID"]; + if (nil == oidString) oidString = @"1.3.6.1.4.1.37374.1"; + array = [oidString componentsSeparatedByString: @"."]; + len = [array count]; + oids = (oid*)malloc(sizeof(oid) * (len + 2)); + for (i = 0; i < len; i++) + { + oids[i] = [[array objectAtIndex: i] intValue]; + } + oids[len] = 0; // alarmsTable + oids[len+1] = 0; // alarmsEntry + + alarmsTable_len = len + 2; + alarmsTable_oid = (oid*)malloc(sizeof(oid) * alarmsTable_len); + memcpy(alarmsTable_oid, oids, sizeof(oid) * (len + 2)); + + resyncFlag_len = len + 1; + resyncFlag_oid = (oid*)malloc(sizeof(oid) * resyncFlag_len); + memcpy(resyncFlag_oid, oids, sizeof(oid) * len); + resyncFlag_oid[len] = 2; + + trapSequenceNumber_len = len + 1; + trapSequenceNumber_oid = (oid*)malloc(sizeof(oid) * trapSequenceNumber_len); + memcpy(trapSequenceNumber_oid, oids, sizeof(oid) * len); + trapSequenceNumber_oid[len] = 3; + + pollHeartBeat_len = len + 1; + pollHeartBeat_oid = (oid*)malloc(sizeof(oid) * pollHeartBeat_len); + memcpy(pollHeartBeat_oid, oids, sizeof(oid) * len); + pollHeartBeat_oid[len] = 4; + + notificationID_len = len + 3; + notificationID_oid = (oid*)malloc(sizeof(oid) * notificationID_len); + memcpy(notificationID_oid, oids, sizeof(oid) * (len + 2)); + notificationID_oid[len+2] = 1; + + perceivedSeverity_len = len + 3; + perceivedSeverity_oid = (oid*)malloc(sizeof(oid) * perceivedSeverity_len); + memcpy(perceivedSeverity_oid, oids, sizeof(oid) * (len + 2)); + perceivedSeverity_oid[len+2] = 2; + + firstEventDate_len = len + 3; + firstEventDate_oid = (oid*)malloc(sizeof(oid) * firstEventDate_len); + memcpy(firstEventDate_oid, oids, sizeof(oid) * (len + 2)); + firstEventDate_oid[len+2] = 3; + + eventDate_len = len + 3; + eventDate_oid = (oid*)malloc(sizeof(oid) * eventDate_len); + memcpy(eventDate_oid, oids, sizeof(oid) * (len + 2)); + eventDate_oid[len+2] = 4; + + eventType_len = len + 3; + eventType_oid = (oid*)malloc(sizeof(oid) * eventType_len); + memcpy(eventType_oid, oids, sizeof(oid) * (len + 2)); + eventType_oid[len+2] = 6; + + idprobableCause_len = len + 3; + idprobableCause_oid = (oid*)malloc(sizeof(oid) * idprobableCause_len); + memcpy(idprobableCause_oid, oids, sizeof(oid) * (len + 2)); + idprobableCause_oid[len+2] = 7; + + specificProblem_len = len + 3; + specificProblem_oid = (oid*)malloc(sizeof(oid) * specificProblem_len); + memcpy(specificProblem_oid, oids, sizeof(oid) * (len + 2)); + specificProblem_oid[len+2] = 8; + + proposedRepairAction_len = len + 3; + proposedRepairAction_oid + = (oid*)malloc(sizeof(oid) * proposedRepairAction_len); + memcpy(proposedRepairAction_oid, oids, sizeof(oid) * (len + 2)); + proposedRepairAction_oid[len+2] = 9; + + additionalText_len = len + 3; + additionalText_oid = (oid*)malloc(sizeof(oid) * additionalText_len); + memcpy(additionalText_oid, oids, sizeof(oid) * (len + 2)); + additionalText_oid[len+2] = 10; + + trendIndicator_len = len + 3; + trendIndicator_oid = (oid*)malloc(sizeof(oid) * trendIndicator_len); + memcpy(trendIndicator_oid, oids, sizeof(oid) * (len + 2)); + trendIndicator_oid[len+2] = 11; + + free(oids); + oidString = [defaults stringForKey: @"ObjectsOID"]; + if (nil == oidString) oidString = @"1.3.6.1.4.1.37374.2"; + array = [oidString componentsSeparatedByString: @"."]; + len = [array count]; + objectId_len = len + 3; + objectId_oid = (oid*)malloc(sizeof(oid) * objectId_len); + for (i = 0; i < len; i++) + { + objectId_oid[i] = [[array objectAtIndex: i] intValue]; + } + objectId_oid[len] = 1; // objectsTable + objectId_oid[len+1] = 1; // objectsEntry + objectId_oid[len+2] = 1; // objectId + + objectsTable_len = len + 1; + objectsTable_oid = (oid*)malloc(sizeof(oid) * objectsTable_len); + memcpy(objectsTable_oid, objectId_oid, sizeof(oid) * (len + 1)); + + /* Create the managed objects table as a read-only item for SNMP. + */ + reg = netsnmp_create_handler_registration( + "objectsTable", + objectsTable_handler, + objectsTable_oid, + objectsTable_len, + HANDLER_CAN_RONLY); + objectsTable = netsnmp_tdata_create_table("objectsTable", 0); + tinfo = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); + netsnmp_table_helper_add_indexes(tinfo, + ASN_OCTET_STR, /* index: objectId */ + 0); + tinfo->min_column = COLUMN_OBJECTID; + tinfo->max_column = COLUMN_OBJECTID; + netsnmp_tdata_register(reg, objectsTable, tinfo); + + /* Create the alarms table as a red-only item for SNMP. + */ + reg = netsnmp_create_handler_registration( + "alarmsTable", + alarmsTable_handler, + alarmsTable_oid, + alarmsTable_len, + HANDLER_CAN_RONLY); + alarmsTable = netsnmp_tdata_create_table("alarmsTable", 0); + tinfo = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); + netsnmp_table_helper_add_indexes(tinfo, + ASN_INTEGER, /* index: notificationID */ + 0); + tinfo->min_column = COLUMN_NOTIFICATIONID; + tinfo->max_column = COLUMN_TRENDINDICATOR; + netsnmp_tdata_register(reg, alarmsTable, tinfo); + + /* Register scalar watchers for each of the MIB objects. + */ + reg = netsnmp_create_handler_registration( + "resyncFlag", + NULL, + resyncFlag_oid, + resyncFlag_len, + HANDLER_CAN_RWRITE); + winfo = netsnmp_create_watcher_info(&resyncFlag, + sizeof(int32_t), ASN_INTEGER, WATCHER_FIXED_SIZE); + if (netsnmp_register_watched_scalar(reg, winfo) < 0) + { + snmp_log(LOG_ERR, "Failed to register watched resyncFlag"); + } + + reg = netsnmp_create_handler_registration( + "trapSequenceNumber", + NULL, + trapSequenceNumber_oid, + trapSequenceNumber_len, + HANDLER_CAN_RONLY); + winfo = netsnmp_create_watcher_info(&trapSequenceNumber, + sizeof(int32_t), ASN_INTEGER, WATCHER_FIXED_SIZE); + if (netsnmp_register_watched_scalar(reg, winfo) < 0) + { + snmp_log(LOG_ERR, "Failed to register watched trapSequenceNumber"); + } + + reg = netsnmp_create_handler_registration( + "pollHeartBeat", + pollHeartBeat_handler, + pollHeartBeat_oid, + pollHeartBeat_len, + HANDLER_CAN_RWRITE); + winfo = netsnmp_create_watcher_info(&pollHeartBeat, + sizeof(int32_t), ASN_INTEGER, WATCHER_FIXED_SIZE); + if (netsnmp_register_watched_scalar(reg, winfo) < 0) + { + snmp_log(LOG_ERR, "Failed to register watched pollHeartBeat"); + } + + /* get alarms at one second intervals to do housekeeping. + */ + snmp_alarm_register(1, SA_REPEAT, housekeeping, NULL); +} + + + +/* + * create a new row in the table + */ +static netsnmp_tdata_row * +alarmsTable_createEntry(int32_t notificationID) +{ + struct alarmsTable_entry *entry; + netsnmp_tdata_row *row; + + entry = SNMP_MALLOC_TYPEDEF(struct alarmsTable_entry); + if (!entry) + { + return NULL; + } + row = netsnmp_tdata_create_row(); + if (!row) + { + SNMP_FREE(entry); + return NULL; + } + row->data = entry; + entry->notificationID = notificationID; + netsnmp_tdata_row_add_index(row, + ASN_INTEGER, &(entry->notificationID), sizeof(entry->notificationID)); + netsnmp_tdata_add_row(alarmsTable, row); + return row; +} + + +static int +pollHeartBeat_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + int32_t *pollHeartBeat_cache = NULL; + int32_t tmp; + + DEBUGMSGTL(("EcTrapSink", "Got instance request:\n")); + + switch (reqinfo->mode) + { + case MODE_GET: + snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, + (u_char*) &pollHeartBeat, sizeof(pollHeartBeat)); + break; + + case MODE_SET_RESERVE1: + if (requests->requestvb->type != ASN_INTEGER) + netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); + break; + + case MODE_SET_RESERVE2: + /* + * store old info for undo later + */ + memdup((u_char**)&pollHeartBeat_cache, + (u_char*)&pollHeartBeat, sizeof(pollHeartBeat)); + if (pollHeartBeat_cache == NULL) + { + netsnmp_set_request_error(reqinfo, requests, + SNMP_ERR_RESOURCEUNAVAILABLE); + return SNMP_ERR_NOERROR; + } + netsnmp_request_add_list_data(requests, + netsnmp_create_data_list + ("EcTrapSink", + pollHeartBeat_cache, free)); + break; + + case MODE_SET_ACTION: + /* + * update current + */ + tmp = *(requests->requestvb->val.integer); + if (tmp > 0) + { + pollHeartBeat = tmp; + [alarmSink _store]; + DEBUGMSGTL(("EcTrapSink", "updated pollHeartBeat -> %d\n", tmp)); + } + else + { + DEBUGMSGTL(("EcTrapSink", "ignored pollHeartBeat -> %d\n", tmp)); + netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGVALUE); + } + break; + + case MODE_SET_UNDO: + pollHeartBeat = + *((int32_t*)netsnmp_request_get_list_data(requests, "EcTrapSink")); + break; + + case MODE_SET_COMMIT: + case MODE_SET_FREE: + /* + * nothing to do + */ + break; + } + + return SNMP_ERR_NOERROR; +} + +/** handles requests for the alarmsTable table */ +static int +alarmsTable_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + netsnmp_request_info *request; + netsnmp_table_request_info *table_info; + struct alarmsTable_entry *table_entry; + + switch (reqinfo->mode) + { + /* + * Read-support (also covers GetNext requests) + */ + case MODE_GET: + for (request = requests; request; request = request->next) + { + table_entry = (struct alarmsTable_entry *) + netsnmp_tdata_extract_entry(request); + table_info = netsnmp_extract_table_info(request); + + switch (table_info->colnum) + { + case COLUMN_NOTIFICATIONID: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER, + table_entry->notificationID); + break; + + case COLUMN_PERCEIVEDSEVERITY: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER, + table_entry->perceivedSeverity); + break; + + case COLUMN_FIRSTEVENTDATE: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, + (u_char *) table_entry-> + firstEventDate, + table_entry->firstEventDate_len); + break; + + case COLUMN_EVENTDATE: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, + (u_char *) table_entry->eventDate, + table_entry->eventDate_len); + break; + + case COLUMN_MANAGEDOBJECT: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, + (u_char *) table_entry-> + managedObject, + table_entry->managedObject_len); + break; + + case COLUMN_IDEVENTTYPE: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER, + table_entry->ideventType); + break; + + case COLUMN_IDPROBABLECAUSE: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER, + table_entry->idprobableCause); + break; + + case COLUMN_SPECIFICPROBLEM: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, + (u_char *) table_entry-> + specificProblem, + table_entry->specificProblem_len); + break; + + case COLUMN_PROPOSEDREPAIRACTION: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, + (u_char *) table_entry-> + proposedRepairAction, + table_entry-> + proposedRepairAction_len); + break; + + case COLUMN_ADDITIONALTEXT: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, + (u_char *) table_entry-> + additionalText, + table_entry->additionalText_len); + break; + + case COLUMN_TRENDINDICATOR: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, + (u_char *) table_entry-> + trendIndicator, + table_entry->trendIndicator_len); + break; + + default: + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHOBJECT); + break; + } + } + break; + } + return SNMP_ERR_NOERROR; +} + +/* + * create a new row in the table + */ +static netsnmp_tdata_row * +objectsTable_createEntry(NSString *objectId) +{ + struct objectsTable_entry *entry; + netsnmp_tdata_row *row; + const char *str; + + entry = SNMP_MALLOC_TYPEDEF(struct objectsTable_entry); + if (!entry) + { + return NULL; + } + row = netsnmp_tdata_create_row(); + if (!row) + { + SNMP_FREE(entry); + return NULL; + } + row->data = entry; + str = [objectId UTF8String]; + entry->objectId_len = strlen(str); + strcpy(entry->objectId, str); + netsnmp_tdata_row_add_index(row, ASN_OCTET_STR, + entry->objectId, entry->objectId_len); + netsnmp_tdata_add_row(objectsTable, row); + return row; +} + + +/** handles requests for the objectsTable table */ +static int +objectsTable_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + netsnmp_request_info *request; + netsnmp_table_request_info *table_info; + struct objectsTable_entry *table_entry; + + switch (reqinfo->mode) + { + /* + * Read-support (also covers GetNext requests) + */ + case MODE_GET: + for (request = requests; request; request = request->next) + { + table_entry = (struct objectsTable_entry *) + netsnmp_tdata_extract_entry(request); + table_info = netsnmp_extract_table_info(request); + + switch (table_info->colnum) + { + case COLUMN_OBJECTID: + if (!table_entry) + { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, + (u_char *) table_entry->objectId, + table_entry->objectId_len); + break; + + default: + netsnmp_set_request_error( + reqinfo, request, SNMP_NOSUCHOBJECT); + break; + } + } + break; + } + return SNMP_ERR_NOERROR; +} + + + + +static NSLock *classLock = nil; + +@implementation EcAlarmSinkSNMP + ++ (EcAlarmSinkSNMP*) alarmSinkSNMP +{ + EcAlarmSinkSNMP *sink; + + [classLock lock]; + sink = [alarmSink retain]; + [classLock unlock]; + if (nil == sink) + { + sink = [self new]; + } + return [sink autorelease]; +} + ++ (void) initialize +{ + if (nil == classLock) + { + classLock = [NSLock new]; + } +} + +- (id) init +{ + [classLock lock]; + if (nil == alarmSink) + { + alarmSink = self = [super init]; + } + else + { + [self release]; + self = [alarmSink retain]; + } + [classLock unlock]; + return self; +} + +- (id) initWithHost: (NSString*)host name: (NSString*)name +{ + [classLock lock]; + if (nil == alarmSink) + { + alarmSink = self = [super initWithHost: host name: name]; + } + else + { + [self release]; + self = [alarmSink retain]; + } + [classLock unlock]; + return self; +} + +- (void) run +{ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + NSString *p; + NSDictionary *d; + + snmp_enable_calllog(); + snmp_enable_stderrlog(); + + /* Make us an agentx client. + */ + netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1); + + /* Initialize tcpip, if necessary + */ + SOCK_STARTUP; + + /* Initialize the agent library to use the standard agentx port + */ + if (nil == _host || [_host isEqual: @""] || [_host isEqual: @"*"]) + { + ASSIGN(_host, @"localhost"); // Local host + } + if (nil == _name || [_name isEqual: @""] || [_name isEqual: @"*"]) + { + ASSIGN(_name, @"705"); // Standard Agent-X port + } + p = [NSString stringWithFormat: @"tcp:%@:%@", _host, _name]; + netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_X_SOCKET, [p UTF8String]); + if (0 != init_agent("EcTrapSink")) + { + NSLog(@"Unable to initialise EcTrapSink as an SNMP sub-agent"); + } + + /* Initialize MIB code here + */ + init_EcTrapSink(); + + /* Will read ecAlarmSinkSNMP.conf files. + */ + init_snmp("EcAlarmSinkSNMP"); + + /* Populate tables and set scalar values from contents of files. + */ + p = [[EcProc cmdDataDirectory] stringByAppendingPathComponent: @"SNMP.plist"]; + persistentStore = [p copy]; + if ([[NSFileManager defaultManager] isReadableFileAtPath: p] == YES + && (d = [NSDictionary dictionaryWithContentsOfFile: p]) != nil) + { + NSData *archive; + NSEnumerator *enumerator; + EcAlarm *alarm; + NSString *name; + + /* get trap and notification numbers. + */ + trapSequenceNumber = [[d objectForKey: @"TrapSequence"] intValue]; + notificationID = [[d objectForKey: @"NotificationID"] intValue]; + pollHeartBeat = [[d objectForKey: @"PollheartBeat"] intValue]; + if (pollHeartBeat < 1) pollHeartBeat = 5; + + /* Get managed objects table and copy it into SNMP table structure. + */ + managedObjects = [[d objectForKey: @"ManagedObjects"] mutableCopy]; + enumerator = [managedObjects objectEnumerator]; + while (nil != (name = [enumerator nextObject])) + { + objectsTable_createEntry(name); + } + + /* Get archived current alarms and add them to the active SNMP table. + */ + archive = [[d objectForKey: @"AlarmsActive"] mutableCopy]; + if (nil != archive) + { + _alarmsActive + = [[NSUnarchiver unarchiveObjectWithData: archive] retain]; + } + enumerator = [_alarmsActive objectEnumerator]; + while (nil != (alarm = [enumerator nextObject])) + { + static netsnmp_tdata_row *row; + + row = alarmsTable_createEntry([alarm notificationID]); + setAlarmTableEntry(row, alarm); + } + } + if (nil == managedObjects) + { + managedObjects = [NSMutableArray new]; + } + managedObjectsCount = [managedObjects count]; + if (nil == _alarmsActive) + { + _alarmsActive = [NSMutableSet new]; + } + [pool release]; + pool = [NSAutoreleasePool new]; + + snmp_log(LOG_INFO,"Control-SNMP-agent is up and running.\n"); + + _isRunning = YES; + while (NO == _shouldStop) + { + agent_check_and_process(1); /* 0 == don't block */ + [pool release]; + pool = [NSAutoreleasePool new]; + } + [pool release]; + + /* at shutdown time */ + snmp_shutdown("EcTrapSink"); + _isRunning = NO; + SOCK_CLEANUP; +} + +- (void) setBackups: (NSArray*)backups +{ + if (nil != backups) + { + [NSException raise: NSInvalidArgumentException + format: @"Calling -setBackups: for a sink is not allowed"]; + } +} + +- (id) setDestination: (id)destination +{ + if (nil != destination) + { + [NSException raise: NSInvalidArgumentException + format: @"Calling -setDestination: for a sink is not allowed"]; + } + return nil; +} + +@end + +@implementation EcAlarmSinkSNMP (Private) + +- (void) _store +{ + NSMutableDictionary *d; + + [_alarmLock lock]; + NS_DURING + { + d = [NSMutableDictionary dictionaryWithCapacity: 6]; + [d setObject: [NSNumber numberWithInt: notificationID] + forKey: @"NotificationID"]; + [d setObject: [NSNumber numberWithInt: trapSequenceNumber] + forKey: @"TrapSequence"]; + [d setObject: [NSNumber numberWithInt: pollHeartBeat] + forKey: @"PollHeartBeat"]; + [d setObject: managedObjects + forKey: @"ManagedObjects"]; + [d setObject: [NSArchiver archivedDataWithRootObject: _alarmsActive] + forKey: @"AlarmsActive"]; + [d setObject: _alarmsActive + forKey: @"AlarmsDescription"]; + [d writeToFile: persistentStore atomically: YES]; + [_alarmLock unlock]; + } + NS_HANDLER + { + [_alarmLock unlock]; + NSLog(@"Problem storing alarm data to disk ... %@", localException); + } + NS_ENDHANDLER +} + +- (void) _trap: (EcAlarm*)alarm forceClear: (BOOL)forceClear +{ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + netsnmp_variable_list *var_list = NULL; + int32_t i; + const char *s; + char ti[2]; + + /* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable( + &var_list, + snmptrap_oid, + OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (u_char*)alarmTrap_oid, + alarmTrap_len * sizeof(oid)); + + /* + * Add any objects from the trap definition + */ + if (++trapSequenceNumber <= 0) trapSequenceNumber = 1; + snmp_varlist_add_variable( + &var_list, + trapSequenceNumber_oid, + trapSequenceNumber_len, + ASN_INTEGER, + (u_char*)&trapSequenceNumber, + sizeof(trapSequenceNumber)); + + i = (int32_t)[alarm notificationID]; + snmp_varlist_add_variable( + &var_list, + notificationID_oid, + notificationID_len, + ASN_INTEGER, + (u_char*)&i, sizeof(i)); + + if (YES == forceClear) + { + i = EcAlarmSeverityCleared; + } + else + { + i = (int32_t)[alarm perceivedSeverity]; + } + snmp_varlist_add_variable( + &var_list, + perceivedSeverity_oid, + perceivedSeverity_len, + ASN_INTEGER, + (u_char*)&i, sizeof(i)); + + s = stringFromDate([alarm firstEventDate]); + snmp_varlist_add_variable( + &var_list, + firstEventDate_oid, + firstEventDate_len, + ASN_OCTET_STR, + (u_char*)s, strlen(s)); + + s = stringFromDate([alarm eventDate]); + snmp_varlist_add_variable( + &var_list, + eventDate_oid, + eventDate_len, + ASN_OCTET_STR, + (u_char*)s, strlen(s)); + + s = [[alarm managedObject] UTF8String]; + snmp_varlist_add_variable( + &var_list, + objectId_oid, + objectId_len, + ASN_OCTET_STR, + (u_char*)s, strlen(s)); + + i = (int32_t)[alarm eventType]; + snmp_varlist_add_variable( + &var_list, + ideventType_oid, + ideventType_len, + ASN_INTEGER, + (u_char*)&i, sizeof(i)); + + i = (int32_t)[alarm probableCause]; + snmp_varlist_add_variable( + &var_list, + idprobableCause_oid, + idprobableCause_len, + ASN_INTEGER, + (u_char*)&i, sizeof(i)); + + s = [[alarm specificProblem] UTF8String]; + snmp_varlist_add_variable( + &var_list, + specificProblem_oid, + specificProblem_len, + ASN_OCTET_STR, + (u_char*)s, strlen(s)); + + s = [[alarm proposedRepairAction] UTF8String]; + snmp_varlist_add_variable( + &var_list, + proposedRepairAction_oid, + proposedRepairAction_len, + ASN_OCTET_STR, + (u_char*)s, strlen(s)); + + s = [[alarm additionalText] UTF8String]; + snmp_varlist_add_variable( + &var_list, + additionalText_oid, + additionalText_len, + ASN_OCTET_STR, + (u_char*)s, strlen(s)); + + ti[0] = (u_char)[alarm trendIndicator]; + ti[1] = 0; + snmp_varlist_add_variable( + &var_list, + trendIndicator_oid, + trendIndicator_len, + ASN_OCTET_STR, + (u_char*)ti, strlen(ti)); + + /* Send the trap and clean up + */ + send_v2trap(var_list); + snmp_free_varbind(var_list); + [pool release]; +} + +@end + + + + + +@implementation EcAlarmSinkSNMP (SNMP) + +/* Call this only with the lock locked. + */ +- (BOOL) snmpClearAlarms: (NSString*)managed +{ + NSEnumerator *enumerator; + EcAlarm *alarm; + BOOL changed = NO; + + /* Enumerate a copy of the _alarmsActive set so that removing objects + * from the set while enumerating is safe. + */ + enumerator = [[_alarmsActive allObjects] objectEnumerator]; + while (nil != (alarm = [enumerator nextObject])) + { + if ([[alarm managedObject] hasPrefix: managed]) + { + netsnmp_tdata_row *row; + + /* Remove from the ObjC table. + */ + [_alarmsActive removeObject: alarm]; + + /* Find and remove the SNMP table entry. + */ + row = (netsnmp_tdata_row*)[alarm extra]; + if (0 != row) + { + struct alarmsTable_entry *entry; + + entry = (struct alarmsTable_entry *) + netsnmp_tdata_remove_and_delete_row(alarmsTable, + row); + if (0 != entry) + { + SNMP_FREE(entry); + } + } + + /* send the clear for the entry. + */ + [alarmSink _trap: alarm forceClear: YES]; + changed = YES; + } + } + return changed; +} + +- (void) snmpHousekeeping +{ + time_t now; + BOOL changed = NO; + + DEBUGMSGTL(("EcTrapSink", "Housekeeping timer called.\n")); + + [_alarmLock lock]; + if (NO == _inTimeout && YES == _isRunning && NO == _shouldStop) + { + _inTimeout = YES; + if (0 == resyncFlag) + { + /* Check for alarms. + */ + while ([_alarmQueue count] > 0) + { + id o = [_alarmQueue objectAtIndex: 0]; + + if (YES == [o isKindOfClass: [EcAlarm class]]) + { + EcAlarm *next = (EcAlarm*)o; + EcAlarm *prev = [_alarmsActive member: next]; + NSString *m = [next managedObject]; + + if (nil == prev) + { + [next setFirstEventDate: [next eventDate]]; + } + else + { + [next setFirstEventDate: [prev firstEventDate]]; + } + + if ([next perceivedSeverity] == EcAlarmSeverityCleared) + { + if (nil != prev) + { + netsnmp_tdata_row *row; + + /* Remove from the ObjC table. + */ + [prev retain]; + [_alarmsActive removeObject: prev]; + + /* Find and remove the SNMP table entry. + */ + row = (netsnmp_tdata_row*)[prev extra]; + if (0 != row) + { + struct alarmsTable_entry *entry; + + entry = (struct alarmsTable_entry *) + netsnmp_tdata_remove_and_delete_row(alarmsTable, + row); + if (0 != entry) + { + SNMP_FREE(entry); + } + } + + /* send the clear for the entry. + */ + [next setNotificationID: [prev notificationID]]; + [alarmSink _trap: next forceClear: NO]; + [self alarmFwd: next]; + [prev release]; + changed = YES; + } + } + else + { + /* Register any new managed object. + */ + if (NO == [managedObjects containsObject: m]) + { + objectsTable_createEntry(m); + [managedObjects addObject: m]; + managedObjectsCount = [managedObjects count]; + [self domanageFwd: m]; + changed = YES; + } + + if ((nil == prev) || ([next perceivedSeverity] + != [prev perceivedSeverity])) + { + netsnmp_tdata_row *row; + + if (nil == prev) + { + row = alarmsTable_createEntry + ([next notificationID]); + /* Add and send the new alarm + */ + if (++notificationID <= 0) notificationID = 1; + [next setNotificationID: notificationID]; + } + else + { + prev = [[prev retain] autorelease]; + [_alarmsActive removeObject: prev]; + row = (netsnmp_tdata_row*)[prev extra]; + /* send the clear for the entry. + */ + [next setNotificationID: [prev notificationID]]; + [alarmSink _trap: prev forceClear: YES]; + } + + /* copy new version of data into row + * and send new severity trap. + */ + setAlarmTableEntry(row, next); + [_alarmsActive addObject: next]; + [alarmSink _trap: next forceClear: NO]; + [self alarmFwd: next]; + changed = YES; + } + } + } + else + { + NSString *s = [o description]; + + if (YES == [s hasPrefix: @"domanage "]) + { + NSString *m = [s substringFromIndex: 9]; + + changed = [self snmpClearAlarms: m]; + if (NO == [managedObjects containsObject: m]) + { + objectsTable_createEntry(m); + [managedObjects addObject: m]; + managedObjectsCount = [managedObjects count]; + changed = YES; + } + } + else if (YES == [s hasPrefix: @"unmanage "]) + { + NSString *m = [s substringFromIndex: 9]; + + if (YES == [managedObjects containsObject: m]) + { + const char *str; + netsnmp_tdata_row *row; + + changed = YES; + [self snmpClearAlarms: m]; + str = [m UTF8String]; + row = netsnmp_tdata_row_first(objectsTable); + while (0 != row) + { + struct objectsTable_entry *entry; + + entry = (struct objectsTable_entry *)row->data; + if (0 == strcmp(entry->objectId, str)) + { + netsnmp_tdata_remove_and_delete_row + (objectsTable, row); + SNMP_FREE(entry); + break; + } + row = netsnmp_tdata_row_next(objectsTable, row); + } + [managedObjects removeObject: m]; + if (YES == [m hasSuffix: @"_"]) + { + NSEnumerator *e; + NSString *s; + + e = [[[managedObjects copy] autorelease] + objectEnumerator]; + while (nil != (s = [e nextObject])) + { + if (YES == [s hasPrefix: m]) + { + [managedObjects removeObject: s]; + } + } + } + managedObjectsCount = [managedObjects count]; + } + } + else + { + NSLog(@"ERROR ... unexpected command '%@'", s); + } + } + [_alarmQueue removeObjectAtIndex: 0]; + [_alarmLock unlock]; + [_alarmLock lock]; + } + } + } + [_alarmLock unlock]; + now = time(0); + if (YES == heartbeat(now)) + { + changed = YES; + } + if (YES == changed) + { + [alarmSink _store]; + } +} + +@end + diff --git a/EcAlerter.h b/EcAlerter.h new file mode 100644 index 0000000..cdec91c --- /dev/null +++ b/EcAlerter.h @@ -0,0 +1,32 @@ + +#import + +@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 + diff --git a/EcAlerter.m b/EcAlerter.m new file mode 100644 index 0000000..563799a --- /dev/null +++ b/EcAlerter.m @@ -0,0 +1,923 @@ + +#import +#import + +#import "EcProcess.h" +#import "EcAlerter.h" +#import "NSFileHandle+Printf.h" + +#include + +@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; +} + +/** + *

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. + *

+ *

The configured rules are compared against each message and any + * actions associated with a matching rule are performed.
+ * The matching fields in each rule are - + *

+ * + * Host + * 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. + * + * Server + * 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. + * + * Type + * The type of message ... Error or Alert. + * If this is not specified, messages of any type may match. + * + * Pattern + * 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. + * + * Stop + * 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.
+ * 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. + *
+ * Flush + * 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.
+ * As a special case, instead of the boolean value, this may take + * the value Email or Sms indicating that a flush + * should be performed, but only on the specified type of messages.
+ * beware The batching mechanism exists to prevent + * a single problem triggering floods of messages. You should only + * override it using Flush where you are sure + * that messages triggering the flush will be infrequent. + *
+ *
+ *

There are two additional fields Extra1 and Extra2 + * 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. + *

+ *

When a match is found the full message is normally sent to all the + * destinations listed in the Email and Sms arrays in + * the rule, and logged to all the destinations in the Log array.
+ * However, the Replacement field may be used to specify + * a message to be sent in the place of the one received. Within the + * Replacement string values enclosed in curly brackets will + * be substituted as follows - + *

+ * + * Extra1 + * The text in the message matched by the Extra1 pattern (if any) + * Extra2 + * The text in the message matched by the Extra2 pattern (if any) + * Host + * The host name of the original message + * Server + * The server name of the original message + * Type + * The type of the original message + * Timestamp + * The timestamp of the original message + * Message + * The text of the original message + * Match + * The text matched by the Pattern if any + * + *

The Log 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).
+ * After the colon you may place a table name, but if you don't then + * the message will be logged to the 'Alert' table.
+ * 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. + * + *

+ *

The Sms array lists phone numbers to which Sms alerts are + * to be sent. + *

+ *

The Email array lists email addresses to which email alerts are + * to be sent.
+ * 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 + * defeats batching of messages in that only messages with the + * same subject may be batched in the same email. + *

+ */ +@implementation EcAlerter : NSObject + +/** + * Called to set up or modify the configuration of the alerter.
+ * The dictionary c must contain (keyed on Rules an + * array of dictionaries, each of which provides a rule for + * delivering some form of alert.
+ * 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 +} + +/** + *

This method handles error/alert messages. It is able to handle + * multiple (newline separated messages. + *

+ *

Each message must be a line of the format -
+ * serverName(hostName): YYYY-MM-DD hh:mm:ss.mmm szzzz type - text + *

+ *

Each message is matched against each rule in the Rules + * configuration in turn, and the first match found is used. The + * message is sent to the people listed in the Email and + * Sms entries in the rule (which may be either single + * names or arryas of names). + *

+ */ +- (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 + diff --git a/EcBroadcastProxy.h b/EcBroadcastProxy.h new file mode 100644 index 0000000..b7cd648 --- /dev/null +++ b/EcBroadcastProxy.h @@ -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 + +@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.
+ * 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 */ + + + + diff --git a/EcBroadcastProxy.m b/EcBroadcastProxy.m new file mode 100644 index 0000000..ec69191 --- /dev/null +++ b/EcBroadcastProxy.m @@ -0,0 +1,676 @@ +/* + * Nicola Pero, Brainstorm, October 2000 + * EcBroadcastProxy - an object able to broadcast a message to + * a list of remote objects. + * + */ + +#import + +#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 diff --git a/EcHost.h b/EcHost.h new file mode 100644 index 0000000..1284692 --- /dev/null +++ b/EcHost.h @@ -0,0 +1,79 @@ + +#import + +@class NSDictionary; +@class NSString; + +/** + *

This category provides additional methods to standardise host names so + * that software can consistently refer to a host by a single well known + * name.
+ * 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. + *

+ *

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. + *

+ *

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:
+ * The value of EcHostCurrentName specifies the well known name for the + * current host (the machine on which the software is running).
+ * 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.
+ * 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.
+ * 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. + *

+ */ +@interface NSHost (EcHost) + +/** Returns the well known name of the 'control' host as obtained from the + * NSUserDefaults system.
+ * If EcHostControlName and EcHostControlDomain are both defined, + * the well known name is the string specified by EcHostControlName.
+ * If EcHostControlDomain is defined, the well known name is the string + * specified by it.
+ * If neither is defined, but EcHostCurrentName is defined, then the well + * known name is the string specified by that default.
+ * 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).
+ * 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).
+ * 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.
+ * 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 + diff --git a/EcHost.m b/EcHost.m new file mode 100644 index 0000000..68c0d2b --- /dev/null +++ b/EcHost.m @@ -0,0 +1,285 @@ + +#import +#import +#import +#import +#import +#import +#import + +#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 + diff --git a/EcLogger.h b/EcLogger.h new file mode 100644 index 0000000..52f6952 --- /dev/null +++ b/EcLogger.h @@ -0,0 +1,37 @@ + +#ifndef _ECLOGGER_H +#define _ECLOGGER_H + +@interface EcLogger : NSObject +{ + 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 )from + sequence: (unsigned)num + extra: (NSData*)data; +- (void) cmdMadeConnectionToServer: (NSString*)name; +- (void) cmdPing: (id )from + sequence: (unsigned)num + extra: (NSData*)data; +- (void) flush; +- (void) log: (NSString*)fmt arguments: (va_list)args; +- (void) update; +@end + +#endif + diff --git a/EcLogger.m b/EcLogger.m new file mode 100644 index 0000000..97ccffb --- /dev/null +++ b/EcLogger.m @@ -0,0 +1,514 @@ + +#include +#include + +#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 )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 server; + + server = (id)[EcProc server: name]; + [server registerClient: self name: cmdLogName()]; +} + +/* Should only be called on main thread, but doesn't matter. + */ +- (void) cmdPing: (id )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 server; + + NS_DURING + { + server = (id)[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 server; + + NS_DURING + { + server = (id) + [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 server; + + if (serverName == nil) + { + NS_DURING + { + server = (id)[EcProc cmdNewServer]; + } + NS_HANDLER + { + server = nil; + NSLog(@"Exception contacting Command server: %@\n", + localException); + } + NS_ENDHANDLER + } + else + { + NS_DURING + { + server = (id) + [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 + diff --git a/EcProcess.h b/EcProcess.h new file mode 100644 index 0000000..78825c3 --- /dev/null +++ b/EcProcess.h @@ -0,0 +1,662 @@ +#ifndef INCLUDED_ECPROCESS_H +#define INCLUDED_ECPROCESS_H + +/** + + The EcProcess class +

+ The EcProcess class provides basic configuration, control, logging, + and inter-process connection systems for servers processors. +

+
+ Configuration options +

+ 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. +

+ + startup settings + + EcDebug- + + Any key of the form EcDebug-xxx turns on the xxx debug level + on program startup. + + EcMemory + + This boolean value determines whether statistics on creation + and destruction of objects are maintained.
+ 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. +
+ EcRelease + + 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.
+ 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. +
+ EcTesting + + 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).
+ 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. +
+ EcTransient + + 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. + +
+
+
+
+ + */ + +#import + +#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 )from + sequence: (unsigned)num + extra: (in bycopy NSData*)data; +- (oneway void) cmdPing: (id )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)c; +- (oneway void) updateConfig: (in bycopy NSData*)info; +@end + +/** Messages that the Command server may send to clients. + */ +@protocol CmdClient +- (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 +- (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 +- (oneway void) information: (NSString*)txt; +@end + +/** + * Messages that clients may send to the server. + * NB. The 'registerClient...' message must be sent first. + */ +@protocol Command +- (oneway void) alarm: (in bycopy EcAlarm*)alarm; +- (oneway void) command: (in bycopy NSData*)dat + to: (NSString*)t + from: (NSString*)f; +- (bycopy NSData*) registerClient: (id)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 +- (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)c + name: (NSString*)n; +- (bycopy NSString*) registerConsole: (id)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)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 - + * + * + *

On startup, these are taken from the command line, or from the + * local user defaults database of the person running the program.
+ * 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. + *

+ *

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. + *

+ *

Settings in the network configuration system will have no effect on + * the following defaults which are used BEFORE the network configuration + * can be read. + *

+ * + * EcDaemon + * To specify whether the program should run in the background + * (boolean, YES if the program is to run as a daemon, NO otherwise).
+ * The value in the network configuration has no effect. + *
+ * EcEffectiveUser + * 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.
+ * Set a value of '' or '*' to remain whoever runs the program + * rather than changing. + *
+ * EcInstance + * To set the program instance ID (an arbitrary string).
+ * If this is specified, the program name has a hyphen and the + * id appended to it by the '-initWithDefaults:' method. + *
+ *
+ *

The following settings will be revised after startup to include the + * values from the network configuration system. + *

+ * + * EcAuditLocal + * A boolean used to specify that audit information should + * be logged locally rather than sending it to be logged centrally.
+ * Default value is NO. + *
+ * EcAuditFlush + * A flush interval in seconds (optionally followed by a colon + * and a buffer size in KiloBytes) to control flushing of audit logs.
+ * Setting an interval of zero or less disables flushing by timer.
+ * Setting a size of zero or less, disables buffering (so logs are + * flushed immediately). + *
+ * EcDebug-XXX + * 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! + * + * EcDebugLocal + * A boolean used to specify that debug information should + * be logged locally rather than sending it to be logged centrally.
+ * Default value is YES. + *
+ * EcDebugFlush + * A flush interval in seconds (optionally followed by a colon + * and a buffer size in KiloBytes) to control flushing of debug logs.
+ * Setting an interval of zero or less disables flushing by timer.
+ * Setting a size of zero or less, disables buffering (so logs are + * flushed immediately). + *
+ * EcMemory + * A boolean used to ensure that monitoring of memory allocation is + * turned on or turned off. + * + * EcRelease + * A boolean used to specify whether the program should perform + * sanity checks for retain/release combinations. Slows things down a lot! + * + * EcTesting + * A boolean used to specify whether the program is running + * in test mode or not. + * + * EcWellKnownHostNames + * A dictionary mapping host names/address values to well known + * names (the canonical values used by Command and Control). + * + *
+ */ +@interface EcProcess : NSObject + +/** 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).
+ * 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.
+ * 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.
+ * If there is already a handle for the specified file, this method + * returns the existing handle rather than creating a new one.
+ * 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.
+ * 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.
+ * 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).
+ * 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).
+ * 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).
+ * 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
+ * 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.
+ * The argument 'when' is the timestamp of the timeout. + */ +- (void) prcNewDay: (NSCalendarDate*)when; + +/** Called on the first timeout of a new hour.
+ * The argument 'when' is the timestamp of the timeout. + */ +- (void) prcNewHour: (NSCalendarDate*)when; + +/** Called on the first timeout of a new minute.
+ * 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.
+ * Returns zero when the run loop completes.
+ * Returns one (immediately) if the receiver is transent.
+ * 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 */ diff --git a/EcProcess.m b/EcProcess.m new file mode 100644 index 0000000..ebb2ddc --- /dev/null +++ b/EcProcess.m @@ -0,0 +1,3594 @@ + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import + + +#import "EcProcess.h" +#import "EcLogger.h" +#import "EcAlarm.h" +#import "EcAlarmDestination.h" +#import "EcHost.h" +#import "EcUserDefaults.h" +#import "EcBroadcastProxy.h" + +#include "malloc.h" + +#include "config.h" + +#ifdef HAVE_SYS_SIGNAL_H +#include +#endif +#ifdef HAVE_SYS_FILE_H +#include +#endif +#ifdef HAVE_SYS_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif + + +#if !defined(EC_DEFAULTS_PREFIX) +#define EC_DEFAULTS_PREFIX nil +#endif +#if !defined(EC_DEFAULTS_STRICT) +#define EC_DEFAULTS_STRICT NO +#endif +#if !defined(EC_EFFECTIVE_USER) +#define EC_EFFECTIVE_USER @"ecuser" +#endif + +/* Lock for controlling access to per-process singleton instance. + */ +static NSRecursiveLock *prcLock = nil; + +static BOOL cmdFlagDaemon = NO; +static BOOL cmdFlagTesting = NO; +static BOOL cmdIsInitialised = NO; +static BOOL cmdIsQuitting = NO; +static NSString *cmdInst = nil; +static NSString *cmdName = nil; +static NSString *cmdUser = nil; +static NSUserDefaults *cmdDefs = nil; +static NSString *cmdDebugName = nil; +static NSMutableDictionary *cmdLogMap = nil; + +static NSDate *started = nil; /* Time object was created. */ +static NSDate *lastIP = nil; /* Time of last input to object. */ +static NSDate *lastOP = nil; /* Time of last output by object. */ + +static Class cDateClass = 0; +static Class dateClass = 0; +static Class stringClass = 0; +static int signalled = 0; + +static RETSIGTYPE +ihandler(int sig) +{ + static BOOL beenHere = NO; + + signal(sig, ihandler); + if (NO == beenHere) + { + beenHere = YES; + signal(SIGABRT, SIG_DFL); + abort(); + } + exit(sig); +#if RETSIGTYPE != void + return 0; +#endif +} + +static RETSIGTYPE +qhandler(int sig) +{ + signal(sig, ihandler); + /* We store the signal value in a global variable and return to normal + * processing ... that way later code can check on the sttate of the + * variable and take action outside the handler. + * We can't act immediately here inside the handler as the signal may + * have interrupted some vital library (eg malloc()) and left it in a + * state such that our code can't continue. For instance if we try to + * cleanup after a signal and call free(), the process may hang waiting + * for a lock that the interupted malloc() functioin still holds. + */ + if (0 == signalled) + { + signalled = sig; + } + else + { + static BOOL beenHere = NO; + + if (NO == beenHere) + { + beenHere = YES; + signal(SIGABRT, SIG_DFL); + abort(); + } + exit(sig); + } +#if RETSIGTYPE != void + return 0; +#endif +} + +int +cmdSignalled() +{ + return signalled; +} + +NSString* +cmdVersion(NSString *ver) +{ + static NSString *version = @"1997-1999"; + + if (ver != nil) + { + ASSIGNCOPY(version, ver); + } + return version; +} + +static NSString *homeDir = nil; + +NSString* +cmdHomeDir() +{ + return homeDir; +} + +void +cmdSetHome(NSString *home) +{ + ASSIGNCOPY(homeDir, home); +} + +static NSString *userDir = nil; + +static NSString* +cmdUserDir() +{ + if (userDir == nil) + return NSHomeDirectoryForUser(cmdUser); + else + return userDir; +} + +static NSString* +cmdSetUserDirectory(NSString *dir) +{ + if (dir == nil) + { + dir = NSHomeDirectoryForUser(cmdUser); + } + else if ([dir isAbsolutePath] == NO) + { + dir = [NSHomeDirectoryForUser(cmdUser) + stringByAppendingPathComponent: dir]; + } + ASSIGNCOPY(userDir, dir); + return userDir; +} + +static NSString *dataDir = nil; + +/* + * Return the current logging directory - if 'today' is not nil, treat it as + * the name of a subdirectory in which todays logs should be archived. + * Create the directory path if necessary. + */ +NSString* +cmdLogsDir(NSString *date) +{ + NSFileManager *mgr = [NSFileManager defaultManager]; + NSString *str = cmdUserDir(); + BOOL flag; + + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + if ([mgr createDirectoryAtPath: str attributes: nil] == NO) + { + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + NSLog(@"Unable to create directory - %@", str); + return nil; + } + } + else + { + flag = YES; + } + } + if (flag == NO) + { + NSLog(@"The path '%@' is not a directory", str); + return nil; + } + + str = [str stringByAppendingPathComponent: @"Logs"]; + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + if ([mgr createDirectoryAtPath: str attributes: nil] == NO) + { + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + NSLog(@"Unable to create directory - %@", str); + return nil; + } + } + else + { + flag = YES; + } + } + if (flag == NO) + { + NSLog(@"The path '%@' is not a directory", str); + return nil; + } + + if (date != nil) + { + str = [str stringByAppendingPathComponent: date]; + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + if ([mgr createDirectoryAtPath: str attributes: nil] == NO) + { + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + NSLog(@"Unable to create directory - %@", str); + return nil; + } + } + else + { + flag = YES; + } + } + if (flag == NO) + { + NSLog(@"The path '%@' is not a directory", str); + return nil; + } + } + + if (homeDir != nil) + { + str = [str stringByAppendingPathComponent: homeDir]; + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + if ([mgr createDirectoryAtPath: str attributes: nil] == NO) + { + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + NSLog(@"Unable to create directory - %@", str); + return nil; + } + } + else + { + flag = YES; + } + } + if (flag == NO) + { + NSLog(@"The path '%@' is not a directory", str); + return nil; + } + } + + return str; +} + +NSString* +cmdLogKey(EcLogType t) +{ + switch (t) + { + case LT_DEBUG: return @"Debug"; + case LT_WARNING: return @"Warn"; + case LT_ERROR: return @"Error"; + case LT_AUDIT: return @"Audit"; + case LT_ALERT: return @"Alert"; + default: return @"UnknownLogType"; + } +} + +NSString* +cmdLogName() +{ + static NSString *n = nil; + + if (n == nil) + { + n = [EcProc cmdName]; + if (n == nil) + { + n = [[NSProcessInfo processInfo] processName]; + } + n = [n copy]; + } + return n; +} + +NSString* +cmdLogFormat(EcLogType t, NSString *fmt) +{ + static NSString *h = nil; + static NSDictionary *l = nil; + NSCalendarDate *c = [[cDateClass alloc] init]; + NSString *f = cmdLogKey(t); + NSString *n = cmdLogName(); + NSString *d; + NSString *result; + + if (h == nil) + { + h = [[[NSHost currentHost] name] copy]; + } + if (l == nil) + { + l = [[cmdDefs dictionaryRepresentation] copy]; + } + d = [c descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z" locale: l]; + result = [stringClass stringWithFormat: @"%@(%@): %@ %@ - %@\n", + n, h, d, f, fmt]; + RELEASE(c); + return result; +} + + + + + + + + + +EcProcess *EcProc = nil; + +static EcAlarmDestination *alarmDestination = nil; + +static EcLogger *alertLogger = nil; +static EcLogger *auditLogger = nil; +static EcLogger *debugLogger = nil; +static EcLogger *errorLogger = nil; +static EcLogger *warningLogger = nil; + +static NSMutableSet *cmdActions = nil; +static id cmdServer = nil; +static id cmdPTimer = nil; +static NSMutableDictionary *cmdConf = nil; +static NSDictionary *cmdOperators = nil; +static NSDate *cmdFirst = nil; +static NSDate *cmdLast = nil; +static BOOL cmdIsTransient = NO; +static NSMutableSet *cmdDebugModes = nil; +static NSMutableDictionary *cmdDebugKnown = nil; +static NSMutableString *replyBuffer = nil; +static SEL cmdTimSelector = 0; +static NSTimeInterval cmdTimInterval = 60.0; + +static NSMutableArray *noNetConfig = nil; + +static NSMutableArray *updateHandlers = nil; + +static NSMutableDictionary *servers = nil; + +static NSString *hostName = nil; + +#define DEFMEMALLOWED 50 +static int memAllowed = DEFMEMALLOWED; +static int memLast = 0; +static int memPeak = 0; +static int memSlot = 0; +static int memRoll[10]; +#define memSize (sizeof(memRoll)/sizeof(*memRoll)) + + +static NSString* +findAction(NSString *cmd) +{ + cmd = [cmd lowercaseString]; + if (NO == [cmdActions containsObject: cmd]) + { + NSEnumerator *enumerator; + NSString *name; + NSString *found; + + found = nil; + enumerator = [cmdActions objectEnumerator]; + while (nil != (name = [enumerator nextObject])) + { + if (YES == [name hasPrefix: cmd]) + { + if (nil == found) + { + found = name; + } + else + { + return nil; // Ambiguous + } + } + } + cmd = found; + } + return cmd; +} + +static NSString* +prcCommandHost() +{ + NSString *host; + + host = [cmdDefs stringForKey: @"CommandHost"]; + if (nil == host) + { + host = @""; /* Local host */ + } + return host; +} + +static NSString* +prcCommandName() +{ + NSString *name; + + name = [cmdDefs stringForKey: @"CommandName"]; + if (nil == name) + { + name = @"Command"; + } + return name; +} + + +@interface UpdateHandler: NSObject +{ + id obj; + SEL sel; +} +- (id) initWithObj: (id)o andSel: (SEL)s; +- (id) obj; +- (SEL) sel; +- (void) setSel: (SEL)s; +@end + +@implementation UpdateHandler +- (void) dealloc +{ + RELEASE(obj); + [super dealloc]; +} +- (id) initWithObj: (id)o andSel: (SEL)s; +{ + self = [super init]; + if (self != nil) + { + obj = RETAIN(o); + sel = s; + } + return self; +} +- (id) obj +{ + return obj; +} +- (SEL) sel +{ + return sel; +} +- (void) setSel: (SEL)s +{ + sel = s; +} +@end + +NSString *cmdDefaultDbg = @"defaultMode"; +NSString *cmdConnectDbg = @"connectMode"; +NSString *cmdDetailDbg = @"detailMode"; + + +static int comp_len = 0; + +static int +comp(const char* s0, const char* s1) +{ + comp_len = 0; + if (s0 == 0) { + s0 = ""; + } + if (s1 == 0) { + s1 = ""; + } + while (*s0) { + if (*s0 != *s1) { + char c0 = islower(*s0) ? toupper(*s0) : *s0; + char c1 = islower(*s1) ? toupper(*s1) : *s1; + + if (c0 != c1) { + if (c0 != '\0') { + comp_len = -1; /* s0 is not a substring of s1. */ + } + return(-1); + } + } + comp_len++; + s0++; + s1++; + } + if (*s0 != *s1) { + return(-1); + } + return(0); +} + +static NSString* +findMode(NSDictionary* d, NSString* s) +{ + NSArray *a = [d allKeys]; + NSString *o; + unsigned int i; + const char *s0 = [s UTF8String]; + const char *s1; + int best_pos = -1; + int best_len = 0; + + for (i = 0; i < [a count]; i++) + { + o = (NSString*)[a objectAtIndex: i]; + s1 = [o UTF8String]; + if (comp(s0, s1) == 0) + { + return o; + } + if (comp_len > best_len) + { + best_len = comp_len; + best_pos = i; + } + } + if (best_pos >= 0) + { + return (NSString*)[a objectAtIndex: best_pos]; + } + return nil; +} + +/* + * Auxiliary object representing a remote server a subclass might need + * to connect to. This class is for EcProcess.m internal use. + */ +@interface RemoteServer : NSObject +{ + /* This is the string which identifies this server */ + NSString *defaultName; + /* These are the actual name and host for this server, as obtained + by configuration for the `defaultName' server */ + NSString *name; + NSString *host; + + /* The same for multiple servers */ + NSArray *multiple; + + /* The real object representing the remote server. */ + id proxy; + /* An object responding to cmdMadeConnectionToServer: and/or + cmdLostConnectionToServer: */ + id delegate; + +} +/* Initialize the object - string is the default server name */ +- (id) initWithDefaultName: (NSString *)string + delegate: (id)object; +- (NSString *) defaultName; +- (void) setName: (NSString *)string; +- (NSString *) name; +- (void) setHost: (NSString *)string; +- (void) setMultiple: (NSArray*)config; +- (NSArray*) multiple; +/* + * Return a proxy to the remote server; create one if needed by making + * a connection, using name and host. + * If the server is multiple, create a EcBroadcastProxy object, and returns + * that object. + */ +- (id) proxy; +/* + * Internal connection management methods + */ +- (id) connectionBecameInvalid: (NSNotification*)notification; +- (BOOL) connection: (NSConnection*)ancestor + shouldMakeNewConnection: (NSConnection*)newConn; +- (void) BCP: (EcBroadcastProxy *)proxy lostConnectionToServer: (NSString *)name + host: (NSString *)host; +- (void) BCP: (EcBroadcastProxy *)proxy madeConnectionToServer: (NSString *)name + host: (NSString *)host; +/* + * Returns YES if the connection is ALIVE, NO if the connection is DEAD + */ +- (BOOL) isConnected; +- (NSString *)description; +- (void) update; +@end + +@implementation RemoteServer + +- (id) initWithDefaultName: (NSString *)string + delegate: (id)object +{ + self = [super init]; + if (self != nil) + { + ASSIGNCOPY(defaultName, string); + ASSIGN(name, defaultName); + host = @"*"; + multiple = nil; + proxy = nil; + delegate = object; + /* + * Grab configuration information. + */ + [self update]; + } + return self; +} + +- (void) dealloc +{ + DESTROY(defaultName); + DESTROY(name); + DESTROY(host); + DESTROY(multiple); + DESTROY(proxy); + [[NSNotificationCenter defaultCenter] removeObserver: self]; + [super dealloc]; +} + +- (NSString *) defaultName +{ + return defaultName; +} + +- (void) setName: (NSString *)string +{ + if ([name isEqual: string] == NO) + { + ASSIGNCOPY(name, string); + DESTROY(proxy); + } +} + +- (NSString *) name +{ + return name; +} + +- (void) setHost: (NSString *)string +{ + if ([host isEqual: string] == NO) + { + ASSIGNCOPY(host, string); + DESTROY(proxy); + } +} + +- (NSString *) host +{ + return host; +} + +- (void) setMultiple: (NSArray *)config +{ + if ([multiple isEqual: config] == NO) + { + ASSIGNCOPY(multiple, config); + DESTROY(proxy); + } +} + +- (NSArray*) multiple +{ + return multiple; +} + +- (id) proxy +{ + if (nil == proxy) + { + if (nil == multiple) + { + [EcProc cmdDbg: cmdConnectDbg + msg: @"Looking for service %@ on host %@", name, host]; + proxy = [NSConnection rootProxyForConnectionWithRegisteredName: name + host: host + usingNameServer: [NSSocketPortNameServer sharedInstance]]; + if (proxy != nil) + { + id connection = [proxy connectionForProxy]; + + RETAIN (proxy); + [connection setDelegate: self]; + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(connectionBecameInvalid:) + name: NSConnectionDidDieNotification + object: connection]; + if ([delegate respondsToSelector: + @selector(cmdMadeConnectionToServer:)] == YES) + { + [delegate cmdMadeConnectionToServer: defaultName]; + } + [EcProc cmdDbg: cmdConnectDbg + msg: @"Connected to %@ server on host %@", + name, host]; + } + else + { + [EcProc cmdDbg: cmdConnectDbg + msg: @"Failed to contact %@ server on host %@", + name, host]; + } + } + else /* a multiple server */ + { + proxy = [[EcBroadcastProxy alloc] initWithReceivers: multiple]; + [proxy BCPsetDelegate: self]; + } + } + return proxy; +} + +- (id) connectionBecameInvalid: (NSNotification*)notification +{ + id connection = [notification object]; + + [[NSNotificationCenter defaultCenter] + removeObserver: self + name: NSConnectionDidDieNotification + object: connection]; + + if ([connection isKindOfClass: [NSConnection class]]) + { + if (connection == [proxy connectionForProxy]) + { + [EcProc cmdDbg: cmdConnectDbg + msg: @"lost connection - clearing %@.", + name]; + if ([delegate respondsToSelector: + @selector(cmdLostConnectionToServer:)] == YES) + { + [delegate cmdLostConnectionToServer: defaultName]; + } + RELEASE (proxy); + proxy = nil; + } + } + else + { + [self error: "non-Connection sent invalidation"]; + } + return self; +} + +/* Debugging purposes only */ +- (BOOL) connection: (NSConnection*)ancestor + shouldMakeNewConnection: (NSConnection*)newConn +{ + [EcProc cmdDbg: cmdConnectDbg + msg: @"New connection 0x%p created", newConn]; + return YES; +} + +- (BOOL) isConnected +{ + if (proxy != nil) + { + return YES; + } + else + { + return NO; + } +} + +- (NSString*) description +{ + if (multiple == nil) + { + NSString *status; + + if (proxy != nil) + { + status = @"LIVE"; + } + else + { + status = @"DEAD"; + } + + return [NSString stringWithFormat: + @"Connection to server `%@' on host `%@' is %@", + name, host, status]; + } + else /* multiple server */ + { + if (proxy == nil) + { + return [NSString stringWithFormat: + @"Multiple connection to servers %@\n" + @" has not yet been initialized", + multiple]; + } + else + { + return [proxy BCPstatus]; + } + } +} + +- (void) BCP: (EcBroadcastProxy*)proxy + lostConnectionToServer: (NSString*)name + host: (NSString*)host +{ + if ([delegate respondsToSelector: + @selector(cmdLostConnectionToServer:)] == YES) + { + /* FIXME: How do we inform delegate of this ? Is it of any use ? */ + // [delegate cmdLostConnectionToServer: defaultName]; + } +} + +- (void) BCP: (EcBroadcastProxy*)proxy + madeConnectionToServer: (NSString*)name + host: (NSString*)host +{ + if ([delegate respondsToSelector: + @selector(cmdLostConnectionToServer:)] == YES) + { + /* FIXME: How do we inform delegate of this ? Is it of any use ? */ + //[delegate cmdMadConnectionToServer: defaultName]; + } +} + +- (void) update +{ + NSString *configKey; + id configValue; + + configKey = [defaultName stringByAppendingString: @"Name"]; + configValue = [cmdDefs stringForKey: configKey]; + if (nil != configValue) + { + [self setName: configValue]; + } + + configKey = [defaultName stringByAppendingString: @"Host"]; + configValue = [cmdDefs stringForKey: configKey]; + if (nil != configValue) + { + [self setHost: configValue]; + } + + configKey = [defaultName stringByAppendingString: @"BroadCast"]; + configValue = [cmdDefs arrayForKey: configKey]; + if (nil != configValue) + { + [self setMultiple: configValue]; + } +} + +@end + +@interface EcProcess (Private) +- (void) cmdMesgrelease: (NSArray*)msg; +- (void) cmdMesgtesting: (NSArray*)msg; +- (NSString*) _moveLog: (NSString*)name to: (NSString*)sub; +- (void) _timedOut: (NSTimer*)timer; +@end + +@implementation EcProcess + +static NSString *noFiles = @"No log files to archive"; + +- (id) cmdConfig: (NSString*)key +{ + return [cmdDefs objectForKey: key]; +} + +- (NSUserDefaults*) cmdDefaults +{ + return cmdDefs; +} + +- (void) cmdDefaultsChanged: (NSNotification*)n +{ + NSEnumerator *enumerator = [cmdDebugKnown keyEnumerator]; + NSDictionary *dict; + NSString *mode; + + while (nil != (mode = [enumerator nextObject])) + { + NSString *key = [@"Debug-" stringByAppendingString: mode]; + + if (YES == [cmdDefs boolForKey: key]) + { + [cmdDebugModes addObject: mode]; + } + else + { + [cmdDebugModes removeObject: mode]; + } + } + + dict = [cmdDefs dictionaryForKey: @"WellKnownHostNames"]; + if (nil != dict) + { + [NSHost setWellKnownNames: dict]; + } + + GSDebugAllocationActive([cmdDefs boolForKey: @"Memory"]); + [NSObject enableDoubleReleaseCheck: [cmdDefs boolForKey: @"Release"]]; + cmdFlagTesting = [cmdDefs boolForKey: @"Testing"]; +} + +- (NSString*) cmdDebugPath +{ + if (cmdDebugName == nil) + return nil; + return [cmdLogsDir(nil) stringByAppendingPathComponent: cmdDebugName]; +} + +- (NSString*) cmdInstance +{ + return cmdInst; +} + +- (BOOL) cmdIsDaemon +{ + return cmdFlagDaemon; +} + +- (BOOL) cmdIsTesting +{ + return cmdFlagTesting; +} + +- (NSDate*) cmdLastIP +{ + return lastIP; +} + +- (NSDate*) cmdLastOP +{ + return lastOP; +} + +- (void) cmdLogEnd: (NSString*)name +{ + NSFileHandle *hdl; + + if ([name length] == 0) + { + NSLog(@"Attempt to end log with empty filename"); + return; + } + name = [name lastPathComponent]; + hdl = [cmdLogMap objectForKey: name]; + if (hdl != nil) + { + NSString *path; + NSDictionary *attr; + NSFileManager *mgr; + + /* + * Ensure that all data is written to file. + */ + fflush(stderr); + [hdl closeFile]; + + /* + * If the file is empty, remove it, otherwise move to archive directory. + */ + path = [cmdLogsDir(nil) stringByAppendingPathComponent: name]; + mgr = [NSFileManager defaultManager]; + attr = [mgr fileAttributesAtPath: path traverseLink: NO]; + if ([[attr objectForKey: NSFileSize] intValue] == 0) + { + [mgr removeFileAtPath: path handler: nil]; + } + else + { + NSDate *when; + NSString *where; + + when = [NSDate date]; + where = [when descriptionWithCalendarFormat: @"%Y-%m-%d" + timeZone: nil locale: nil]; + if (where != nil) + { + [self _moveLog: name to: where]; + } + } + + /* + * Unregister filename. + */ + [cmdLogMap removeObjectForKey: name]; + } +} + +- (NSFileHandle*) cmdLogFile: (NSString*)name +{ + NSFileHandle *hdl; + NSString *status = nil; + + if ([name length] == 0) + { + NSLog(@"Attempt to log with empty filename"); + return nil; + } + name = [name lastPathComponent]; + hdl = [cmdLogMap objectForKey: name]; + if (hdl == nil) + { + NSFileManager *mgr = [NSFileManager defaultManager]; + NSString *path; + + path = [cmdLogsDir(nil) stringByAppendingPathComponent: name]; + + if ([mgr fileExistsAtPath: path] == YES) + { + NSDictionary *attr; + NSDate *when; + NSString *where; + + attr = [mgr fileAttributesAtPath: path traverseLink: NO]; + when = [attr objectForKey: NSFileModificationDate]; + where = [when descriptionWithCalendarFormat: @"%Y-%m-%d" + timeZone: nil locale: nil]; + if (where != nil) + { + status = [self _moveLog: name to: where]; + } + } + + /* + * Create the file if necessary, and open it for updating. + */ + if ([mgr isWritableFileAtPath: path] == NO + && [mgr createFileAtPath: path contents: nil attributes: nil] == NO) + { + NSLog(@"File '%@' is not writable and can't be created", path); + } + else + { + hdl = [NSFileHandle fileHandleForUpdatingAtPath: path]; + if (hdl == nil) + { + if (status != nil) + { + NSLog(@"%@", status); + } + NSLog(@"Unable to log to %@", path); + } + else + { + [hdl seekToEndOfFile]; + } + } + if (hdl == nil) + { + return nil; + } + /* + * As a special case, if this is the default debug file + * we must set it up to write to stderr. + */ + if ([name isEqual: cmdDebugName] == YES) + { + int desc; + + desc = [hdl fileDescriptor]; + if (desc != 2) + { + dup2(desc, 2); + [hdl closeFile]; + hdl = [NSFileHandle fileHandleWithStandardError]; + } + } + /* + * Store the file handle in the dictionary for later use. + */ + [cmdLogMap setObject: hdl forKey: name]; + if (status != nil) + { + NSLog(@"%@", status); + } + } + return hdl; +} + +- (NSString*) cmdName +{ + return cmdName; +} + +- (NSDate*) cmdStarted +{ + return started; +} + +- (NSString*) cmdDataDirectory +{ + if (dataDir == nil) + { + NSFileManager *mgr = [NSFileManager defaultManager]; + NSString *str = cmdUserDir(); + BOOL flag; + + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + if ([mgr createDirectoryAtPath: str attributes: nil] == NO) + { + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + NSLog(@"Unable to create directory - %@", str); + return nil; + } + } + else + { + flag = YES; + } + } + if (flag == NO) + { + NSLog(@"The path '%@' is not a directory", str); + return nil; + } + + str = [str stringByAppendingPathComponent: @"Data"]; + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + if ([mgr createDirectoryAtPath: str attributes: nil] == NO) + { + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + NSLog(@"Unable to create directory - %@", str); + return nil; + } + } + else + { + flag = YES; + } + } + if (flag == NO) + { + NSLog(@"The path '%@' is not a directory", str); + return nil; + } + + if (homeDir != nil) + { + str = [str stringByAppendingPathComponent: homeDir]; + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + if ([mgr createDirectoryAtPath: str attributes: nil] == NO) + { + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + NSLog(@"Unable to create directory - %@", str); + return nil; + } + } + else + { + flag = YES; + } + } + if (flag == NO) + { + NSLog(@"The path '%@' is not a directory", str); + return nil; + } + } + + ASSIGNCOPY(dataDir, str); + } + return dataDir; +} + +- (oneway void) alarm: (in bycopy EcAlarm*)event +{ + [alarmDestination alarm: event]; +} + +- (oneway void) domanage: (in bycopy NSString*)managedObject +{ + [alarmDestination domanage: managedObject]; +} + +- (oneway void) unmanage: (in bycopy NSString*)managedObject +{ + [alarmDestination unmanage: managedObject]; +} + +- (void) setCmdInterval: (NSTimeInterval)interval +{ + if (interval > 60.0) + { + interval = 60.0; + } + if (interval < 0.001) + { +NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); + interval = 10.0; + } + if (interval < cmdTimInterval) + { + if (cmdTimInterval - interval < 0.001) + { + interval = cmdTimInterval; + } + } + else if (interval - cmdTimInterval < 0.001) + { + interval = cmdTimInterval; + } + if (interval != cmdTimInterval) + { + cmdTimInterval = interval; + [self triggerCmdTimeout]; + } +} + +- (NSString*) prcCopyright +{ + return @""; +} + ++ (void) initialize +{ + if (nil == prcLock) + { + prcLock = [NSRecursiveLock new]; + dateClass = [NSDate class]; + cDateClass = [NSCalendarDate class]; + stringClass = [NSString class]; + cmdLogMap = [[NSMutableDictionary alloc] initWithCapacity: 4]; + + hostName = RETAIN([[NSHost currentHost] name]); + + cmdDebugModes = [[NSMutableSet alloc] initWithCapacity: 4]; + cmdDebugKnown = [[NSMutableDictionary alloc] initWithCapacity: 4]; + + [cmdDebugKnown setObject: @"Mode for distributed object connections" + forKey: cmdConnectDbg]; + [cmdDebugKnown setObject: @"Standard mode for basic debug information" + forKey: cmdDefaultDbg]; + [cmdDebugKnown setObject: @"Detailed but general purpose debugging" + forKey: cmdDetailDbg]; + + [cmdDebugModes addObject: cmdDefaultDbg]; + + /* + * Set the timeouts for the default connection so that + * they will be inherited by other connections. + * A two minute timeout is long enough for almost all + * circumstances. + */ + [[NSConnection defaultConnection] setRequestTimeout: 120.0]; + [[NSConnection defaultConnection] setReplyTimeout: 120.0]; + } +} + +- (void) addServerToList: (NSString *)serverName +{ + [self addServerToList: serverName for: nil]; +} + +- (void) addServerToList: (NSString *)serverName for: (id)anObject +{ + RemoteServer *remote; + + if ((serverName == nil) + || ([serverName isKindOfClass: [NSString class]] == NO)) + { + NSLog (@"Warning: invalid string passed to addServerToList:for:"); + return; + } + + if (anObject == nil) + { + anObject = self; + } + + if (servers == nil) + { + servers = [[NSMutableDictionary alloc] initWithCapacity: 2]; + } + + remote = [[RemoteServer alloc] initWithDefaultName: serverName + delegate: anObject]; + [servers setObject: remote forKey: serverName]; + [remote release]; +} + +- (void) removeServerFromList: (NSString *)serverName +{ + if ((serverName == nil) + || ([serverName isKindOfClass: [NSString class]] == NO)) + { + NSLog (@"Warning: invalid array passed to removeServerFromList:"); + return; + } + [servers removeObjectForKey: serverName]; +} + +- (id) cmdConnectionBecameInvalid: (NSNotification*)notification +{ + id connection; + + connection = [notification object]; + [[NSNotificationCenter defaultCenter] + removeObserver: self + name: NSConnectionDidDieNotification + object: connection]; + if (cmdServer != nil && connection == [cmdServer connectionForProxy]) + { + [alarmDestination setDestination: nil]; + DESTROY(cmdServer); + NSLog(@"lost connection 0x%p to command server\n", connection); + /* + * Cause timeout to go off really soon so we will try to + * re-establish the link to the server. + */ + if (cmdPTimer != nil) + { + [cmdPTimer invalidate]; + cmdPTimer = nil; + } + cmdPTimer = [NSTimer scheduledTimerWithTimeInterval: 0.1 + target: self + selector: @selector(_timedOut:) + userInfo: nil + repeats: NO]; + } + else + { + NSLog(@"unknown-Connection sent invalidation\n"); + } + return self; +} + +- (void) cmdAlert: (NSString*)fmt, ... +{ + va_list ap; + + if (nil == alertLogger) + { + alertLogger = [[EcLogger loggerForType: LT_ALERT] retain]; + } + va_start (ap, fmt); + [alertLogger log: fmt arguments: ap]; + va_end (ap); +} + +- (NSString*) cmdArchive: (NSString*)subdir +{ + NSString *status = @""; + + if ([cmdLogMap count] == 0) + { + status = noFiles; + } + else + { + NSEnumerator *enumerator = [[cmdLogMap allKeys] objectEnumerator]; + NSString *name; + + if (subdir == nil) + { + NSCalendarDate *when = [NSCalendarDate date]; + int y, m, d; + + y = [when yearOfCommonEra]; + m = [when monthOfYear]; + d = [when dayOfMonth]; + + subdir = [stringClass stringWithFormat: @"%04d-%02d-%02d", y, m, d]; + } + + while ((name = [enumerator nextObject]) != nil) + { + NSString *s; + + s = [self _moveLog: name to: subdir]; + if ([status length] > 0) + status = [status stringByAppendingString: @"\n"]; + status = [status stringByAppendingString: s]; + [self cmdLogEnd: name]; + if (cmdIsQuitting == NO) + { + [self cmdLogFile: name]; + } + } + } + return status; +} + +- (void) cmdAudit: (NSString*)fmt, ... +{ + va_list ap; + + if (nil == auditLogger) + { + auditLogger = [[EcLogger loggerForType: LT_AUDIT] retain]; + } + va_start (ap, fmt); + [auditLogger log: fmt arguments: ap]; + va_end (ap); +} + +- (void) cmdDbg: (NSString*)type msg: (NSString*)fmt, ... +{ + if (nil != [cmdDebugModes member: type]) + { + va_list ap; + + if (nil == debugLogger) + { + debugLogger = [[EcLogger loggerForType: LT_DEBUG] retain]; + } + va_start (ap, fmt); + [debugLogger log: fmt arguments: ap]; + va_end (ap); + } +} + +- (void) cmdDebug: (NSString*)fmt, ... +{ + if (nil != [cmdDebugModes member: cmdDefaultDbg]) + { + va_list ap; + + if (nil == debugLogger) + { + debugLogger = [[EcLogger loggerForType: LT_DEBUG] retain]; + } + va_start (ap, fmt); + [debugLogger log: fmt arguments: ap]; + va_end (ap); + } +} + +- (void) cmdError: (NSString*)fmt, ... +{ + va_list ap; + + if (nil == errorLogger) + { + errorLogger = [[EcLogger loggerForType: LT_ERROR] retain]; + } + va_start (ap, fmt); + [errorLogger log: fmt arguments: ap]; + va_end (ap); +} + + +- (void) cmdFlushLogs +{ + [alertLogger flush]; + [auditLogger flush]; + [debugLogger flush]; + [errorLogger flush]; + [warningLogger flush]; +} + +- (NSTimeInterval) cmdInterval +{ + return cmdTimInterval; +} + +- (BOOL) cmdIsClient +{ + return YES; +} + +- (void) log: (NSString*)message type: (EcLogType)t +{ + switch (t) + { + case LT_DEBUG: + [self cmdDebug: @"%@", message]; + break; + case LT_WARNING: + [self cmdWarn: @"%@", message]; + break; + case LT_ERROR: + [self cmdError: @"%@", message]; + break; + case LT_ALERT: + [self cmdAlert: @"%@", message]; + break; + case LT_AUDIT: + [self cmdAudit: @"%@", message]; + break; + default: + [self cmdError: @"%@", message]; + break; + } +} + +- (NSMutableDictionary*) cmdOperator: (NSString*)name password: (NSString*)pass +{ + NSMutableDictionary *d = (NSMutableDictionary*)cmdOperators; + + if (d == nil || [d isKindOfClass: [NSDictionary class]] == NO) + { + return nil; + } + d = [d objectForKey: name]; + if (d == nil || [d isKindOfClass: [NSDictionary class]] == NO) + { + return nil; + } + d = [d mutableCopy]; + if (pass != nil && [[d objectForKey: @"Password"] isEqual: pass] == YES) + { + [d setObject: @"yes" forKey: @"Password"]; + } + else + { + [d setObject: @"no" forKey: @"Password"]; + } + return AUTORELEASE(d); +} + +- (id) cmdNewServer +{ + static BOOL connecting = NO; + + if (NO == connecting) + { + /* + * Use the 'cmdLast' variable to ensure that we don't try to + * check memory usage or connect to the command server more + * than once every 10 sec. + */ + if (cmdLast == nil || [cmdLast timeIntervalSinceNow] < -10.0) + { + connecting = YES; + + ASSIGN(cmdLast, [dateClass date]); + if (cmdFirst == nil) + { + ASSIGN(cmdFirst, cmdLast); + } + + if (cmdServer == nil && YES == [self cmdIsClient]) + { + NSString *name = nil; + NSString *host = nil; + id proxy; + + NS_DURING + { + host = prcCommandHost(); + name = prcCommandName(); + + proxy = [NSConnection + rootProxyForConnectionWithRegisteredName: name + host: host + usingNameServer: [NSSocketPortNameServer sharedInstance]]; + } + NS_HANDLER + { + proxy = nil; + NSLog(@"Exception connecting to Command server %@ on %@): %@", + name, host, localException); + } + NS_ENDHANDLER + + if (proxy != nil) + { + NSMutableDictionary *r = nil; + + [proxy setProtocolForProxy: @protocol(Command)]; + + NS_DURING + { + NSData *d; + + d = [proxy registerClient: self + name: cmdLogName() + transient: cmdIsTransient]; + r = [NSPropertyListSerialization + propertyListWithData: d + options: NSPropertyListMutableContainers + format: 0 + error: 0]; + } + NS_HANDLER + { + r = [NSMutableDictionary dictionaryWithCapacity: 1]; + [r setObject: [localException reason] + forKey: @"rejected"]; + NSLog(@"Caught exception registering with Command: %@", + localException); + } + NS_ENDHANDLER + + /* We could be rejected or told to back off, + * otherwise we continue as normal. + */ + if (r != nil && [r objectForKey: @"rejected"] != nil) + { + NSLog(@"Rejected by Command - %@", + [r objectForKey: @"rejected"]); + [self cmdQuit: 0]; /* Rejected by server. */ + } + else if (nil == r || nil == [r objectForKey: @"back-off"]) + { + NSConnection *connection; + + cmdServer = [proxy retain]; + connection = [cmdServer connectionForProxy]; + [connection enableMultipleThreads]; + if (nil == alarmDestination) + { + alarmDestination = [EcAlarmDestination new]; + } + [alarmDestination setDestination: cmdServer]; + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(cmdConnectionBecameInvalid:) + name: NSConnectionDidDieNotification + object: connection]; + [self cmdUpdate: r]; + } + } + } + connecting = NO; + } + else if (cmdServer == nil && YES == [self cmdIsClient]) + { + NSLog(@"Unable to connect to Command server ... not retry time yet"); + } + } + + return cmdServer; +} + +- (void) cmdUnregister +{ + if (nil != cmdServer) + { + NS_DURING + { + [cmdServer unregisterByObject: self]; + } + NS_HANDLER + { + DESTROY(cmdServer); + NSLog(@"Caught exception unregistering from Command: %@", + localException); + } + NS_ENDHANDLER + DESTROY(cmdServer); + } +} + +- (void) cmdWarn: (NSString*)fmt, ... +{ + va_list ap; + + if (nil == warningLogger) + { + warningLogger = [[EcLogger loggerForType: LT_WARNING] retain]; + } + va_start (ap, fmt); + [warningLogger log: fmt arguments: ap]; + va_end (ap); +} + +- (void) prcNewDay: (NSCalendarDate*)when +{ + NSString *sub; + + /* New day ... archive debug/log files into a subdirectory based on + * the current date. This is yesterday's debug, so we use yesterday. + */ + sub = [[when dateByAddingYears: 0 months: 0 days: -1 hours: 0 minutes: 0 + seconds: 0] descriptionWithCalendarFormat: @"%Y-%m-%d"]; + NSLog(@"%@", [self cmdArchive: sub]); +} + +- (void) prcNewHour: (NSCalendarDate*)when +{ + return; +} + +- (void) prcNewMinute: (NSCalendarDate*)when +{ + struct mallinfo info; + + info = mallinfo(); + if (memSlot >= memSize) + { + NSUInteger average; + NSUInteger notLeaked; + int i; + + notLeaked = [self prcNotLeaked]; + + /* Next slot to record in will be zero. + */ + memSlot = 0; + + /* Find the average usage over the last set of samples. + * Round up to a block size. + */ + for (average = i = 0; i < memSize; i++) + { + average += memRoll[i]; + } + average /= memSize; + + /* The cache size should probably be less than the heap usage + * though some objects in the cache could actually be using + * memory which didn't come from the heap, so subtracting one + * from the other is not completely reliable. + */ + average = (notLeaked < average) ? average - notLeaked : 0; + + /* Convert to 1KB blocks. + */ + average = ((average / 1024) + 1) * 1024; + if (average > memPeak) + { + /* Alert if the we have peaked above the allowed size. + */ + if (average > (memAllowed * 1024 * 1024)) + { + [self cmdError: @"Average memory usage grown to %"PRIuPTR"KB", + average / 1024]; + } + + /* Set the peak memory from the last set of readings. + */ + memLast = memPeak; + for (i = 0; i < memSize; i++) + { + unsigned size = memRoll[i]; + + size = (notLeaked < size) ? size - notLeaked : 0; + if (size > memPeak) + { + memPeak = size; + } + } + if (YES == [cmdDefs boolForKey: @"Memory"]) + { + /* We want detailed memory information, so we set the next + * alerting threshold from 20 to 40 KB above the current + * peak usage. + */ + memPeak = ((memPeak / (20 * 1024)) + 2) * (50 * 1024); + } + else + { + /* We do not want detailed memory information, + * so we set the next alerting threshold from + * 500 to 1000 KB above the current peak usage, + * ensuring that only serious increases + * in usage will generate an alert. + */ + memPeak = ((memPeak / (500 * 1024)) + 2) * (500 * 1024); + } + } + } + /* Record the latest memory usage. + */ + memRoll[memSlot] = info.uordblks; + if (YES == [cmdDefs boolForKey: @"Memory"]) + { + [self cmdDbg: cmdDetailDbg + msg: @"Memory usage %u", memRoll[memSlot]]; + } + memSlot++; + + return; +} + +- (void) prcHadIP: (NSDate*)when +{ + if (when == nil) + { + when = [dateClass date]; + } + ASSIGN(lastIP, when); +} + +- (void) prcHadOP: (NSDate*)when +{ + if (when == nil) + { + when = [dateClass date]; + } + ASSIGN(lastOP, when); +} + +- (NSUInteger) prcNotLeaked +{ + return 0; +} + +- (int) prcRun +{ + NSConnection *c; + + if (YES == cmdIsTransient) + { + [self cmdWarn: @"Attempted to run transient process."]; + return 1; + } + + c = [[NSConnection alloc] initWithReceivePort: (NSPort*)[NSSocketPort port] + sendPort: nil]; + [c setRootObject: self]; + + if ([c registerName: [self cmdName] + withNameServer: [NSSocketPortNameServer sharedInstance]] == NO) + { + DESTROY(c); + [self cmdError: @"Unable to register with name server."]; + return 2; + } + + [c setDelegate: self]; + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(connectionBecameInvalid:) + name: NSConnectionDidDieNotification + object: c]; + + [self cmdAudit: @"Started `%@'", [self cmdName]]; + + while ([c isValid]) + { + NS_DURING + { + [[NSRunLoop currentRunLoop] run]; + } + NS_HANDLER + { + [self cmdAlert: @"Problem running server: %@", localException]; + } + NS_ENDHANDLER; + } + + /* finish server */ + + [self cmdQuit: 0]; + return 0; +} + +- (void) setCmdDebug: (NSString*)mode withDescription: (NSString*)desc +{ + [cmdDebugKnown setObject: desc forKey: mode]; +} + +- (void) setCmdTimeout: (SEL)sel +{ + cmdTimSelector = sel; + [self triggerCmdTimeout]; +} + +- (void) setCmdUpdate: (id)obj withMethod: (SEL)sel +{ + UpdateHandler *u; + unsigned count = [updateHandlers count]; + unsigned i; + + if (updateHandlers == nil) + { + updateHandlers = [[NSMutableArray alloc] initWithCapacity: 1]; + } + for (i = 0; i < count; i++) + { + u = [updateHandlers objectAtIndex: i]; + if ([u obj] == obj) + { + break; + } + } + if (i == count) + { + if (sel != 0) + { + u = [[UpdateHandler alloc] initWithObj: obj andSel: sel]; + [updateHandlers addObject: u]; + RELEASE(u); + } + } + else + { + if (sel == 0) + { + [updateHandlers removeObjectAtIndex: i]; + } + else + { + [u setSel: sel]; + } + } +} + +- (void) triggerCmdTimeout +{ + if (cmdPTimer != nil) + { + /* + * If the timer is due to go off soon - don't reset it - + * continually resetting could lead to it never firing. + */ + if ([[cmdPTimer fireDate] timeIntervalSinceNow] <= 0.01) + { + return; + } + [cmdPTimer invalidate]; + cmdPTimer = nil; + } + cmdPTimer = [NSTimer scheduledTimerWithTimeInterval: 0.001 + target: self + selector: @selector(_timedOut:) + userInfo: nil + repeats: NO]; +} + +- (BOOL) cmdDebugMode: (NSString*)mode +{ + if ([cmdDebugModes member: mode] == nil) + return NO; + return YES; +} + +- (void) cmdDebugMode: (NSString*)mode active: (BOOL)flag +{ + if ((mode = findMode(cmdDebugKnown, mode)) != nil) + { + if (flag == YES && [cmdDebugModes member: mode] == nil) + { + [cmdDebugModes addObject: mode]; + } + if (flag == NO && [cmdDebugModes member: mode] != nil) + { + [cmdDebugModes removeObject: mode]; + } + } +} + +- (void) cmdGnip: (id )from + sequence: (unsigned)num + extra: (NSData*)data +{ + [self cmdDbg: cmdConnectDbg msg: @"cmdGnip: %lx sequence: %u extra: %lx", + (unsigned long)from, num, (unsigned long)data]; +} + +- (BOOL) cmdIsConnected +{ + return cmdServer != nil; +} + +- (BOOL) cmdMatch: (NSString*)val toKey: (NSString*)key +{ + unsigned int len = [val length]; + + if (len == 0) + { + return NO; + } + if (len > [key length]) + { + return NO; + } + if ([key compare: val + options: NSCaseInsensitiveSearch|NSLiteralSearch + range: NSMakeRange(0, len)] != NSOrderedSame) + { + return NO; + } + return YES; +} + +- (NSString*) cmdMesg: (NSArray*)msg +{ + NSMutableString *saved; + NSString *result; + NSString *cmd; + SEL sel; + + if (msg == nil || [msg count] < 1) + { + return @"no command specified\n"; + } + + cmd = findAction([msg objectAtIndex: 0]); + if (nil == cmd) + { + return @"unrecognised command\n"; + } + + sel = NSSelectorFromString([NSString stringWithFormat: @"cmdMesg%@:", cmd]); + + saved = replyBuffer; + replyBuffer = [NSMutableString stringWithCapacity: 50000]; + + NS_DURING + { + [self performSelector: sel withObject: msg]; + } + NS_HANDLER + { + [self cmdPrintf: @"\n%@ during command\n", localException]; + } + NS_ENDHANDLER + + result = replyBuffer; + replyBuffer = saved; + return result; +} + +/* + * Name - cmdMesgData: from: + * Purpose - Invoke other methods to handle commands. + */ +- (void) cmdMesgData: (NSData*)dat from: (NSString*)name +{ + NSArray *msg; + NSString *val; + + msg = [NSPropertyListSerialization + propertyListWithData: dat + options: NSPropertyListMutableContainers + format: 0 + error: 0]; + val = [self cmdMesg: msg]; + if (cmdServer) + { + NS_DURING + { + [cmdServer reply: val to: name from: cmdLogName()]; + } + NS_HANDLER + { + cmdServer = nil; + NSLog(@"Caught exception sending client reply to Command: %@ %@", + name, localException); + } + NS_ENDHANDLER + } +} + +- (void) cmdMesgarchive: (NSArray*)msg +{ + if ([msg count] == 0) + { + [self cmdPrintf: @"archives log files"]; + } + else + { + if (strcmp([[msg objectAtIndex: 0] UTF8String], "help") == 0) + { + [self cmdPrintf: @"\nThe archive command is used to archive the"]; + [self cmdPrintf: @" debug file to a subdirectory.\n"]; + [self cmdPrintf: @"You should not need it - as archiving should"]; + [self cmdPrintf: @"be done automatically at midnight.\n"]; + } + else + { + [self cmdPrintf: @"\n%@\n", [self cmdArchive: nil]]; + } + } +} + +- (void) cmdMesgdebug: (NSArray*)msg +{ + if ([msg count] == 0) + { + [self cmdPrintf: @"turns on debug logging"]; + } + else + { + if (strcmp([[msg objectAtIndex: 0] UTF8String], "help") == 0) + { + [self cmdPrintf: @"\nWithout parameters, the debug command is "]; + [self cmdPrintf: @"used to list the currently active "]; + [self cmdPrintf: @"debug modes.\n"]; + [self cmdPrintf: @"With the single parameter 'default', the debug "]; + [self cmdPrintf: @"command is used to revert to default "]; + [self cmdPrintf: @"debug settings.\n"]; + [self cmdPrintf: @"With the single parameter 'all', the debug "]; + [self cmdPrintf: @"command is used to activate all "]; + [self cmdPrintf: @"debugging.\n"]; + [self cmdPrintf: @"With any other parameter, the debug command "]; + [self cmdPrintf: @"is used to activate one of the "]; + [self cmdPrintf: @"debug modes listed below.\n\n"]; + + [self cmdPrintf: @"%@\n", cmdDebugKnown]; + } + else if ([msg count] > 1) + { + NSString *mode = (NSString*)[msg objectAtIndex: 1]; + NSString *key; + + if ([mode caseInsensitiveCompare: @"default"] == NSOrderedSame) + { + NSEnumerator *enumerator = [cmdDebugKnown keyEnumerator]; + + while (nil != (mode = [enumerator nextObject])) + { + key = [@"Debug-" stringByAppendingString: mode]; + [cmdDefs setCommand: nil forKey: key]; + } + [self cmdPrintf: @"Now using debug settings from config.\n"]; + } + else if ([mode caseInsensitiveCompare: @"all"] == NSOrderedSame) + { + NSEnumerator *enumerator = [cmdDebugKnown keyEnumerator]; + + while (nil != (mode = [enumerator nextObject])) + { + key = [@"Debug-" stringByAppendingString: mode]; + [cmdDefs setCommand: @"YES" forKey: key]; + } + [self cmdPrintf: @"All debugging is now active.\n"]; + } + else + { + [self cmdPrintf: @"debug mode '"]; + if ((mode = findMode(cmdDebugKnown, mode)) == nil) + { + [self cmdPrintf: @"%@' is not known.\n", mode]; + } + else + { + [self cmdPrintf: @"%@", mode]; + if ([cmdDebugModes member: mode] == nil) + { + [self cmdPrintf: @"' is now active."]; + } + else + { + [self cmdPrintf: @"' is already active."]; + } + key = [@"Debug-" stringByAppendingString: mode]; + [cmdDefs setCommand: @"YES" forKey: key]; + } + } + } + else + { + [self cmdPrintf: @"%@\n", [EcLogger loggerForType: LT_DEBUG]]; + [self cmdPrintf: @"Current active debug modes -\n"]; + if ([cmdDebugModes count] == 0) + { + [self cmdPrintf: @"\nNone.\n"]; + } + else + { + [self cmdPrintf: @"%@\n", cmdDebugModes]; + } + } + } +} + +- (void) cmdMesghelp: (NSArray*)msg +{ + NSEnumerator *e = [cmdActions objectEnumerator]; + NSString *cmd; + SEL sel; + + if ([msg count] == 0) + { + [self cmdPrintf: @"provides helpful information :-)"]; + return; + } + else if ([msg count] > 1) + { + NSString *found; + + cmd = [msg objectAtIndex: 1]; + found = findAction(cmd); + + if ([cmd caseInsensitiveCompare: @"control"] == NSOrderedSame) + { + [self cmdPrintf: @"Detailed help on the 'control' command -\n"]; + [self cmdPrintf: @"This command enables you to send an"]; + [self cmdPrintf: @"instruction to the 'Control' server rather\n"]; + [self cmdPrintf: @"than to the currently connected server.\n"]; + [self cmdPrintf: @"Everything typed on the line after the word"]; + [self cmdPrintf: @" 'control' is treated as a command to\n"]; + [self cmdPrintf: @"the 'Control' server process.\n"]; + [self cmdPrintf: @"\nTo disconnect from the server type -\n"]; + [self cmdPrintf: @" control connect\n"]; + [self cmdPrintf: @"\nTo disconnect from the host type -\n"]; + [self cmdPrintf: @" control host\n"]; + return; + } + else if (nil == found) + { + [self cmdPrintf: @"Unable to find the '%@' command -\n", cmd]; + } + else if ([found caseInsensitiveCompare: @"help"] != NSOrderedSame) + { + NSMutableArray *m; + + [self cmdPrintf: @"Detailed help on the '%@' command -\n", found]; + sel = NSSelectorFromString( + [NSString stringWithFormat: @"cmdMesg%@:", found]); + + /* To get the help on a command, we invoke that command + * by passing the command and arguments (ie, the msg array). + * The command implementation should check the argument 0 - + * if it is "help", it should print out help on itself. + * Save expanded (unabbreviated) commands so the methods + * getting the help request don't need to recheck the values. + */ + m = [[msg mutableCopy] autorelease]; + [m replaceObjectAtIndex: 0 withObject: @"help"]; + [m replaceObjectAtIndex: 1 withObject: found]; + + [self performSelector: sel withObject: m]; + return; + } + } + + [self cmdPrintf: @"\n"]; + [self cmdPrintf: @"For help on a particular command, type 'help '\n"]; + [self cmdPrintf: @"\n"]; + [self cmdPrintf: @"These are the commands available to you -\n"]; + [self cmdPrintf: @"\n"]; + while ((cmd = [e nextObject]) != nil) + { + unsigned l; + + sel = NSSelectorFromString( + [NSString stringWithFormat: @"cmdMesg%@:", cmd]); + [self cmdPrintf: @"%@ - ", cmd]; + l = [cmd length]; + while (l++ < 9) + { + [self cmdPrintf: @" "]; + } + [self performSelector: sel withObject: nil]; + [self cmdPrintf: @"\n"]; + } +} + +- (void) cmdMesgnodebug: (NSArray*)msg +{ + if ([msg count] == 0) + { + [self cmdPrintf: @"turns off debug logging"]; + } + else + { + if (strcmp([[msg objectAtIndex: 0] UTF8String], "help") == 0) + { + [self cmdPrintf: @"\n"]; + [self cmdPrintf: @"Without parameters, the nodebug command is "]; + [self cmdPrintf: @"used to list the currently inactive\n"]; + [self cmdPrintf: @"debug modes.\n"]; + [self cmdPrintf: @"With the single parameter 'all', the nodebug "]; + [self cmdPrintf: @"command is used to deactivate all\n"]; + [self cmdPrintf: @"debugging.\n"]; + [self cmdPrintf: @"With the single parameter 'default', the "]; + [self cmdPrintf: @"nodebug command is used to revert to default "]; + [self cmdPrintf: @"debug settings.\n"]; + [self cmdPrintf: @"With any other parameter, the nodebug command is"]; + [self cmdPrintf: @" used to deactivate one of the\n"]; + [self cmdPrintf: @"debug modes listed below.\n"]; + [self cmdPrintf: @"\n"]; + [self cmdPrintf: @"%@\n", cmdDebugKnown]; + } + else if ([msg count] > 1) + { + NSString *mode = (NSString*)[msg objectAtIndex: 1]; + NSString *key; + + if ([mode caseInsensitiveCompare: @"default"] == NSOrderedSame) + { + NSEnumerator *enumerator = [cmdDebugKnown keyEnumerator]; + + while (nil != (mode = [enumerator nextObject])) + { + key = [@"Debug-" stringByAppendingString: mode]; + [cmdDefs setCommand: nil forKey: key]; + } + [self cmdPrintf: @"Now using debug settings from config.\n"]; + } + else if ([mode caseInsensitiveCompare: @"all"] == NSOrderedSame) + { + NSEnumerator *enumerator = [cmdDebugKnown keyEnumerator]; + + while (nil != (mode = [enumerator nextObject])) + { + key = [@"Debug-" stringByAppendingString: mode]; + [cmdDefs setCommand: @"NO" forKey: key]; + } + [self cmdPrintf: @"All debugging is now inactive.\n"]; + } + else + { + [self cmdPrintf: @"debug mode '"]; + if ((mode = findMode(cmdDebugKnown, mode)) == nil) + { + [self cmdPrintf: @"%@' is not known.\n", mode]; + } + else + { + [self cmdPrintf: @"%@' is ", mode]; + if ([cmdDebugModes member: mode] == nil) + { + [self cmdPrintf: @"already inactive.\n"]; + } + else + { + [self cmdPrintf: @"now deactivated.\n"]; + } + key = [@"Debug-" stringByAppendingString: mode]; + [cmdDefs setCommand: @"NO" forKey: key]; + } + } + } + else + { + NSArray *a = [cmdDebugKnown allKeys]; + NSMutableSet *s = [NSMutableSet setWithArray: a]; + + /* + * Find items known but not active. + */ + [s minusSet: cmdDebugModes]; + [self cmdPrintf: @"Current inactive debug modes -\n"]; + if (a == 0) + { + [self cmdPrintf: @"none.\n"]; + } + else + { + [self cmdPrintf: @"%@\n", s]; + } + } + } +} + +- (void) cmdMesgmemory: (NSArray*)msg +{ + if ([msg count] == 0 + || [[msg objectAtIndex: 0] caseInsensitiveCompare: @"help"] + == NSOrderedSame) + { + [self cmdPrintf: @"controls recording of memory management statistics"]; + } + else + { + if (strcmp([[msg objectAtIndex: 0] UTF8String], "help") == 0) + { + [self cmdPrintf: @"\n"]; + [self cmdPrintf: @"Without parameters, the memory command is "]; + [self cmdPrintf: @"used to list the changes in the numbers of "]; + [self cmdPrintf: @"objects allocated since the command was "]; + [self cmdPrintf: @"last issued.\n"]; + [self cmdPrintf: @"With the single parameter 'all', the memory "]; + [self cmdPrintf: @"command is used to list the cumulative totals "]; + [self cmdPrintf: @"of objects allocated since the first time a "]; + [self cmdPrintf: @"memory command was issued.\n"]; + [self cmdPrintf: @"With the single parameter 'yes', the memory "]; + [self cmdPrintf: @"command is used to turn on gathering of "]; + [self cmdPrintf: @"memory usage statistics.\n"]; + [self cmdPrintf: @"With the single parameter 'no', the memory "]; + [self cmdPrintf: @"command is used to turn off gathering of "]; + [self cmdPrintf: @"memory usage statistics.\n"]; + [self cmdPrintf: @"With the single parameter 'default', the "]; + [self cmdPrintf: @"gathering of memory usage statistics reverts "]; + [self cmdPrintf: @"to the default setting."]; + [self cmdPrintf: @"\n"]; + } + else if ([msg count] > 1) + { + NSString *word = [msg objectAtIndex: 1]; + + if ([word caseInsensitiveCompare: @"default"] == NSOrderedSame) + { + [cmdDefs setCommand: nil forKey: @"Memory"]; + [self cmdPrintf: @"Memory checking: %s\n", + [cmdDefs boolForKey: @"Memory"] ? "YES" : "NO"]; + } + else if ([word caseInsensitiveCompare: @"all"] == NSOrderedSame) + { + if (NO == [cmdDefs boolForKey: @"Memory"]) + { + [self cmdPrintf: + @"Memory statistics were not being gathered.\n"]; + [self cmdPrintf: @"Memory statistics Will start from NOW.\n"]; + } + else + { + const char* list; + + list = (const char*)GSDebugAllocationList(NO); + [self cmdPrintf: @"%s", list]; + } + [cmdDefs setCommand: @"YES" forKey: @"Memory"]; + } + else if ([word boolValue] == YES) + { + if (NO == [cmdDefs boolForKey: @"Memory"]) + { + [self cmdPrintf: + @"Memory statistics were not being gathered.\n"]; + [self cmdPrintf: @"Statistics Will start from NOW.\n"]; + } + else + { + [self cmdPrintf: + @"Memory statistics are already being gathered.\n"]; + } + [cmdDefs setCommand: @"YES" forKey: @"Memory"]; + } + else + { + if (NO == [cmdDefs boolForKey: @"Memory"]) + { + [self cmdPrintf: + @"Memory statistics were not being gathered.\n"]; + } + [self cmdPrintf: @"Memory statistics are turned off NOW.\n"]; + [cmdDefs setCommand: @"NO" forKey: @"Memory"]; + } + } + else + { + [self cmdPrintf: @"\n%@ on %@ running since %@\n\n", + cmdLogName(), hostName, [self cmdStarted]]; + + if (NO == [cmdDefs boolForKey: @"Memory"]) + { + [self cmdPrintf: @"Memory stats are not being gathered.\n"]; + } + else + { + const char* list; + + list = (const char*)GSDebugAllocationList(YES); + [self cmdPrintf: @"%s", list]; + } + } + } +} + +- (void) cmdMesgstatus: (NSArray*)msg +{ + if ([msg count] == 0) + { + [self cmdPrintf: @"provides server status information"]; + } + else + { + [self cmdPrintf: @"\n%@ on %@ running since %@\n", + cmdLogName(), hostName, [self cmdStarted]]; + if ([self cmdLastIP] != nil) + { + [self cmdPrintf: @"Last IP at %@\n", [self cmdLastIP]]; + } + if ([self cmdLastOP] != nil) + { + [self cmdPrintf: @"Last OP at %@\n", [self cmdLastOP]]; + } + if (servers != nil) + { + NSEnumerator *e; + RemoteServer *server; + + e = [servers objectEnumerator]; + while ((server = (RemoteServer *)[e nextObject]) != 0) + { + [self cmdPrintf: @"%@\n", server]; + } + } + + if (YES == [cmdDefs boolForKey: @"Memory"]) + { + [self cmdPrintf: @"Memory usage: %u (peak), %u (current)\n", + memPeak, memLast]; + } + } +} + + +- (void) cmdPing: (id )from + sequence: (unsigned)num + extra: (NSData*)data +{ + [self cmdDbg: cmdConnectDbg msg: @"cmdPing: %lx sequence: %u extra: %lx", + (unsigned long)from, num, (unsigned long)data]; + [from cmdGnip: self sequence: num extra: nil]; +} + +- (void) cmdPrintf: (NSString*)fmt, ... +{ + va_list ap; + NSString *tmp; + + va_start(ap, fmt); + tmp = [[stringClass alloc] initWithFormat: fmt arguments: ap]; + va_end(ap); + [replyBuffer appendString: tmp]; + RELEASE(tmp); +} + +- (void) cmdQuit: (int)status +{ + cmdIsQuitting = YES; + + if (cmdPTimer != nil) + { + [cmdPTimer invalidate]; + cmdPTimer = nil; + } + + [alarmDestination shutdown]; + + [self cmdFlushLogs]; + + [self cmdUnregister]; + + [alarmDestination shutdown]; + [alarmDestination release]; + + { + NSArray *keys; + unsigned index; + + /* + * Close down all log files. + */ + keys = [cmdLogMap allKeys]; + for (index = 0; index < [keys count]; index++) + { + [self cmdLogEnd: [keys objectAtIndex: index]]; + } + } + + exit(status); +} + +- (void) cmdUpdate: (NSMutableDictionary*)info +{ + NSMutableDictionary *newConfig; + NSDictionary *dict; + NSEnumerator *enumerator; + NSString *key; + BOOL update = NO; + + if (info == nil) + { + update = YES; + } + else + { + newConfig = [NSMutableDictionary dictionaryWithCapacity: 32]; + /* + * Put all values for this application in the cmdConf dictionary. + */ + dict = [info objectForKey: cmdLogName()]; + if (dict != nil) + { + enumerator = [dict keyEnumerator]; + while ((key = [enumerator nextObject]) != nil) + { + id obj; + + if ([noNetConfig containsObject: key]) + { + [self cmdWarn: @"Bad key '%@' in net config.", key]; + continue; + } + obj = [dict objectForKey: key]; + [newConfig setObject: obj forKey: key]; + } + } + /* + * Add any default values to the cmdConf + * dictionary where we don't have application + * specific values. + */ + dict = [info objectForKey: @"*"]; + if (dict) + { + enumerator = [dict keyEnumerator]; + while ((key = [enumerator nextObject]) != nil) + { + if ([newConfig objectForKey: key] == nil) + { + id obj; + + if ([noNetConfig containsObject: key]) + { + [self cmdWarn: @"Bad key '%@' in net config.", key]; + continue; + } + obj = [dict objectForKey: key]; + [newConfig setObject: obj forKey: key]; + } + } + } + + dict = [info objectForKey: @"Operators"]; + if (dict != nil && dict != cmdOperators) + { + ASSIGNCOPY(cmdOperators, dict); + } + if (cmdConf == nil || [cmdConf isEqual: newConfig] == NO) + { + ASSIGN(cmdConf, newConfig); + update = [cmdDefs setConfiguration: newConfig]; + } + } + + /* + * Now we update any settings which may be changed on the fly. + */ + + if (update == YES) + { + NSString *str; + + if ((str = [self cmdConfig: @"CmdInterval"]) != nil) + { + [self setCmdInterval: [str floatValue]]; + } + + str = [self cmdConfig: @"MemAllowed"]; + if (nil != str) + { + memAllowed = [str intValue]; + if (memAllowed <= 0) + { + memAllowed = DEFMEMALLOWED; // Fifty megabytes default + } + } + + /* + * Notify all interested parties of update. + */ + if (updateHandlers != nil) + { + NSArray *a = [NSArray arrayWithArray: updateHandlers]; + unsigned count = [a count]; + unsigned i; + + for (i = 0; i < count; i++) + { + UpdateHandler *u = [a objectAtIndex: i]; + + if ([updateHandlers indexOfObjectIdenticalTo: u] != NSNotFound) + { + [[u obj] performSelector: [u sel]]; + } + } + } + } + + if (servers != nil) + { + NSEnumerator *e; + RemoteServer *server; + + e = [servers objectEnumerator]; + + while ((server = [e nextObject])) + { + [server update]; + } + } +} + + +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver: self]; + [prcLock lock]; + if (self == EcProc) + { + EcProc = nil; + cmdIsInitialised = NO; + } + [prcLock unlock]; + [super dealloc]; +} + +- (NSString*) description +{ + return [stringClass stringWithFormat: @"%@ (%@) on %@", + [super description], cmdLogName(), hostName]; +} + +- (id) init +{ + id objects[2]; + id keys[2]; + NSString *prefix; + + objects[0] = [[NSProcessInfo processInfo] processName]; + objects[1] = @"."; + prefix = EC_DEFAULTS_PREFIX; + if (nil == prefix) + { + prefix = @""; + } + keys[0] = [prefix stringByAppendingString: @"ProgramName"]; + keys[1] = [prefix stringByAppendingString: @"HomeDirectory"]; + + return [self initWithDefaults: [NSDictionary dictionaryWithObjects: objects + forKeys: keys + count: 2]]; +} + +- (id) initWithDefaults: (NSDictionary*) defs +{ + [prcLock lock]; + if (nil != EcProc) + { + [self release]; + [prcLock unlock]; + [NSException raise: NSGenericException + format: @"EcProcess initialiser called more than once"]; + } + if (nil == (self = [super init])) + { + [prcLock unlock]; + return nil; + } + else + { + NSProcessInfo *pinfo; + NSFileManager *mgr; + NSEnumerator *enumerator; + NSString *name; + NSString *str; + NSString *dbg; + NSString *prf; + BOOL flag; + int i; + + EcProc = self; + + pinfo = [NSProcessInfo processInfo]; + mgr = [NSFileManager defaultManager]; + prf = EC_DEFAULTS_PREFIX; + if (nil == prf) + { + prf = @""; + } + + ASSIGN(cmdDefs, [NSUserDefaults + userDefaultsWithPrefix: prf + strict: EC_DEFAULTS_STRICT]); + if (defs != nil) + { + [cmdDefs registerDefaults: defs]; + } + + if ([[pinfo arguments] containsObject: @"--help"]) + { + NSLog(@"Standard command-line arguments ...\n\n" + @"-%@CommandHost [aHost] Host of command server to use.\n" + @"-%@CommandName [aName] Name of command server to use.\n" + @"-%@Daemon [YES/NO] Fork process to run in background?\n" + @"-%@EffectiveUser [aName] User to run as\n" + @"-%@HomeDirectory [relDir] Relative home within user directory\n" + @"-%@UserDirectory [dir] Override home directory for user\n" + @"-%@Instance [aNumber] Instance number for multiple copies\n" + @"-%@Memory [YES/NO] Enable memory allocation checks?\n" + @"-%@ProgramName [aName] Name to use for this program\n" + @"-%@Testing [YES/NO] Run in test mode (if supported)\n" + @"\n--version to get version information and quit\n\n", + prf, prf, prf, prf, prf, prf, prf, prf, prf, prf + ); + RELEASE(self); + [prcLock unlock]; + return nil; + } + + if ([[pinfo arguments] containsObject: @"--version"]) + { + NSLog(@"%@ %@", [self prcCopyright], cmdVersion(nil)); + RELEASE(self); + [prcLock unlock]; + return nil; + } + + cmdUser = EC_EFFECTIVE_USER; + if (nil == cmdUser) + { + cmdUser = [[cmdDefs stringForKey: @"EffectiveUser"] retain]; + } + if (0 == [cmdUser length] || YES == [cmdUser isEqual: @"*"] + || YES == [cmdUser isEqualToString: NSUserName()]) + { + ASSIGN(cmdUser, NSUserName()); + } + else + { + const char *user = [cmdUser UTF8String]; + struct passwd *pwd = getpwnam(user); + int uid; + + if (pwd != 0) + { + uid = pwd->pw_uid; + } + else + { + uid = geteuid(); + } + + if (uid != (int)geteuid()) + { + if (geteuid() == 0 || (int)getuid() == uid) + { + if (0 != setuid(uid)) + { + [prcLock unlock]; + NSLog(@"You must be '%@' to run this.", cmdUser); + exit(1); + } + } + else + { + [prcLock unlock]; + NSLog(@"You must be '%@' to run this.", cmdUser); + exit(1); + } + } + GSSetUserName(cmdUser); + if (NO == [cmdUser isEqualToString: NSUserName()]) + { + [prcLock unlock]; + NSLog(@"You must be '%@' to run this.", cmdUser); + exit(1); + } + ASSIGN(cmdDefs, [NSUserDefaults + userDefaultsWithPrefix: prf + strict: EC_DEFAULTS_STRICT]); + if (defs != nil) + { + [cmdDefs registerDefaults: defs]; + } + } + + if (nil == noNetConfig) + { + noNetConfig = [[NSMutableArray alloc] initWithCapacity: 4]; + [noNetConfig + addObject: [prf stringByAppendingString: @"Daemon"]]; + [noNetConfig + addObject: [prf stringByAppendingString: @"EffectiveUser"]]; + [noNetConfig + addObject: [prf stringByAppendingString: @"Instance"]]; + [noNetConfig + addObject: [prf stringByAppendingString: @"Transient"]]; + } + + defs = [cmdDefs dictionaryRepresentation]; + enumerator = [defs keyEnumerator]; + dbg = [prf stringByAppendingString: @"Debug-"]; + while ((str = [enumerator nextObject]) != nil) + { + if ([str hasPrefix: dbg]) + { + id obj = [defs objectForKey: str]; + NSString *key = [str substringFromIndex: [dbg length]]; + + if ([cmdDebugKnown objectForKey: key] == nil) + { + [cmdDebugKnown setObject: key forKey: key]; + } + if ([obj isKindOfClass: stringClass]) + { + if ([obj intValue] != 0 + || [obj isEqual: @"YES"] || [obj isEqual: @"yes"]) + { + if ([cmdDebugModes member: key] == nil) + { + [cmdDebugModes addObject: key]; + } + } + else + { + if ([cmdDebugModes member: key] != nil) + { + [cmdDebugModes removeObject: key]; + } + } + } + } + } + + started = RETAIN([dateClass date]); + + ASSIGN(cmdName, [cmdDefs stringForKey: @"ProgramName"]); + if (nil == cmdName) + { + ASSIGN(cmdName, [pinfo processName]); + } + + /* + * Make sure our users home directory exists. + */ + str = [cmdDefs objectForKey: @"UserDirectory"]; + str = cmdSetUserDirectory(str); + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + if ([mgr createDirectoryAtPath: str attributes: nil] == NO) + { + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + [prcLock unlock]; + NSLog(@"Unable to create directory - %@", str); + exit(1); + } + } + else + { + flag = YES; + } + } + if (flag == NO) + { + [prcLock unlock]; + NSLog(@"The path '%@' is not a directory", str); + exit(1); + } + + str = [cmdDefs objectForKey: @"HomeDirectory"]; + if (str != nil) + { + if ([str length] == 0) + { + str = nil; + } + else if ([str isAbsolutePath] == YES) + { + NSLog(@"Absolute HomeDirectory ignored."); + str = nil; + } + cmdSetHome(str); + } + + for (i = 0; i < 32; i++) + { + switch (i) + { + case SIGPROF: + case SIGABRT: + break; + + case SIGPIPE: + case SIGTTOU: + case SIGTTIN: + case SIGHUP: + case SIGCHLD: + + /* SIGWINCH is generated when the terminal size + changes (for example when you resize the xterm). + Ignore it. */ +#ifdef SIGWINCH + case SIGWINCH: +#endif + + signal(i, SIG_IGN); + break; + + case SIGINT: + case SIGTERM: + signal(i, qhandler); + break; + + case SIGSTOP: + case SIGCONT: + case SIGTSTP: + signal(i, SIG_DFL); + break; + + default: + signal(i, ihandler); + break; + } + } + + ASSIGN(cmdInst, [cmdDefs stringForKey: @"Instance"]); + if (nil != cmdInst) + { + str = [[NSString alloc] initWithFormat: @"%@-%@", cmdName, cmdInst]; + ASSIGN(cmdName, str); + [str release]; + } + + str = userDir; + if (cmdHomeDir() != nil) + { + str = [str stringByAppendingPathComponent: cmdHomeDir()]; + } + str = [str stringByStandardizingPath]; + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + if ([mgr createDirectoryAtPath: str attributes: nil] == NO) + { + if ([mgr fileExistsAtPath: str isDirectory: &flag] == NO) + { + [prcLock unlock]; + NSLog(@"Unable to create directory - %@", str); + exit(1); + } + } + else + { + flag = YES; + } + } + if (flag == NO) + { + [prcLock unlock]; + NSLog(@"The path '%@' is not a directory", str); + exit(1); + } + + if ([mgr changeCurrentDirectoryPath: str] == NO) + { + [prcLock unlock]; + NSLog(@"Unable to move to directory - %@", str); + exit(1); + } + + /* + * Make sure the data directory exists. + */ + if ([self cmdDataDirectory] == nil) + { + [prcLock unlock]; + NSLog(@"Unable to create/access data directory"); + exit(1); + } + + /* + * Make sure the logs directory exists. + */ + if (cmdLogsDir(nil) == nil) + { + [prcLock unlock]; + NSLog(@"Unable to create/access logs directory"); + exit(1); + } + + [[NSProcessInfo processInfo] setProcessName: cmdName]; + + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(cmdDefaultsChanged:) + name: NSUserDefaultsDidChangeNotification + object: [NSUserDefaults standardUserDefaults]]; + + [self cmdDefaultsChanged: nil]; + + /* Archive any existing debug log left over by a crash. + */ + str = [cmdName stringByAppendingPathExtension: @"debug"]; + if (cmdDebugName == nil || [cmdDebugName isEqual: str] == NO) + { + NSFileHandle *hdl; + + /* + * Force archiving of old logfile. + */ + [self cmdArchive: nil]; + + ASSIGNCOPY(cmdDebugName, str); + hdl = [self cmdLogFile: cmdDebugName]; + if (hdl == nil) + { + [prcLock unlock]; + exit(1); + } + } + + /* The cmdActions set contains the names of all the commands this + * instance will accept from the Command server. These are methods + * taking an array of strings as an argument and returning a string + * as their result. All have names of the form cmdMesgXXX: where + * XXX is the (lowercase) command. + */ + cmdActions = [NSMutableSet new]; + enumerator = [GSObjCMethodNames(self, YES) objectEnumerator]; + while (nil != (name = [enumerator nextObject])) + { + NSRange r = [name rangeOfString: @":"]; + + if ([name hasPrefix: @"cmdMesg"] && 1 == r.length && r.location > 7) + { + name = [name substringWithRange: NSMakeRange(7, r.location - 7)]; + if (YES == [name isEqual: [name lowercaseString]]) + { + [cmdActions addObject: name]; + } + } + } + + cmdIsTransient = [cmdDefs boolForKey: @"Transient"]; + + if ([cmdDefs objectForKey: @"CmdInterval"] != nil) + { + [self setCmdInterval: [cmdDefs floatForKey: @"CmdInterval"]]; + } + + if (YES == [self cmdIsClient] && nil == [self cmdNewServer]) + { + NSLog(@"Giving up - unable to contact '%@' server on '%@'", + prcCommandName(), prcCommandHost()); + [self release]; + self = nil; + } + } + [prcLock unlock]; + + /* + * Finally, put self in background. + */ + if ([cmdDefs boolForKey: @"Daemon"] == YES) + { + int pid = fork(); + + if (pid == 0) + { + cmdFlagDaemon = YES; + setpgid(0, getpid()); + } + else + { + if (pid < 0) + { + printf("Failed fork to run as daemon.\r\n"); + } + else + { + printf("Process backgrounded (running as daemon)\r\n"); + } + exit(0); + } + } + + return self; +} + +/* + * Implement the CmdConfig protocol. + */ + +- (void) replaceFile: (NSData*)data + name: (NSString*)name + isConfig: (BOOL)f +{ + [NSException raise: NSGenericException + format: @"Illegal method call"]; +} + +- (void) requestConfigFor: (id)c +{ + [NSException raise: NSGenericException + format: @"Illegal method call"]; +} + +- (void) requestFile: (BOOL)flag + name: (NSString*)name + for: (id)c +{ + [NSException raise: NSGenericException + format: @"Illegal method call"]; +} + +- (void) updateConfig: (NSData*)info +{ + id plist = [NSPropertyListSerialization + propertyListWithData: info + options: NSPropertyListMutableContainers + format: 0 + error: 0]; + + if (nil != plist) + { + [self cmdUpdate: plist]; + } +} + +- (id) server: (NSString *)serverName +{ + RemoteServer *server; + + server = (RemoteServer *)[servers objectForKey: serverName]; + + if (server == nil) + { + NSLog (@"Trying to ask for not-existent server %@", serverName); + return nil; + } + + return [server proxy]; +} + +- (id) server: (NSString *)serverName forNumber: (NSString*)num +{ + RemoteServer *server; + NSArray *config; + + server = (RemoteServer *)[servers objectForKey: serverName]; + + if (server == nil) + { + NSLog (@"Trying to ask for not-existent server %@", serverName); + return nil; + } + config = [server multiple]; + if (config != nil && [config count] > 1) + { + int val = -1; + unsigned count = [config count]; + + /* + * Get trailing two digits of number ... in range 00 to 99 + */ + if ([num length] >= 2) + { + val = [[num substringFromIndex: [num length] - 2] intValue]; + } + if (val < 0) + { + val = 0; + } + /* + * Try to find a broadcast server with a numeric range matching + * the number we were given. + */ + while (count-- > 0) + { + NSDictionary *d = [config objectAtIndex: count]; + + if (val >= [[d objectForKey: @"Low"] intValue] + && val <= [[d objectForKey: @"High"] intValue]) + { + return [[server proxy] BCPproxy: count]; + } + } + [self cmdError: @"Attempt to get %@ server for number %@ with bad config", + serverName, num]; + return nil; + } + return [server proxy]; +} + +- (BOOL) isServerMultiple: (NSString *)serverName +{ + RemoteServer *server; + + server = (RemoteServer *)[servers objectForKey: serverName]; + + if (server == nil) + { + NSLog (@"Trying to ask for not-existent server %@", serverName); + return NO; + } + + return ([server multiple] == nil) ? NO : YES; +} +@end + +@implementation EcProcess (Private) + +// For logging from the Control server. +- (void) log: (NSString*)message type: (EcLogType)t +{ + NSLog(@"%@", message); +} + +- (void) cmdMesgrelease: (NSArray*)msg +{ + if ([msg count] == 0) + { + [self cmdPrintf: @"controls double release memory error detection"]; + return; + } + + if ([[msg objectAtIndex: 0] caseInsensitiveCompare: @"help"] == NSOrderedSame) + { + [self cmdPrintf: @"controls double release memory error detection\n"]; + [self cmdPrintf: @"This has a big impact on program performance.\n"]; + [self cmdPrintf: @"'release yes' turns on checking\n"]; + [self cmdPrintf: @"'release no' turns off checking\n"]; + [self cmdPrintf: @"'release default' reverts to default setting\n"]; + [self cmdPrintf: @"'release' reports current status\n"]; + return; + } + + if ([msg count] == 1) + { + [self cmdPrintf: @"Double release checking: %s\n", + [cmdDefs boolForKey: @"Release"] ? "YES" : "NO"]; + } + + if ([msg count] > 1) + { + if ([[msg objectAtIndex: 1] caseInsensitiveCompare: @"default"] + == NSOrderedSame) + { + [cmdDefs setCommand: nil forKey: @"Release"]; + } + else + { + [cmdDefs setCommand: [msg objectAtIndex: 1] forKey: @"Release"]; + } + [self cmdPrintf: @"Double release checking: %s\n", + [cmdDefs boolForKey: @"Release"] ? "YES" : "NO"]; + } +} + +- (void) cmdMesgtesting: (NSArray*)msg +{ + if ([msg count] == 0) + { + [self cmdPrintf: @"controls whether server is running in testing mode"]; + return; + } + + if ([[msg objectAtIndex: 0] caseInsensitiveCompare: @"help"] == NSOrderedSame) + { + [self cmdPrintf: @"controls whether server is running in testing mode\n"]; + [self cmdPrintf: @"Behavior in testing mode is server dependent.\n"]; + [self cmdPrintf: @"'testing yes' turns on testing mode\n"]; + [self cmdPrintf: @"'testing no' turns off testing mode\n"]; + [self cmdPrintf: @"'testing default' reverts to default setting\n"]; + [self cmdPrintf: @"'testing' reports current status\n"]; + return; + } + + if ([msg count] == 1) + { + [self cmdPrintf: @"Server running in testing mode: %s\n", + cmdFlagTesting ? "YES" : "NO"]; + } + + if ([msg count] > 1) + { + if ([[msg objectAtIndex: 1] caseInsensitiveCompare: @"default"] + == NSOrderedSame) + { + [cmdDefs setObject: nil forKey: @"Testing"]; + } + else + { + [cmdDefs setObject: [msg objectAtIndex: 1] forKey: @"Testing"]; + } + [self cmdPrintf: @"Server running in testing mode: %s\n", + cmdFlagTesting ? "YES" : "NO"]; + } +} + +- (NSString*) _moveLog: (NSString*)name to: (NSString*)sub +{ + NSString *status; + NSString *where; + + NS_DURING + { + where = cmdLogsDir(sub); + if (where != nil) + { + NSFileManager *mgr = [NSFileManager defaultManager]; + NSString *from; + NSString *path; + NSString *base; + NSString *gzpath; + unsigned count = 0; + + path = [where stringByAppendingPathComponent: name]; + from = [cmdLogsDir(nil) stringByAppendingPathComponent: name]; + + /* + * Check for pre-existing file - if found, try another. + */ + base = path; + path = [base stringByAppendingPathExtension: @"0"]; + gzpath = [path stringByAppendingPathExtension: @"gz"]; + while ([mgr fileExistsAtPath: path] == YES + || [mgr fileExistsAtPath: gzpath] == YES) + { + NSString *ext; + + ext = [stringClass stringWithFormat: @"%u", ++count]; + path = [base stringByAppendingPathExtension: ext]; + gzpath = [path stringByAppendingPathExtension: @"gz"]; + } + + if ([mgr movePath: from + toPath: path + handler: nil] == NO) + { + status = [NSString stringWithFormat: + @"Unable to move %@ to %@", from, path]; + } + else + { + status = [NSString stringWithFormat: + @"Moved %@ to %@", from, path]; + } + } + else + { + status = [NSString stringWithFormat: + @"Unable to archive log %@ into %@", name, sub]; + } + } + NS_HANDLER + { + status = [NSString stringWithFormat: @"Problem in %@ with %@ to %@ - %@", + NSStringFromSelector(_cmd), name, sub, localException]; + } + NS_ENDHANDLER + return status; +} + +- (void) _timedOut: (NSTimer*)timer +{ + static BOOL inProgress = NO; + int sig = cmdSignalled(); + + cmdPTimer = nil; + if (sig > 0) + { + [self cmdQuit: sig]; + } + if (YES == inProgress) + { + NSLog(@"_timedOut: ignored because timeout already in progress"); + } + else + { + BOOL delay = NO; + + inProgress = YES; + NS_DURING + { + NSCalendarDate *now = [NSCalendarDate date]; + static int lastDay = 0; + static int lastHour = 0; + static int lastMinute = 0; + static int lastTenSecond = 0; + BOOL newDay = NO; + BOOL newHour = NO; + BOOL newMinute = NO; + BOOL newTenSecond = NO; + int i; + + i = [now dayOfWeek]; + if (i != lastDay) + { + lastDay = i; + newDay = YES; + newHour = YES; + newMinute = YES; + newTenSecond = YES; + } + i = [now hourOfDay]; + if (i != lastHour) + { + lastHour = i; + newHour = YES; + newMinute = YES; + newTenSecond = YES; + } + i = [now minuteOfHour]; + if (i != lastMinute) + { + lastMinute = i; + newMinute = YES; + newTenSecond = YES; + } + i = [now secondOfMinute] / 10; + if (i != lastTenSecond) + { + lastTenSecond = i; + newTenSecond = YES; + } + if (YES == newTenSecond) + { + [self cmdNewServer]; + } + if (YES == newMinute) + { + [self prcNewMinute: now]; + } + if (YES == newHour) + { + [self prcNewHour: now]; + } + if (YES == newDay) + { + [self prcNewDay: now]; + } + if (cmdTimSelector != 0) + { + [self performSelector: cmdTimSelector]; + } + } + NS_HANDLER + { + NSLog(@"Exception performing regular timeout: %@", localException); + delay = YES; // Avoid runaway logging. + } + NS_ENDHANDLER + + if (cmdPTimer == nil) + { + NSTimeInterval when = cmdTimInterval; + + if (delay == YES && when < 1.0) + { + when = 10.0; + } + cmdPTimer = + [NSTimer scheduledTimerWithTimeInterval: when + target: self + selector: @selector(_timedOut:) + userInfo: nil + repeats: NO]; + } + inProgress = NO; + } +} + +@end + diff --git a/EcUserDefaults.h b/EcUserDefaults.h new file mode 100644 index 0000000..99170dc --- /dev/null +++ b/EcUserDefaults.h @@ -0,0 +1,52 @@ + +#import + +@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.
+ * If aPrefix is nil, the string given by the EcUserDefaultsPrefix user + * default is used.
+ * 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).
+ * 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.
+ * Returns YES if the configuration changed, NO otherwise. + */ +- (BOOL) setConfiguration: (NSDictionary*)config; + +@end + diff --git a/EcUserDefaults.m b/EcUserDefaults.m new file mode 100644 index 0000000..5753aba --- /dev/null +++ b/EcUserDefaults.m @@ -0,0 +1,347 @@ + +#import +#import +#import +#import +#import +#import + +#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 + diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..2fb96a1 --- /dev/null +++ b/GNUmakefile @@ -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 diff --git a/GNUmakefile.postamble b/GNUmakefile.postamble new file mode 100644 index 0000000..703089d --- /dev/null +++ b/GNUmakefile.postamble @@ -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% + diff --git a/GNUmakefile.preamble b/GNUmakefile.preamble new file mode 100644 index 0000000..fd45c7a --- /dev/null +++ b/GNUmakefile.preamble @@ -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) + diff --git a/NSFileHandle+Printf.h b/NSFileHandle+Printf.h new file mode 100644 index 0000000..a918d33 --- /dev/null +++ b/NSFileHandle+Printf.h @@ -0,0 +1,13 @@ + +#include + +/** File handle printing utilities. + */ +@interface NSFileHandle (Printf) + +- (void) printWithFormat: (NSString*)format arguments: (va_list)args; +- (void) printf: (NSString*)format,...; +- (void) puts: (NSString*)text; + +@end + diff --git a/NSFileHandle+Printf.m b/NSFileHandle+Printf.m new file mode 100644 index 0000000..0fb844b --- /dev/null +++ b/NSFileHandle+Printf.m @@ -0,0 +1,48 @@ +#import +#import +#import +#import + +#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 + diff --git a/Terminate.m b/Terminate.m new file mode 100644 index 0000000..c55a01e --- /dev/null +++ b/Terminate.m @@ -0,0 +1,50 @@ + +#include + +#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)proxy terminate]; + + RELEASE(arp); + return 0; +} + diff --git a/config.guess b/config.guess new file mode 100755 index 0000000..182c2a1 --- /dev/null +++ b/config.guess @@ -0,0 +1,1326 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002 Free Software Foundation, Inc. + +timestamp='2002-02-12' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + + +dummy=dummy-$$ +trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int dummy(){}" > $dummy.c ; + for c in cc gcc c89 ; do + ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ; + if test $? = 0 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + rm -f $dummy.c $dummy.o $dummy.rel ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + UNAME_MACHINE_ARCH=`(uname -p) 2>/dev/null` || \ + UNAME_MACHINE_ARCH=unknown + case "${UNAME_MACHINE_ARCH}" in + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mipseb-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + cat <$dummy.s + .data +\$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + + .text + .globl main + .align 4 + .ent main +main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) + UNAME_MACHINE="alpha" + ;; + 1-0) + UNAME_MACHINE="alphaev5" + ;; + 1-1) + UNAME_MACHINE="alphaev56" + ;; + 1-101) + UNAME_MACHINE="alphapca56" + ;; + 2-303) + UNAME_MACHINE="alphaev6" + ;; + 2-307) + UNAME_MACHINE="alphaev67" + ;; + 2-1307) + UNAME_MACHINE="alphaev68" + ;; + esac + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy \ + && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null) && HP_ARCH=`./$dummy` + if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi + rm -f $dummy.c $dummy + fi ;; + esac + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3D:*:*:*) + echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + x86:Interix*:3*) + echo i386-pc-interix3 + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i386-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + rm -f $dummy.c + test x"${CPU}" != x && echo "${CPU}-pc-linux-gnu" && exit 0 + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + rm -f $dummy.c + test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + echo `uname -p`-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + if test "${UNAME_MACHINE}" = "x86pc"; then + UNAME_MACHINE=pc + echo i386-${UNAME_MACHINE}-nto-qnx + else + echo `uname -p`-${UNAME_MACHINE}-nto-qnx + fi + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[GKLNPTVW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0 +rm -f $dummy.c $dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.h b/config.h new file mode 100644 index 0000000..491e711 --- /dev/null +++ b/config.h @@ -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 header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_TELNET_H 1 + +/* Define to 1 if you have the 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 header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the 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 header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SIGNAL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the 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 and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to `int' if doesn't define. */ +/* #undef gid_t */ + +/* Define to `int' if does not define. */ +/* #undef mode_t */ + +/* Define to `int' if doesn't define. */ +/* #undef uid_t */ diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..eade8ba --- /dev/null +++ b/config.h.in @@ -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 header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_TELNET_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FCNTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SIGNAL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the 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 and . */ +#undef TIME_WITH_SYS_TIME + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define to `int' if does not define. */ +#undef mode_t + +/* Define to `int' if doesn't define. */ +#undef uid_t diff --git a/config.make.in b/config.make.in new file mode 100644 index 0000000..e69de29 diff --git a/config.sub b/config.sub new file mode 100755 index 0000000..d1f7cb9 --- /dev/null +++ b/config.sub @@ -0,0 +1,1451 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002 Free Software Foundation, Inc. + +timestamp='2002-02-12' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dsp16xx \ + | fr30 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips16 | mips64 | mips64el | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el | mips64vr4300 \ + | mips64vr4300el | mips64vr5000 | mips64vr5000el \ + | mipsbe | mipseb | mipsel | mipsle | mipstx39 | mipstx39el \ + | mipsisa32 \ + | mn10200 | mn10300 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[34] | sh[34]eb | shbe | shle | sh64 \ + | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armv*-* \ + | avr-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c54x-* \ + | clipper-* | cray2-* | cydra-* \ + | d10v-* | d30v-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | m32r-* \ + | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \ + | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipseb-* \ + | mipsle-* | mipsel-* | mipstx39-* | mipstx39el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* | sh64-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* | xstormy16-* \ + | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [cjt]90) + basic_machine=${basic_machine}-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mipsel*-linux*) + basic_machine=mipsel-unknown + os=-linux-gnu + ;; + mips*-linux*) + basic_machine=mips-unknown + os=-linux-gnu + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i686-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=t3e-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + windows32) + basic_machine=i386-pc + os=-windows32-msvcrt + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + mips) + if [ x$os = x-linux-gnu ]; then + basic_machine=mips-unknown + else + basic_machine=mips-mips + fi + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh3eb | sh4eb) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + c4x*) + basic_machine=c4x-none + os=-coff + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto*) + os=-nto-qnx + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure b/configure new file mode 100755 index 0000000..967e186 --- /dev/null +++ b/configure @@ -0,0 +1,5445 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.63. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell bug-autoconf@gnu.org about your system, + echo including any error possibly output before this message. + echo This can help us improve future autoconf versions. + echo Configuration will now proceed without shell functions. +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="EcProcess.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +EGREP +GREP +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { $as_echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { $as_echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2 + { (exit 1); exit 1; }; } ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { $as_echo "$as_me: error: working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { $as_echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.63 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.63. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + ac_site_file1=$CONFIG_SITE +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test -r "$ac_site_file"; then + { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:$LINENO: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:$LINENO: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_config_headers="$ac_config_headers config.h" + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +if test -z "$ac_file"; then + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + fi + fi +fi +{ $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest$ac_cv_exeext +{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if test "${ac_cv_objext+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:$LINENO: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5 +$as_echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + { { $as_echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5 +$as_echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;} + { (exit 1); exit 1; }; } + +{ $as_echo "$as_me:$LINENO: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if test "${ac_cv_build+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + { { $as_echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +$as_echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5 +$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical build" >&5 +$as_echo "$as_me: error: invalid value of canonical build" >&2;} + { (exit 1); exit 1; }; };; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:$LINENO: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if test "${ac_cv_host+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5 +$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;} + { (exit 1); exit 1; }; } +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical host" >&5 +$as_echo "$as_me: error: invalid value of canonical host" >&2;} + { (exit 1); exit 1; }; };; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + + +{ $as_echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +$as_echo_n "checking return type of signal handlers... " >&6; } +if test "${ac_cv_type_signal+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +return *(signal (0, 0)) (0) == 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type_signal=int +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_signal=void +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +$as_echo "$ac_cv_type_signal" >&6; } + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + + + +{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if test "${ac_cv_path_GREP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done +done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:$LINENO: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done +done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } +if test "${ac_cv_header_time+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_time=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_time=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +$as_echo "$ac_cv_header_time" >&6; } +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } +if test "${ac_cv_header_sys_wait_h+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_sys_wait_h=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_sys_wait_h=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +$as_echo "$ac_cv_header_sys_wait_h" >&6; } +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + + + + + + + + + + +for ac_header in 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 +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +{ $as_echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5 +$as_echo_n "checking for uid_t in sys/types.h... " >&6; } +if test "${ac_cv_type_uid_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5 +$as_echo "$ac_cv_type_uid_t" >&6; } +if test $ac_cv_type_uid_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uid_t int +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define gid_t int +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking type of array argument to getgroups" >&5 +$as_echo_n "checking type of array argument to getgroups... " >&6; } +if test "${ac_cv_type_getgroups+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_type_getgroups=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Mike Rendell for this test. */ +$ac_includes_default +#define NGID 256 +#undef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +int +main () +{ + gid_t gidset[NGID]; + int i, n; + union { gid_t gval; long int lval; } val; + + val.lval = -1; + for (i = 0; i < NGID; i++) + gidset[i] = val.gval; + n = getgroups (sizeof (gidset) / MAX (sizeof (int), sizeof (gid_t)) - 1, + gidset); + /* Exit non-zero if getgroups seems to require an array of ints. This + happens when gid_t is short int but getgroups modifies an array + of ints. */ + return n > 0 && gidset[n] != val.gval; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_getgroups=gid_t +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_type_getgroups=int +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +if test $ac_cv_type_getgroups = cross; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "getgroups.*int.*gid_t" >/dev/null 2>&1; then + ac_cv_type_getgroups=gid_t +else + ac_cv_type_getgroups=int +fi +rm -f conftest* + +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_getgroups" >&5 +$as_echo "$ac_cv_type_getgroups" >&6; } + +cat >>confdefs.h <<_ACEOF +#define GETGROUPS_T $ac_cv_type_getgroups +_ACEOF + + +{ $as_echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +$as_echo_n "checking return type of signal handlers... " >&6; } +if test "${ac_cv_type_signal+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +return *(signal (0, 0)) (0) == 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type_signal=int +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_signal=void +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +$as_echo "$ac_cv_type_signal" >&6; } + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + +{ $as_echo "$as_me:$LINENO: checking for mode_t" >&5 +$as_echo_n "checking for mode_t... " >&6; } +if test "${ac_cv_type_mode_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_type_mode_t=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof (mode_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if (sizeof ((mode_t))) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_mode_t=yes +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_mode_t" >&5 +$as_echo "$ac_cv_type_mode_t" >&6; } +if test "x$ac_cv_type_mode_t" = x""yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define mode_t int +_ACEOF + +fi + + + + +for ac_func in getpid setpgid +do +as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 +$as_echo_n "checking for $ac_func... " >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + eval "$as_ac_var=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +ac_config_files="$ac_config_files config.make" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.63. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTION]... [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.63, + with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2008 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_FILES="$CONFIG_FILES '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + { $as_echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { $as_echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "config.make") CONFIG_FILES="$CONFIG_FILES config.make" ;; + + *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + $as_echo "$as_me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=' ' +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\).*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\).*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5 +$as_echo "$as_me: error: could not setup config files machinery" >&2;} + { (exit 1); exit 1; }; } +_ACEOF + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_t=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_t"; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5 +$as_echo "$as_me: error: could not setup config headers machinery" >&2;} + { (exit 1); exit 1; }; } +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5 +$as_echo "$as_me: error: invalid tag $ac_tag" >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + ac_file_inputs="$ac_file_inputs '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:$LINENO: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +$as_echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + esac \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" + } >"$tmp/config.h" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$tmp/config.h" "$ac_file" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ + || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5 +$as_echo "$as_me: error: could not create -" >&2;} + { (exit 1); exit 1; }; } + fi + ;; + + + esac + +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..f2fcbdf --- /dev/null +++ b/configure.in @@ -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) + diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..ebc6691 --- /dev/null +++ b/install-sh @@ -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 diff --git a/local.make b/local.make new file mode 100644 index 0000000..04fe892 --- /dev/null +++ b/local.make @@ -0,0 +1,6 @@ + +ADDITIONAL_CPPFLAGS += \ + '-DEC_DEFAULTS_PREFIX=@"BS"'\ + '-DEC_DEFAULTS_STRICT=NO'\ + '-DEC_EFFECTIVE_USER=@"brains99"'\ +