Improvements to handling config updates

This commit is contained in:
Richard Frith-Macdonald 2018-01-04 11:23:29 +00:00
parent 3d211f9d37
commit 1d05222942
3 changed files with 114 additions and 61 deletions

View file

@ -1,3 +1,16 @@
2018-01-04 Richard Frith-Macdonald <rfm@gnu.org>
EcProcess.m: Avoid warning about unknown connection on shutdown.
Rewrite config update code to catch exceptions, ensure that methods
are called in the correct order so that -cmdUpdated is always last,
and ensure that -cmdUpdated is called when a local defaults update
occurs.
2017-12-04 Richard Frith-Macdonald <rfm@gnu.org>
EcCommand.m: Change default period to start compressing logs to 7 days
and default for starting del;eting logs to 180 days.
2017-11-02 Richard Frith-Macdonald <rfm@gnu.org>
* EcProcess.h:

View file

@ -409,10 +409,10 @@ extern NSString* cmdVersion(NSString *ver);
@interface EcProcess : NSObject <CmdClient,EcAlarmDestination>
/** Provides initial configuration.
* This method is used by -init and its return value is passed to
* -initWithDefaults: method.<br />
* The default implementation simply sets the ProgramName and
* HomeDirectory defaults (with the default prefix configured
* This method is used by -init and its return value is passed to
* -initWithDefaults: method.<br />
* The default implementation simply sets the ProgramName and
* HomeDirectory defaults (with the default prefix configured
* when the library was built) to the current program name and
* the current directory ('.').<br />
* Subclasses may override this method to provide additional
@ -429,7 +429,7 @@ extern NSString* cmdVersion(NSString *ver);
/** Registers an NSUserDefaults key that the receiver understands.<br />
* This is primarily intended for user defaults which can reasonably
* be supplied at the command line when a process is started (and for
* which the process should therefore supply help information)<br />
* which the process should therefore supply help information).<br />
* The type text must be a a short string saying what kind of value
* must be provided (eg 'YES/NO') for the default, or nil if no help
* is to be provided for the default.<br />
@ -659,18 +659,19 @@ extern NSString* cmdVersion(NSString *ver);
*/
- (void) cmdDebug: (NSString*)fmt, ... NS_FORMAT_FUNCTION(1,2);
/** Called whenever the user defaults are updated (which may be due to a
* central configuration in additions to local defaults system changes).<br />
* This is automatically called by -cmdUpdate: (even if the user defaults
* database has not actually changed, in this case the notification
* argument is nil).<br />
/** Called automatically in response to a local NSUserDefaults database change
* or in response to a configuration update from the Control server.<br />
* This is automatically called after -cmdUpdate: (even if the user defaults
* database has not actually changed), in which case the notification
* argument is nil.<br />
* An automatic call to this method is (if it does not raise an exception)
* immediately followed by a call to the -cmdUpdated method.<br />
* This method deals with the updates for any defaults registered using
* the +ecRegisterDefault:withTypeText:andHelpText:action:value: method, so
* if you override this to handle configuration changes, don't forget
* to call the superclass implementation.<br />
* If you wish to manage updates from the central database in a specific
* order, you may wish to override the -cmdUpdate: and -cmdUpdated methods
* directly.
* order, you may wish to override the -cmdUpdate: and/or -cmdUpdated method.
*/
- (void) cmdDefaultsChanged: (NSNotification*)n;
@ -788,9 +789,9 @@ extern NSString* cmdVersion(NSString *ver);
* configuration is changed as necessary, the user defaults database is
* updated.
* </item>
* <item>Any subclass of the -cmdDefaultsChanged: method is entere
* (either as a result of the user defaults update,
* or directly by the base -cmdUpdate: method).
* <item>Any subclass implementation of the -cmdDefaultsChanged: method is
* entered (either as a result of an NSUserDefaults notification,
* or directly after the -cmdUpdate: method).
* </item>
* <item>The base implementation of the -cmdDefaultsChanged: method is
* entered, and any messages registered using the
@ -801,26 +802,27 @@ extern NSString* cmdVersion(NSString *ver);
* </item>
* <item>Any subclass implementation of the -cmdDefaultsChanged: method ends.
* </item>
* <item>The -cmdUpdated method is called.
* <item>Once all NSUserDefaults notifications have completed (ie in a
* succeeding run loop itermation), the -cmdUpdated method is called.
* </item>
* </list>
* You should usually either register your own methods to handle changes
* to particular defaults values, or override the -cmdDefaultsChanged:
* method to handle general configuration changes.<br />
* You should usually override the -cmdUpdated method to handle changes
* to particular defaults values (whether from the central database used
* by the Control sarver or from local NSUserDefaults changes).<br />
* Use this method only when you want to check/override changes
* before they take effect.
*/
- (void) cmdUpdate: (NSMutableDictionary*)info;
/** Used to tell your application about configuration changes.<br />
* 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.<br />
/** Used to tell your application about configuration changes (including
* changes to the NSUserDefaults system).<br />
* 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.<br />
* This method is called in a run loop iteration of the main thread after
* any NSUserDefaults notifications so that it can check the state of things
* after any code dealing with the notifications has run.<br />
* The return value of this method is used to control automatic generation
* of alarms for fatal configuration errors by passing it to the
* -ecConfigurationError: method.<br />
@ -828,9 +830,6 @@ extern NSString* cmdVersion(NSString *ver);
* calls the superclass implementation, and if that returns a non-nil
* result, you should pass that on as the return value from your own
* implementation.
* Use this method only for handling config changes which must be done
* after any code which is watching NSUserDefaultsDidChangNotification
* has run, or for situations where a config error may be fatal.
*/
- (NSString*) cmdUpdated;

View file

@ -146,6 +146,8 @@ ecNativeThreadID()
*/
static NSRecursiveLock *ecLock = nil;
static NSString *configError = nil;
static BOOL configInProgress = NO;
static BOOL cmdFlagDaemon = NO;
static BOOL cmdFlagTesting = NO;
static BOOL cmdIsRunning = NO;
@ -1377,6 +1379,65 @@ static NSString *noFiles = @"No log files to archive";
return cmdDefs;
}
/* This method handles the final stage of a configuration update either
* from the Control server or via the local NSUserDefaults system.
* If no error has occurred so far, we call the method to chewck/apply
* the updated config.
* If an error occurs at any stage, we reset the error string and call
* the method to report it.
*/
- (void) _checkUpdate
{
NSString *err;
if (nil == configError)
{
NS_DURING
ASSIGN(configError, [self cmdUpdated]);
NS_HANDLER
NSLog(@"Problem after updating config (in cmdUpdated) %@",
localException);
ASSIGN(configError, @"the -cmdUpdated method raised an exception");
NS_ENDHANDLER
}
err = AUTORELEASE(configError);
configError = nil;
/* NB. if err is nil this will clear any currently raised alarm
*/
[self ecConfigurationError: err];
}
/* This method is called when the defaults database is updated for any
* reason and also if the configuration from the Control server changes.
* In the latter case, the notification argument is nil.
* If no error has occurred, we call -cmdDefaultsChanged:
* After this is done, we check that the update is OK (on the next runloop
* iteration in the main thread). The async processing ensures that all
* handling of defaults database notifications has been done before we
* check the effects of the update.
*/
- (void) _defaultsChanged: (NSNotification*)n
{
if (YES == configInProgress)
{
return; // Ignore defaults updates during configuration update.
}
if (nil == configError)
{
NS_DURING
[self cmdDefaultsChanged: n];
NS_HANDLER
NSLog(@"Problem in cmdDefaultsChanged:) %@", localException);
ASSIGN(configError,
@"the -cmdDefaultsChanged: method raised an exception");
NS_ENDHANDLER
}
[self performSelectorOnMainThread: @selector(_checkUpdate)
withObject: nil
waitUntilDone: NO];
}
- (void) cmdDefaultsChanged: (NSNotification*)n
{
NSEnumerator *enumerator;
@ -1817,8 +1878,13 @@ static BOOL ecDidAwaken = NO;
DESTROY(alarmDestination);
/* Ensure our DO connection is invalidated so there will be no more
* remote communications.
* remote communications or connection related events.
*/
[EcProcConnection setDelegate: nil];
[[NSNotificationCenter defaultCenter]
removeObserver: self
name: nil
object: EcProcConnection];
[EcProcConnection invalidate];
/* The very last thing we do is to close down the log filed so they
@ -3937,24 +4003,8 @@ With two parameters ('maximum' and a number),\n\
- (void) cmdUpdate: (NSMutableDictionary*)info
{
BOOL defaultsChanged;
if (nil == info)
{
defaultsChanged = NO;
}
else
{
ASSIGNCOPY(cmdConf, info);
defaultsChanged = [cmdDefs setConfiguration: cmdConf];
}
/* If the defaults did not actually change,
* trigger an update anyway.
*/
if (NO == defaultsChanged)
{
[self cmdDefaultsChanged: nil];
}
ASSIGNCOPY(cmdConf, info);
[cmdDefs setConfiguration: cmdConf];
}
- (NSString*) cmdUpdated
@ -4485,7 +4535,7 @@ With two parameters ('maximum' and a number),\n\
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(cmdDefaultsChanged:)
selector: @selector(_defaultsChanged:)
name: NSUserDefaultsDidChangeNotification
object: [NSUserDefaults standardUserDefaults]];
@ -5290,26 +5340,17 @@ With two parameters ('maximum' and a number),\n\
if (nil == cmdConf || [cmdConf isEqual: newConfig] == NO)
{
NSString *err = nil;
DESTROY(configError);
configInProgress = YES;
NS_DURING
[self cmdUpdate: newConfig];
NS_HANDLER
NSLog(@"Problem before updating config (in cmdUpdate:) %@",
localException);
err = @"the -cmdUpdate: method raised an exception";
ASSIGN(configError, @"the -cmdUpdate: method raised an exception");
NS_ENDHANDLER
if (nil == err)
{
NS_DURING
err = [self cmdUpdated];
NS_HANDLER
NSLog(@"Problem after updating config (in cmdUpdated) %@",
localException);
err = @"the -cmdUpdated method raised an exception";
NS_ENDHANDLER
}
[self ecConfigurationError: err];
configInProgress = NO;
[self _defaultsChanged: nil];
}
}