commit 94ff5e8d81112fb3692dba958c31b2259a65fa42 Author: Richard Frith-MacDonald Date: Sun Feb 19 11:59:22 2012 +0000 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 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"'\ +