Use execve to "launch" Interface on macOS

Co-authored-by: dante ruiz <dante@highfidelity.io>
This commit is contained in:
Matt Hardcastle 2019-08-03 14:07:25 -07:00
parent a097d567ca
commit cff4f9da19
4 changed files with 102 additions and 16 deletions

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSTask (NSTaskExecveAdditions)
- (void) replaceThisProcess;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,73 @@
#import "NSTask+NSTaskExecveAdditions.h"
#import <libgen.h>
char **
toCArray(NSArray<NSString *> *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