From cff4f9da19ac5d1160c85e38d6000e6da897d921 Mon Sep 17 00:00:00 2001 From: Matt Hardcastle Date: Sat, 3 Aug 2019 14:07:25 -0700 Subject: [PATCH] Use execve to "launch" Interface on macOS Co-authored-by: dante ruiz --- launchers/darwin/CMakeLists.txt | 2 + launchers/darwin/src/Launcher.m | 34 +++++---- .../darwin/src/NSTask+NSTaskExecveAdditions.h | 9 +++ .../darwin/src/NSTask+NSTaskExecveAdditions.m | 73 +++++++++++++++++++ 4 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 launchers/darwin/src/NSTask+NSTaskExecveAdditions.h create mode 100644 launchers/darwin/src/NSTask+NSTaskExecveAdditions.m diff --git a/launchers/darwin/CMakeLists.txt b/launchers/darwin/CMakeLists.txt index fe7f4298ce..f71976960e 100644 --- a/launchers/darwin/CMakeLists.txt +++ b/launchers/darwin/CMakeLists.txt @@ -43,6 +43,8 @@ set(src_files src/LaunchInterface.h src/CustomUI.h src/CustomUI.m + src/NSTask+NSTaskExecveAdditions.h + src/NSTask+NSTaskExecveAdditions.m src/main.mm nib/Window.xib nib/SplashScreen.xib diff --git a/launchers/darwin/src/Launcher.m b/launchers/darwin/src/Launcher.m index 38027d6fd3..8fb501db55 100644 --- a/launchers/darwin/src/Launcher.m +++ b/launchers/darwin/src/Launcher.m @@ -7,6 +7,7 @@ #import "ProcessScreen.h" #import "ErrorViewController.h" #import "Settings.h" +#import "NSTask+NSTaskExecveAdditions.h" @interface Launcher () @@ -456,8 +457,6 @@ static BOOL const DELETE_ZIP_FILES = TRUE; NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; NSURL *url = [NSURL fileURLWithPath:[workspace fullPathForApplication:[[self getAppPath] stringByAppendingString:@"interface.app/Contents/MacOS/interface"]]]; - NSError *error = nil; - NSString* contentPath = [[self getDownloadPathForContentAndScripts] stringByAppendingString:@"content"]; NSString* displayName = [ self displayName]; NSString* scriptsPath = [[self getAppPath] stringByAppendingString:@"interface.app/Contents/Resources/scripts/simplifiedUIBootstrapper.js"]; @@ -484,13 +483,11 @@ static BOOL const DELETE_ZIP_FILES = TRUE; @"--no-updater", @"--no-launcher", nil]; } - [workspace launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error]; - [NSTimer scheduledTimerWithTimeInterval: 3.0 - target: self - selector: @selector(exitLauncher:) - userInfo:nil - repeats: NO]; + NSTask *task = [[NSTask alloc] init]; + task.launchPath = [url path]; + task.arguments = arguments; + [task replaceThisProcess]; } - (ProcessState) currentProccessState @@ -500,15 +497,20 @@ static BOOL const DELETE_ZIP_FILES = TRUE; - (void) callLaunchInterface:(NSTimer*) timer { + NSWindow* mainWindow = [[[NSApplication sharedApplication] windows] objectAtIndex:0]; + ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil]; - [[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen]; - [self launchInterface]; -} - - -- (void) exitLauncher:(NSTimer*) timer -{ - [NSApp terminate:self]; + [mainWindow setContentViewController: processScreen]; + @try + { + [self launchInterface]; + } + @catch (NSException *exception) + { + NSLog(@"Caught exception: Name: %@, Reason: %@", exception.name, exception.reason); + ErrorViewController* errorViewController = [[ErrorViewController alloc] initWithNibName:@"ErrorScreen" bundle:nil]; + [mainWindow setContentViewController: errorViewController]; + } } @end diff --git a/launchers/darwin/src/NSTask+NSTaskExecveAdditions.h b/launchers/darwin/src/NSTask+NSTaskExecveAdditions.h new file mode 100644 index 0000000000..f26a4021de --- /dev/null +++ b/launchers/darwin/src/NSTask+NSTaskExecveAdditions.h @@ -0,0 +1,9 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSTask (NSTaskExecveAdditions) +- (void) replaceThisProcess; +@end + +NS_ASSUME_NONNULL_END diff --git a/launchers/darwin/src/NSTask+NSTaskExecveAdditions.m b/launchers/darwin/src/NSTask+NSTaskExecveAdditions.m new file mode 100644 index 0000000000..f204d065fa --- /dev/null +++ b/launchers/darwin/src/NSTask+NSTaskExecveAdditions.m @@ -0,0 +1,73 @@ +#import "NSTask+NSTaskExecveAdditions.h" + +#import + +char ** +toCArray(NSArray *array) +{ + // Add one to count to accommodate the NULL that terminates the array. + char **cArray = (char **) calloc([array count] + 1, sizeof(char *)); + if (cArray == NULL) { + NSException *exception = [NSException + exceptionWithName:@"MemoryException" + reason:@"malloc failed" + userInfo:nil]; + @throw exception; + } + char *str; + for (int i = 0; i < [array count]; i++) { + str = (char *) [array[i] UTF8String]; + if (str == NULL) { + NSException *exception = [NSException + exceptionWithName:@"NULLStringException" + reason:@"UTF8String was NULL" + userInfo:nil]; + @throw exception; + } + if (asprintf(&cArray[i], "%s", str) == -1) { + for (int j = 0; j < i; j++) { + free(cArray[j]); + } + free(cArray); + NSException *exception = [NSException + exceptionWithName:@"MemoryException" + reason:@"malloc failed" + userInfo:nil]; + @throw exception; + } + } + return cArray; +} + +@implementation NSTask (NSTaskExecveAdditions) + +- (void) replaceThisProcess { + char **args = toCArray([@[[self launchPath]] arrayByAddingObjectsFromArray:[self arguments]]); + + NSMutableArray *env = [[NSMutableArray alloc] init]; + NSDictionary* environvment = [[NSProcessInfo processInfo] environment]; + for (NSString* key in environvment) { + NSString* environmentVariable = [[key stringByAppendingString:@"="] stringByAppendingString:environvment[key]]; + [env addObject:environmentVariable]; + } + + char** envp = toCArray(env); + // `execve` replaces the current process with `path`. + // It will only return if it fails to replace the current process. + chdir(dirname(args[0])); + execve(args[0], (char * const *)args, envp); + + // If we're here `execve` failed. :( + for (int i = 0; i < [[self arguments] count]; i++) { + free((void *) args[i]); + } + free((void *) args); + + NSException *exception = [NSException + exceptionWithName:@"ExecveException" + reason:[NSString stringWithFormat:@"couldn't execve: %s", strerror(errno)] + userInfo:nil]; + @throw exception; +} + +@end