diff --git a/ChangeLog b/ChangeLog index 743a4e0..4509f10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2014-07-01 Richard Frith-Macdonald + + * Command.m: + * Control.m: + Implement auto-restart ... + Run a 'Watcher' process as a daemon with the actual server as a + subtask. If the subtask dies with a status other than zero, + restart it after a 30 second delay. + 2014-06-20 Richard Frith-Macdonald * EcProcess.m: Make sure the NSHost cache is flushed at least diff --git a/Command.m b/Command.m index 68956bf..e6810c2 100644 --- a/Command.m +++ b/Command.m @@ -74,6 +74,112 @@ inner_main() int main(int argc, char *argv[]) { + NSProcessInfo *pInfo; + NSArray *pArgs; + NSString *pName; + CREATE_AUTORELEASE_POOL(pool); + + pInfo = [NSProcessInfo processInfo]; + pArgs = [pInfo arguments]; + pName = [pInfo processName]; + + if ([pArgs containsObject: @"--Watched"] == NO) + { + NSMutableArray *args = AUTORELEASE([pArgs mutableCopy]); + NSString *path = [[NSBundle mainBundle] executablePath]; + NSAutoreleasePool *inner = nil; + BOOL done = NO; + int status = 0; + NSFileHandle *null; + NSTask *t; + + [args removeObjectAtIndex: 0]; + + if ([pArgs containsObject: @"--Watcher"] == NO) + { + /* In the top level task ... set flags to create a subtask + * to act as a watcher for other tasks, and once that has + * been created, exit to leave it running as a daemon. + */ + [args addObject: @"--Watcher"]; + t = [NSTask new]; + NS_DURING + { + [t setLaunchPath: path]; + [t setArguments: args]; + [t setEnvironment: [pInfo environment]]; + null = [NSFileHandle fileHandleWithNullDevice]; + [t setStandardInput: null]; + [t setStandardOutput: null]; + [t setStandardError: null]; + [t launch]; + } + NS_HANDLER + { + NSLog(@"Problem creating %@ subprocess: %@", + pName, localException); + exit(1); + } + NS_ENDHANDLER + [t release]; + exit(0); + } + + /* This is the watcher ... its subtasks are those which are watched. + */ + + /* Set args to tell subtask task not to make itself a daemon + */ + [args addObject: @"-Daemon"]; + [args addObject: @"NO"]; + + /* Set args to tell task it is being watched. + */ + [args removeObject: @"--Watcher"]; + [args addObject: @"--Watched"]; + + while (NO == done) + { + DESTROY(inner); + inner = [NSAutoreleasePool new]; + t = [[NSTask new] autorelease]; + NS_DURING + { + [t setLaunchPath: path]; + [t setArguments: args]; + [t setEnvironment: [pInfo environment]]; + null = [NSFileHandle fileHandleWithNullDevice]; + [t setStandardInput: null]; + [t setStandardOutput: null]; + [t setStandardError: null]; + [t launch]; + [t waitUntilExit]; + if (0 == [t terminationStatus]) + { + done = YES; + } + else + { + /* Subprocess died ... try to restart after 30 seconds + */ + [NSThread sleepForTimeInterval: 30.0]; + } + } + NS_HANDLER + { + done = YES; + status = 1; + NSLog(@"Problem creating %@ subprocess: %@", + pName, localException); + } + NS_ENDHANDLER + } + DESTROY(inner); + DESTROY(pool); + exit(status); + } + DESTROY(pool); + inner_main(); return 0; } diff --git a/Control.m b/Control.m index 47a393a..8599b92 100644 --- a/Control.m +++ b/Control.m @@ -71,6 +71,112 @@ inner_main() int main(int argc, char *argv[]) { + NSProcessInfo *pInfo; + NSArray *pArgs; + NSString *pName; + CREATE_AUTORELEASE_POOL(pool); + + pInfo = [NSProcessInfo processInfo]; + pArgs = [pInfo arguments]; + pName = [pInfo processName]; + + if ([pArgs containsObject: @"--Watched"] == NO) + { + NSMutableArray *args = AUTORELEASE([pArgs mutableCopy]); + NSString *path = [[NSBundle mainBundle] executablePath]; + NSAutoreleasePool *inner = nil; + BOOL done = NO; + int status = 0; + NSFileHandle *null; + NSTask *t; + + [args removeObjectAtIndex: 0]; + + if ([pArgs containsObject: @"--Watcher"] == NO) + { + /* In the top level task ... set flags to create a subtask + * to act as a watcher for other tasks, and once that has + * been created, exit to leave it running as a daemon. + */ + [args addObject: @"--Watcher"]; + t = [NSTask new]; + NS_DURING + { + [t setLaunchPath: path]; + [t setArguments: args]; + [t setEnvironment: [pInfo environment]]; + null = [NSFileHandle fileHandleWithNullDevice]; + [t setStandardInput: null]; + [t setStandardOutput: null]; + [t setStandardError: null]; + [t launch]; + } + NS_HANDLER + { + NSLog(@"Problem creating %@ subprocess: %@", + pName, localException); + exit(1); + } + NS_ENDHANDLER + [t release]; + exit(0); + } + + /* This is the watcher ... its subtasks are those which are watched. + */ + + /* Set args to tell subtask task not to make itself a daemon + */ + [args addObject: @"-Daemon"]; + [args addObject: @"NO"]; + + /* Set args to tell task it is being watched. + */ + [args removeObject: @"--Watcher"]; + [args addObject: @"--Watched"]; + + while (NO == done) + { + DESTROY(inner); + inner = [NSAutoreleasePool new]; + t = [[NSTask new] autorelease]; + NS_DURING + { + [t setLaunchPath: path]; + [t setArguments: args]; + [t setEnvironment: [pInfo environment]]; + null = [NSFileHandle fileHandleWithNullDevice]; + [t setStandardInput: null]; + [t setStandardOutput: null]; + [t setStandardError: null]; + [t launch]; + [t waitUntilExit]; + if (0 == [t terminationStatus]) + { + done = YES; + } + else + { + /* Subprocess died ... try to restart after 30 seconds + */ + [NSThread sleepForTimeInterval: 30.0]; + } + } + NS_HANDLER + { + done = YES; + status = 1; + NSLog(@"Problem creating %@ subprocess: %@", + pName, localException); + } + NS_ENDHANDLER + } + DESTROY(inner); + DESTROY(pool); + exit(status); + } + DESTROY(pool); + inner_main(); return 0; }