Mac OS X Launcher:

* Enabled Apple's "Hardened Runtime", however unsecure memory had to be allowed to spawn java etc.
  * Updated docs about Event Manager code
  * Make the launcher handle cases where extract is incomplete or invalid
  * Bugfixes as always
This commit is contained in:
meeh
2018-09-26 20:42:58 +00:00
parent d27000ec07
commit 22a0f396e6
8 changed files with 123 additions and 50 deletions

View File

@@ -59,10 +59,18 @@ import Cocoa
let sub:Subprocess = Subprocess.init(executablePath: "/bin/sh", arguments: cmdArgs)
let results:ExecutionResult = sub.execute(captureOutput: true)!
if (results.didCaptureOutput) {
let i2pVersion = results.outputLines.first?.replace(target: "I2P Core version: ", withString: "")
NSLog("I2P version detected: %@",i2pVersion ?? "Unknown")
RouterProcessStatus.routerVersion = i2pVersion
RouterManager.shared().eventManager.trigger(eventName: "router_version", information: i2pVersion)
if (results.status == 0) {
let i2pVersion = results.outputLines.first?.replace(target: "I2P Core version: ", withString: "")
NSLog("I2P version detected: %@",i2pVersion ?? "Unknown")
RouterProcessStatus.routerVersion = i2pVersion
RouterManager.shared().eventManager.trigger(eventName: "router_version", information: i2pVersion)
RouterManager.shared().eventManager.trigger(eventName: "router_can_start", information: i2pVersion)
} else {
NSLog("Non zero exit code from subprocess while trying to detect version number!")
for line in results.errorsLines {
NSLog(line)
}
}
} else {
print("Warning: Version Detection did NOT captured output")
}

View File

@@ -32,6 +32,7 @@ import Foundation
DetectJava.hasJRE = true
self.javaHome = self.javaHome.replace(target: "\n", withString: "").replace(target: "Internet Plug-Ins", withString: "Internet\\ Plug-Ins")
print("DetectJava.javaHome did change to "+self.javaHome)
RouterManager.shared().eventManager.trigger(eventName: "java_found", information: self.javaHome)
}
};
private var testedEnv : Bool = false

View File

@@ -8,6 +8,13 @@
import Foundation
enum ErrorsInRouterMgmr: Swift.Error {
case NoJavaFound
case InvalidVersion
case NotFound
}
class RouterManager : NSObject {
// MARK: - Properties
@@ -18,6 +25,10 @@ class RouterManager : NSObject {
var logViewStorage: NSTextStorage?
private static func handleRouterException(information:Any?) {
NSLog("event! - handle router exception")
NSLog(information as! String)
}
private static func handleRouterStart(information:Any?) {
NSLog("event! - handle router start")
RouterProcessStatus.routerStartedAt = Date()
@@ -31,17 +42,32 @@ class RouterManager : NSObject {
RouterProcessStatus.isRouterRunning = false
}
private static func handleRouterPid(information:Any?) {
Swift.print("event! - handle router pid: %s", information ?? "")
Swift.print("event! - handle router pid: ", information ?? "")
}
private static func handleRouterVersion(information:Any?) {
Swift.print("event! - handle router version: %s", information ?? "")
let currentVersion : String = information as! String
if (packedVersion.compare(currentVersion, options: .numeric) == .orderedDescending) {
Swift.print("event! - router version: Packed version is newer, gonna re-deploy")
RouterManager.shared().eventManager.trigger(eventName: "router_must_upgrade", information: "got new version")
} else {
Swift.print("event! - router version: No update needed")
RouterManager.shared().eventManager.trigger(eventName: "router_can_start", information: "all ok")
do {
Swift.print("event! - handle router version: ", information ?? "")
guard let currentVersion : String = information as? String else {
throw ErrorsInRouterMgmr.InvalidVersion
}
if (currentVersion == "Unknown") {
throw ErrorsInRouterMgmr.InvalidVersion
}
if (packedVersion.compare(currentVersion, options: .numeric) == .orderedDescending) {
Swift.print("event! - router version: Packed version is newer, gonna re-deploy")
RouterManager.shared().eventManager.trigger(eventName: "router_must_upgrade", information: "got new version")
} else {
Swift.print("event! - router version: No update needed")
RouterManager.shared().eventManager.trigger(eventName: "router_can_start", information: "all ok")
}
} catch ErrorsInRouterMgmr.InvalidVersion {
// This is most likely due to an earlier extract got killed halfway or something
// Trigger an update
RouterManager.shared().eventManager.trigger(eventName: "router_must_upgrade", information: "invalid version found")
} catch {
// WTF
NSLog("Fatal error in RouterManager");
print(error)
}
}
@@ -57,6 +83,7 @@ class RouterManager : NSObject {
routerManager.eventManager.listenTo(eventName: "router_stop", action: handleRouterStop)
routerManager.eventManager.listenTo(eventName: "router_pid", action: handleRouterPid)
routerManager.eventManager.listenTo(eventName: "router_version", action: handleRouterVersion)
routerManager.eventManager.listenTo(eventName: "router_exception", action: handleRouterException)
return routerManager
}()

View File

@@ -1,5 +1,24 @@
# The Mac OS X Launcher
## The Event Manager
This is some Swift code which makes the application use events to signal the different compoents of the application. This seems to be the cleanest way since we're doing java subprocesses and so on. This can also be named a publish-subscribe system as well. If you're familiar with Javascript it would be a handy skill.
### Event Overview
| Event name | Details sent as arguments | Description |
| ------------- | ------------- | ------------- |
| router_start | nothing | This event get triggered when the router starts |
| router_exception | exception message | This will be triggered in case something within the functions that start the java (router) subprocess throws an exception |
| java_found | the location | This will be triggered once the DetectJava swift class has found java |
| router_must_upgrade | nothing | This will be triggered if no I2P is found, or if it's failing to get the version from the jar file |
| extract_completed | nothing | This is triggered when the deployment process is done extracting I2P to it's directory |
| router_can_start | nothing | This event will be triggered when I2P is found and a version number has been found, router won't start before this event |
| router_stop | error if any | Triggered when the router subprocess exits |
| router_pid | the pid number as string | Triggered when we know the pid of the router subprocess |
| router_version | the version string | Triggered when we have successfully extracted current I2P version |
| extract_errored | the error message | Triggered if the process didn't exit correctly |
## Misc
**Note** this project is WIP, cause Meeh has yet to merge in Obj-C/Swift code for GUI stuff in OSX.

View File

@@ -85,9 +85,7 @@
NSLog(@"Expection occurred %@", [e reason]);
auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
self.isRouterRunning = NO;
[swiftRouterStatus setRouterStatus: false];
[swiftRouterStatus setRouterRanByUs: false];
[swiftRouterStatus triggerEventWithEn:@"router_stop" details:@"error shutdown"];
[swiftRouterStatus triggerEventWithEn:@"router_exception" details:[e reason]];
[[SBridge sharedInstance] setCurrentRouterInstance:nil];
sendUserNotification(@"An error occured, can't start the I2P Router", [e reason]);
return 0;

View File

@@ -36,16 +36,15 @@ std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments,
[[SBridge sharedInstance] setCurrentRouterInstance:instance];
[instance execute];
sendUserNotification(APP_IDSTR, @"The I2P router is starting up.");
auto pid = [instance getPID];
NSLog(@"Got pid: %d", pid);
if (routerStatus != nil) {
[routerStatus setRouterStatus: true];
[routerStatus setRouterRanByUs: true];
[routerStatus triggerEventWithEn:@"router_start" details:@"normal start"];
[routerStatus triggerEventWithEn:@"router_pid" details:[NSString stringWithFormat:@"%d", pid]];
}
sendUserNotification(APP_IDSTR, @"The I2P router is starting up.");
auto pid = [instance getPID];
NSLog(@"Got pid: %d", pid);
if (routerStatus != nil) [routerStatus triggerEventWithEn:@"router_pid" details:[NSString stringWithFormat:@"%d", pid]];
return std::async(std::launch::async, [&pid]{
return pid;
@@ -59,8 +58,6 @@ std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments,
[[SBridge sharedInstance] setCurrentRouterInstance:nil];
if (routerStatus != nil) {
[routerStatus setRouterStatus: false];
[routerStatus setRouterRanByUs: false];
[routerStatus triggerEventWithEn:@"router_exception" details:errStr];
}

View File

@@ -107,23 +107,16 @@ using namespace subprocess;
for_each(cli, [&execStr](std::string str){ execStr += std::string(" ") + str; });
NSLog(@"Trying cmd: %@", [NSString stringWithUTF8String:execStr.c_str()]);
try {
sendUserNotification(APP_IDSTR, @"Please hold on while we extract I2P. You'll get a new message once done!");
int extractStatus = Popen(execStr.c_str(), environment{{
{"ZIPPATH", zippath.c_str()},
{"I2PBASE", basePath.c_str()}
}}).wait();
NSLog(@"Extraction exit code %@",[NSString stringWithUTF8String:(std::to_string(extractStatus)).c_str()]);
if (extractStatus == 0)
{
NSLog(@"Extraction complete!");
}
} catch (subprocess::OSError &err) {
auto errMsg = [NSString stringWithUTF8String:err.what()];
//success = NO;
NSLog(@"Exception: %@", errMsg);
sendUserNotification(APP_IDSTR, [NSString stringWithFormat:@"Error: %@", errMsg]);
sendUserNotification(APP_IDSTR, @"Please hold on while we extract I2P. You'll get a new message once done!");
int extractStatus = Popen(execStr.c_str(), environment{{
{"ZIPPATH", zippath.c_str()},
{"I2PBASE", basePath.c_str()}
}}).wait();
NSLog(@"Extraction exit code %@",[NSString stringWithUTF8String:(std::to_string(extractStatus)).c_str()]);
if (extractStatus == 0) {
NSLog(@"Extraction process done");
} else {
NSLog(@"Something went wrong");
}
// All done. Assume success and error are already set.
@@ -137,6 +130,7 @@ using namespace subprocess;
} catch (OSError &err) {
auto errMsg = [NSString stringWithUTF8String:err.what()];
NSLog(@"Exception: %@", errMsg);
sendUserNotification(APP_IDSTR, [NSString stringWithFormat:@"Error: %@", errMsg]);
}
});
}
@@ -224,27 +218,52 @@ using namespace subprocess;
// This will trigger the router start after an upgrade.
[routerStatus listenForEventWithEventName:@"router_must_upgrade" callbackActionFn:^(NSString* information) {
NSLog(@"Got signal, router must be upgraded");
NSLog(@"Got signal, router must be deployed from base.zip");
[self extractI2PBaseDir:^(BOOL success, NSError *error) {
sendUserNotification(@"I2P is done extracting", @"I2P is now installed and ready to run!");
NSLog(@"Done extracting I2P");
[routerStatus triggerEventWithEn:@"router_can_start" details:@"upgrade complete"];
if (success && error != nil) {
sendUserNotification(@"I2P is done extracting", @"I2P is now installed and ready to run!");
NSLog(@"Done extracting I2P");
[routerStatus triggerEventWithEn:@"extract_completed" details:@"upgrade complete"];
} else {
NSLog(@"Error while extracting I2P");
[routerStatus triggerEventWithEn:@"extract_errored" details:[NSString stringWithFormat:@"%@", error]];
}
}];
}];
// Initialize the Swift environment (the UI components)
[self.swiftRuntime applicationDidFinishLaunching];
NSString *nsI2PBaseStr = [NSString stringWithUTF8String:i2pBaseDir.c_str()];
struct stat sb;
if ( !(stat(i2pBaseDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) )
//struct stat sb;
//if ( !(stat(i2pBaseDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) )
BOOL shouldBeTrueOnReturnDir = YES;
if (! [NSFileManager.defaultManager fileExistsAtPath: nsI2PBaseStr isDirectory: &shouldBeTrueOnReturnDir])
{
// I2P is not extracted.
if (self.enableVerboseLogging) NSLog(@"I2P Directory don't exists!");
[routerStatus triggerEventWithEn:@"router_must_upgrade" details:@"deploy needed"];
if (shouldBeTrueOnReturnDir) {
if (self.enableVerboseLogging) NSLog(@"I2P Directory don't exists!");
[routerStatus triggerEventWithEn:@"router_must_upgrade" details:@"deploy needed"];
} else {
// TODO: handle if i2p path exists but it's not a dir.
}
} else {
// I2P was already found extracted
NSLog(@"Time to detect I2P version in install directory");
[self.swiftRuntime findInstalledI2PVersion];
NSString *nsI2pJar = [NSString stringWithFormat:@"%@/lib/i2p.jar", nsI2PBaseStr];
// But does I2PBASE/lib/i2p.jar exists?
if ([NSFileManager.defaultManager fileExistsAtPath:nsI2pJar]) {
NSLog(@"Time to detect I2P version in install directory");
[self.swiftRuntime findInstalledI2PVersion];
} else {
// The directory exists, but not i2p.jar - most likely we're in mid-extraction state.
[routerStatus listenForEventWithEventName:@"extract_completed" callbackActionFn:^(NSString* information) {
NSLog(@"Time to detect I2P version in install directory");
[self.swiftRuntime findInstalledI2PVersion];
}];
}
}
#endif

View File

@@ -1,5 +1,7 @@
#!/bin/bash
. .sign-secrets
APP_NAME="I2PLauncher"
VERSION="0.9.36"
DMG_BACKGROUND_IMG="Background.png"
@@ -98,6 +100,8 @@ hdiutil detach "${DEVICE}"
echo "Creating compressed image"
hdiutil convert "${DMG_TMP}" -format UDZO -imagekey zlib-level=9 -o "${DMG_FINAL}"
codesign --force --sign "${APPLE_CODE_SIGNER_ID}" "${DMG_FINAL}"
# clean up
rm -rf "${DMG_TMP}"
rm -rf "${STAGING_DIR}"