diff --git a/EcAlarm.m b/EcAlarm.m index 2781a38..26febb9 100644 --- a/EcAlarm.m +++ b/EcAlarm.m @@ -467,19 +467,38 @@ EcMakeManagedObject(NSString *host, NSString *process, NSString *component) { 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]; + if (0 == _notificationID) + { + return [NSString stringWithFormat: + @"Alarm addr: %-16" PRIx64 " %@ %@ %@ %@ %@ at %@(%@) %@ %@ %@", + (uint64_t)(uintptr_t)self, + _managedObject, + [c stringFromEventType: _eventType], + [c stringFromProbableCause: _probableCause], + [c stringFromSeverity: _perceivedSeverity], + [c stringFromTrend: _trendIndicator], + _eventDate, + _firstEventDate, + _specificProblem, + _proposedRepairAction, + _additionalText]; + } + else + { + return [NSString stringWithFormat: + @"Alarm notification: %-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 diff --git a/EcLogger.m b/EcLogger.m index f8b4234..d8ec218 100644 --- a/EcLogger.m +++ b/EcLogger.m @@ -82,7 +82,12 @@ static NSArray *modes; logger->interval = 10; logger->size = 8 * 1024; logger->message = [[NSMutableString alloc] initWithCapacity: 2048]; - [EcProc setCmdUpdate: logger withMethod: @selector(update)]; + + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(update) + name: NSUserDefaultsDidChangeNotification + object: [EcProc cmdDefaults]]; [logger update]; [loggers addObject: logger]; RELEASE(logger); @@ -133,6 +138,7 @@ static NSArray *modes; { [self flush]; [timer invalidate]; + [[NSNotificationCenter defaultCenter] removeObserver: self]; RELEASE(key); RELEASE(flushKey); RELEASE(serverKey); @@ -479,13 +485,22 @@ static NSArray *modes; [self _flush]; } -/* Should only be called on main thread. +/* Should only execute on main thread. */ - (void) update { - NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; + NSUserDefaults *defs; NSString *str; + if (NO == [NSThread isMainThread]) + { + [self performSelectorOnMainThread: @selector(update) + withObject: nil + waitUntilDone: NO + modes: modes]; + return; + } + defs = [EcProc cmdDefaults]; /* * 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 diff --git a/EcProcess.h b/EcProcess.h index fd2bd31..1768700 100644 --- a/EcProcess.h +++ b/EcProcess.h @@ -181,12 +181,16 @@ typedef enum { 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 + +/** This is an exceptional method which may be used without registering + * your process with a Command server first (ie, it 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). + * configuration from a central location (the Control/Command servers).
+ * The returned value is a a serialized property list ... you need to + * deserialize using the standard GNUstep property list APIs.
* 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. @@ -391,7 +395,8 @@ extern NSString* cmdVersion(NSString *ver); */ - (void) cmdAlert: (NSString*)fmt arguments: (va_list)args; -/** Send a SEVERE error message to the server. +/** Send a SEVERE error message to the server by calling the + * -cmdAlert:arguments: method. */ - (void) cmdAlert: (NSString*)fmt, ...; @@ -406,7 +411,7 @@ extern NSString* cmdVersion(NSString *ver); */ - (void) cmdAudit: (NSString*)fmt arguments: (va_list)args; -/** Send a log message to the server. +/** Send a log message to the server by calling the -cmdAudit:arguments: method. */ - (void) cmdAudit: (NSString*)fmt, ...; @@ -425,22 +430,29 @@ extern NSString* cmdVersion(NSString *ver); - (void) cmdDbg: (NSString*)type msg: (NSString*)fmt arguments: (va_list)args; /** Send a debug message - as long as the debug mode specified as 'type' - * is currently set. + * is currently set. Operates by calling the -cmdDbg:msg:arguments: method. */ - (void) cmdDbg: (NSString*)type msg: (NSString*)fmt, ...; -/** Send a debug message with debug mode 'defaultMode'. +/** Send a debug message with debug mode 'defaultMode'.
+ * Calls the -cmdDbg:msg:arguments: method. */ - (void) cmdDebug: (NSString*)fmt arguments: (va_list)args; -/** Send a debug message with debug mode 'defaultMode'. +/** Send a debug message with debug mode 'defaultMode'.
+ * Operates by calling the -cmdDebug:arguments: method. */ - (void) cmdDebug: (NSString*)fmt, ...; /** Called whenever the user defaults are updated due to a central * configuration change (or another defaults system change).
+ * This is also called by -cmdUpdate: even if no configuration + * actually changed ... in which case the notification argument + * is nil.
* If you override this to handle configuration changes, don't forget - * to call the superclass implementation. + * to call the superclass implementation.
+ * This method is provided to allow subclasses to control the order + * in which defaults changes are handled by them and their superclasses. */ - (void) cmdDefaultsChanged: (NSNotification*)n; @@ -448,11 +460,12 @@ extern NSString* cmdVersion(NSString *ver); */ - (void) cmdError: (NSString*)fmt arguments: (va_list)args; -/** Send an error message to the server. +/** Send an error message to the server by calling the + * -cmdError:arguments: method. */ - (void) cmdError: (NSString*)fmt, ...; -/** Flush logging information +/** Flush logging information. */ - (void) cmdFlushLogs; @@ -465,12 +478,13 @@ extern NSString* cmdVersion(NSString *ver); */ - (BOOL) cmdIsClient; -/** Returns a fag indicating whether this process is currently connected +/** Returns a flag 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. +/** Returns YES is the process is running in test mode, NO otherwise.
+ * Test mode is defined by the EcTesting user default. */ - (BOOL) cmdIsTesting; @@ -505,10 +519,10 @@ extern NSString* cmdVersion(NSString *ver); * 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 + * you should respond with some useful help 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). + * description of the command). */ - (NSString*) cmdMesg: (NSArray*)msg; @@ -525,7 +539,9 @@ extern NSString* cmdVersion(NSString *ver); */ - (NSMutableDictionary*)cmdOperator: (NSString*)name password: (NSString*)pass; -/** Used to tell your application to quit. +/** Used to tell your application to quit.
+ * Subclasses should override this method to perform any pre-shutdown cleanup + * before they call the superclass implementation. */ - (void) cmdQuit: (NSInteger)status; @@ -533,11 +549,32 @@ extern NSString* cmdVersion(NSString *ver); */ - (int) cmdSignalled; -/** Used to tell your application about configuration changes. +/** Used to tell your application about configuration changes.
+ * This is called before the NSUserDefaults system is updated with the + * changes, so you may use it to update internal state in the knowledge + * that code watching for user defaults change notifications will not + * have updated yet.
+ * The base class implementation is responsible for updating the user + * defaults system ... so be sure that your implementation calls the + * superclass implementation (unless you wish to suppress the configuration + * update).
+ * You may alter the info dictionary prior to passign it to the superclass + * implementation if you wish to adjust the new configuration before it + * takes effect. */ - (void) cmdUpdate: (NSMutableDictionary*)info; - +/** Used to tell your application about configuration changes.
+ * This is called after the NSUserDefaults system is updated with the + * changes, so you may use it to update internal state in the knowledge + * that code watching for user defaults change notifications will have + * updated already.
+ * NB. This method will be called even if your implementation of + * -cmdUpdate: suppresses the actual update. In this situation this + * method will find the configuration unchanged since the previous + * time that it was called. + */ +- (void) cmdUpdated; - (void) log: (NSString*)message type: (EcLogType)t; @@ -572,25 +609,13 @@ extern NSString* cmdVersion(NSString *ver); */ - (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. +/** Deprecated ... use -cmdDefaults instead. */ - (id) cmdConfig: (NSString*)key; diff --git a/EcProcess.m b/EcProcess.m index f281387..875e7d2 100644 --- a/EcProcess.m +++ b/EcProcess.m @@ -433,7 +433,7 @@ static EcLogger *warningLogger = nil; static NSMutableSet *cmdActions = nil; static id cmdServer = nil; static id cmdPTimer = nil; -static NSMutableDictionary *cmdConf = nil; +static NSDictionary *cmdConf = nil; static NSDictionary *cmdOperators = nil; static NSDate *cmdFirst = nil; static NSDate *cmdLast = nil; @@ -446,8 +446,6 @@ static NSTimeInterval cmdTimInterval = 60.0; static NSMutableArray *noNetConfig = nil; -static NSMutableArray *updateHandlers = nil; - static NSMutableDictionary *servers = nil; static NSString *hostName = nil; @@ -536,47 +534,6 @@ ecCommandName() } -@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"; @@ -980,6 +937,7 @@ findMode(NSDictionary* d, NSString* s) - (void) cmdMesgtesting: (NSArray*)msg; - (NSString*) _moveLog: (NSString*)name to: (NSString*)sub; - (void) _timedOut: (NSTimer*)timer; +- (void) _update: (NSMutableDictionary*)info; @end @implementation EcProcess @@ -1088,10 +1046,12 @@ static NSString *noFiles = @"No log files to archive"; - (void) cmdDefaultsChanged: (NSNotification*)n { - NSEnumerator *enumerator = [cmdDebugKnown keyEnumerator]; + NSEnumerator *enumerator; NSDictionary *dict; NSString *mode; + NSString *str; + enumerator = [cmdDebugKnown keyEnumerator]; while (nil != (mode = [enumerator nextObject])) { NSString *key = [@"Debug-" stringByAppendingString: mode]; @@ -1118,6 +1078,34 @@ static NSString *noFiles = @"No log files to archive"; GSDebugAllocationActive([cmdDefs boolForKey: @"Memory"]); [NSObject enableDoubleReleaseCheck: [cmdDefs boolForKey: @"Release"]]; cmdFlagTesting = [cmdDefs boolForKey: @"Testing"]; + + if ((str = [cmdDefs stringForKey: @"CmdInterval"]) != nil) + { + [self setCmdInterval: [str floatValue]]; + } + + str = [cmdDefs stringForKey: @"MemAllowed"]; + if (nil != str) + { + memAllowed = [str intValue]; + if (memAllowed <= 0) + { + memAllowed = DEFMEMALLOWED; // Fifty megabytes default + } + } + + if (servers != nil) + { + NSEnumerator *e; + RemoteServer *server; + + e = [servers objectEnumerator]; + + while ((server = [e nextObject])) + { + [server update]; + } + } } - (NSString*) cmdDebugPath @@ -1791,7 +1779,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); selector: @selector(cmdConnectionBecameInvalid:) name: NSConnectionDidDieNotification object: connection]; - [self cmdUpdate: r]; + [self _update: r]; } } } @@ -2046,46 +2034,6 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); [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) @@ -2341,8 +2289,8 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); { [self cmdPrintf: @"\nThe clear command is used to clear the"]; [self cmdPrintf: @" alarms currently active for this process.\n"]; - [self cmdPrintf: @"You may use the ord 'all' or a space separated"]; - [self cmdPrintf: @" list of alarm notification IDs.\n"]; + [self cmdPrintf: @"You may use the word 'all' or a space separated"]; + [self cmdPrintf: @" list of alarm addreesses.\n"]; } else { @@ -2362,6 +2310,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); for (index = 1; index < count; index++) { + uint64_t addr; NSString *arg = [msg objectAtIndex: index]; if ([arg caseInsensitiveCompare: _(@"all")] @@ -2377,16 +2326,15 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); [alarmDestination alarm: alarm]; } } - else + else if (1 == sscanf([arg UTF8String], "%" PRIx64, &addr)) { - int n = [arg intValue]; NSUInteger i; alarm = nil; for (i = 0; i < alarmCount; i++) { alarm = [a objectAtIndex: i]; - if ([alarm notificationID] == n) + if ((uint64_t)(uintptr_t)alarm == addr) { break; } @@ -2395,7 +2343,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); if (nil == alarm) { [self cmdPrintf: - @"No alarm found with the notificationID '%@'\n", + @"No alarm found with the address '%@'\n", arg]; } else @@ -2405,6 +2353,11 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); [alarmDestination alarm: alarm]; } } + else + { + [self cmdPrintf: @"Not a hexadecimal address: '%@'\n", + arg]; + } } } } @@ -2900,135 +2853,31 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); - (void) cmdUpdate: (NSMutableDictionary*)info { - NSMutableDictionary *newConfig; - NSDictionary *dict; - NSEnumerator *enumerator; - NSString *key; - BOOL update = NO; + BOOL defaultsChanged; - if (info == nil) + if (nil == info) { - update = YES; + defaultsChanged = NO; } 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]; - } + ASSIGNCOPY(cmdConf, info); + defaultsChanged = [cmdDefs setConfiguration: cmdConf]; } - - /* - * Now we update any settings which may be changed on the fly. + /* If the defaults did not actually change, + * trigger an update anyway. */ - - if (update == YES) + if (NO == defaultsChanged) { - 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]; - } + [self cmdDefaultsChanged: nil]; } } +- (void) cmdUpdated +{ + return; +} + - (void) dealloc { @@ -3602,7 +3451,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); if (nil != plist) { - [self cmdUpdate: plist]; + [self _update: plist]; } } @@ -3950,5 +3799,80 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); } } +- (void) _update: (NSMutableDictionary*)info +{ + NSMutableDictionary *newConfig; + NSDictionary *dict; + NSEnumerator *enumerator; + NSString *key; + + 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 (nil == cmdConf || [cmdConf isEqual: newConfig] == NO) + { + NS_DURING + [self cmdUpdate: newConfig]; + NS_HANDLER + [self cmdError: @"Problem before updating config: %@", localException]; + NS_ENDHANDLER + NS_DURING + [self cmdUpdated]; + NS_HANDLER + [self cmdError: @"Problem after updating config: %@", localException]; + NS_ENDHANDLER + } +} + @end