Skip to content

Commit

Permalink
Exec julia to determine its version
Browse files Browse the repository at this point in the history
When a julia executable is found, if it's not in a framework then there
is no way to know what version it is.  To determine the version, run a
simple julia expression to print the version.  Of course, running any
random executable named 'Julia' isn't smart so the executable is instead
run inside an App Sandbox with mostly restricted permissions.  The only
special permission granted to the sandbox is the read/exec '/'
permission to allow the executable to be read and executed.
  • Loading branch information
slarew authored and staticfloat committed Apr 16, 2019
1 parent 57367d5 commit bb9310d
Show file tree
Hide file tree
Showing 10 changed files with 538 additions and 28 deletions.
12 changes: 12 additions & 0 deletions contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http:https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.temporary-exception.files.absolute-path.read-only</key>
<array>
<string>/</string>
</array>
</dict>
</plist>
7 changes: 7 additions & 0 deletions contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

@import Foundation;
#import "ExecSandboxProtocol.h"

@interface ExecSandbox : NSObject <ExecSandboxProtocol>
@end
149 changes: 149 additions & 0 deletions contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

#import "ExecSandbox.h"

@class JuliaTask;

@interface ExecSandbox () {
NSMutableArray<JuliaTask *> *_Nonnull _tasks;
}
- (void)taskTerminated:(JuliaTask *_Nonnull)jt;
@end

@interface JuliaTask : NSObject <TaskProtocol> {
ExecSandbox *__weak _delegate;
dispatch_block_t _Nullable _onCleanup;
NSTask *_Nonnull _task;
}
@end

@implementation JuliaTask

- (instancetype)initWithTask:(NSTask *_Nonnull)task
delegate:(ExecSandbox *)d
cleanup:(dispatch_block_t)onCleanup {
self = [super init];
if (self == nil) {
return nil;
}
_delegate = d;
_onCleanup = onCleanup;
_task = task;
return self;
}

- (void)launch:(void (^_Nullable)(int status))onTermination {
dispatch_block_t onCleanup = _onCleanup;
JuliaTask __weak *weakSelf = self;
_task.terminationHandler = ^(NSTask *_Nonnull t) {
if (onTermination != nil) {
onTermination(t.terminationStatus);
}
if (onCleanup != nil) {
onCleanup();
}
JuliaTask *strongSelf = weakSelf;
if (strongSelf) {
[strongSelf->_delegate taskTerminated:strongSelf];
}
};
@try {
[_task launch];
} @catch (NSException *exception) {
NSLog(@"NSTask launch exception: %@", exception);
}
}

- (void)terminate {
@try {
[_task terminate];
} @catch (NSException *exception) {
NSLog(@"NSTask terminate exception: %@", exception);
}
}

@end

@implementation ExecSandbox

- (instancetype)init {
self = [super init];
if (self == nil) {
return nil;
}
_tasks = [[NSMutableArray alloc] init];
return self;
}

- (void)eval:(NSString *)p
withJulia:(NSData *)executableBookmark
arguments:(NSArray<NSString *> *)baseArgs
task:(void (^)(id<TaskProtocol> task, NSFileHandle *stdIn,
NSFileHandle *stdOut, NSFileHandle *stdErr))reply {

NSURL *executableURL =
[NSURL URLByResolvingBookmarkData:executableBookmark
options:NSURLBookmarkResolutionWithoutUI
relativeToURL:nil
bookmarkDataIsStale:nil
error:nil];
if (executableURL == nil) {
reply(nil, nil, nil, nil);
return;
}

for (NSString *arg in baseArgs) {
if ([arg isEqual:@"--"]) {
reply(nil, nil, nil, nil);
return;
}
}

NSURL *temporaryDirectoryURL = [NSURL fileURLWithPath:NSTemporaryDirectory()
isDirectory:YES];
NSString *temporaryFilename =
[[NSProcessInfo processInfo] globallyUniqueString];
NSURL *temporaryFileURL =
[temporaryDirectoryURL URLByAppendingPathComponent:temporaryFilename
isDirectory:false];

[[p dataUsingEncoding:NSUTF8StringEncoding] writeToURL:temporaryFileURL
atomically:false];

NSMutableArray<NSString *> *args = [[NSMutableArray alloc] init];
[args addObjectsFromArray:baseArgs];
[args addObjectsFromArray:@[ @"--", temporaryFileURL.path ]];

NSPipe *stdIn = [NSPipe pipe], *stdOut = [NSPipe pipe],
*stdErr = [NSPipe pipe];

NSTask *t = [[NSTask alloc] init];
if (@available(macOS 10.13, *)) {
t.executableURL = executableURL;
} else {
t.launchPath = executableURL.path;
}
t.arguments = args;
t.standardInput = stdIn;
t.standardOutput = stdOut;
t.standardError = stdErr;

JuliaTask *jt =
[[JuliaTask alloc] initWithTask:t
delegate:self
cleanup:^() {
[[NSFileManager defaultManager]
removeItemAtURL:temporaryDirectoryURL
error:nil];
}];
[_tasks addObject:jt];

reply(jt, stdIn.fileHandleForWriting, stdOut.fileHandleForReading,
stdErr.fileHandleForReading);
}

- (void)taskTerminated:(JuliaTask *_Nonnull)jt {
[_tasks removeObject:jt];
}

@end
31 changes: 31 additions & 0 deletions contrib/mac/frameworkapp/ExecSandbox/ExecSandboxProtocol.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

@import Foundation;

@protocol TaskProtocol
/// Launch the task and upon termination receive the exit status.
- (void)launch:(void (^_Nullable)(int status))onTermination;
/// Terminate (SIGTERM) the task.
- (void)terminate;
@end

@protocol ExecSandboxProtocol
/**
Evaluate a Julia program with a Julia executable.
@param juliaProgram Julia source code to be evaluated.
@param executableBookmark NSURL file bookmark for the julia executable to run.
@param args Arguments to pass to julia.
@param reply Async result with task and standard in, out, and error. An error
occured if task is nil.
*/
- (void)eval:(NSString *_Nonnull)juliaProgram
withJulia:(NSData *_Nonnull)executableBookmark
arguments:(NSArray<NSString *> *_Nullable)args
task:(void (^_Nonnull)(id<TaskProtocol> _Nullable task,
NSFileHandle *_Nullable stdIn,
NSFileHandle *_Nullable stdOut,
NSFileHandle *_Nullable stdErr))reply;
@end

NSXPCInterface *_Nonnull CreateExecSandboxXPCInterface(void);
14 changes: 14 additions & 0 deletions contrib/mac/frameworkapp/ExecSandbox/ExecSandboxProtocol.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

#import "ExecSandboxProtocol.h"

NSXPCInterface *CreateExecSandboxXPCInterface(void) {
NSXPCInterface *i =
[NSXPCInterface interfaceWithProtocol:@protocol(ExecSandboxProtocol)];
/// Reply sends a task proxy:
[i setInterface:[NSXPCInterface interfaceWithProtocol:@protocol(TaskProtocol)]
forSelector:@selector(eval:withJulia:arguments:task:)
argumentIndex:0
ofReply:true];
return i;
}
31 changes: 31 additions & 0 deletions contrib/mac/frameworkapp/ExecSandbox/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http:https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>ExecSandbox</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>$(APP_SHORT_VERSION_STRING)</string>
<key>CFBundleVersion</key>
<string>$(APP_VERSION)</string>
<key>XPCService</key>
<dict>
<key>RunLoopType</key>
<string>NSRunLoop</string>
<key>ServiceType</key>
<string>Application</string>
</dict>
</dict>
</plist>
28 changes: 28 additions & 0 deletions contrib/mac/frameworkapp/ExecSandbox/main.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

@import Foundation;
#import "ExecSandbox.h"

@interface ServiceDelegate : NSObject <NSXPCListenerDelegate>
@end

@implementation ServiceDelegate

- (BOOL)listener:(NSXPCListener *)listener
shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
newConnection.exportedInterface = CreateExecSandboxXPCInterface();
ExecSandbox *exportedObject = [[ExecSandbox alloc] init];
newConnection.exportedObject = exportedObject;
[newConnection resume];
return YES;
}

@end

int main(int argc, const char *argv[]) {
ServiceDelegate *delegate = [[ServiceDelegate alloc] init];
NSXPCListener *listener = [NSXPCListener serviceListener];
listener.delegate = delegate;
[listener resume];
return 0;
}
Loading

0 comments on commit bb9310d

Please sign in to comment.