From 3c0a8cf4abea0ab3dbacc758431063e7ff1ef0ab Mon Sep 17 00:00:00 2001 From: meeh Date: Thu, 20 Sep 2018 02:38:44 +0000 Subject: [PATCH] Mac OSX Launcher: A lot of bugfixes, refactoring and cleanup. --- .../I2PLauncher/SwiftMainDelegate.swift | 17 +- .../I2PLauncher/routermgmt/DetectJava.swift | 20 +- .../routermgmt/RouterProcessStatus.swift | 2 +- .../userinterface/PopoverViewController.swift | 15 +- launchers/macosx/Info.plist | 2 +- launchers/macosx/RouterTask.h | 17 +- launchers/macosx/RouterTask.mm | 17 -- launchers/macosx/include/strutil.hpp | 4 +- launchers/macosx/main.mm | 178 +++++------------- 9 files changed, 95 insertions(+), 177 deletions(-) diff --git a/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift b/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift index 9415a698b8..b5b60bf1c1 100644 --- a/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift +++ b/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift @@ -13,18 +13,19 @@ import Cocoa //let statusItem = NSStatusBar.system().statusItem(withLength: NSSquareStatusItemLength ) let statusBarController = StatusBarController() - let javaDetector = DetectJava() + static let javaDetector = DetectJava() static let objCBridge = SBridge() override init() { super.init() - - self.javaDetector.findIt() - if (!javaDetector.isJavaFound()) { - print("Could not find java....") - terminate("No java..") + if (!SwiftMainDelegate.javaDetector.isJavaFound()) { + SwiftMainDelegate.javaDetector.findIt() + if (!SwiftMainDelegate.javaDetector.isJavaFound()) { + print("Could not find java....") + terminate("No java..") + } } - let javaBinPath = self.javaDetector.javaHome + let javaBinPath = SwiftMainDelegate.javaDetector.javaHome RouterProcessStatus.knownJavaBinPath = javaBinPath print("Found java home = ", javaBinPath) @@ -77,8 +78,6 @@ import Cocoa var i2pPath = NSHomeDirectory() i2pPath += "/Library/I2P" - //let javaBinPath = self.javaDetector.javaHome.replace(target: " ", withString: "\\ ") - let fileManager = FileManager() var ok = ObjCBool(true) let doesI2PDirExists = fileManager.fileExists(atPath: i2pPath, isDirectory: &ok) diff --git a/launchers/macosx/I2PLauncher/routermgmt/DetectJava.swift b/launchers/macosx/I2PLauncher/routermgmt/DetectJava.swift index f70d20f8de..186318e092 100644 --- a/launchers/macosx/I2PLauncher/routermgmt/DetectJava.swift +++ b/launchers/macosx/I2PLauncher/routermgmt/DetectJava.swift @@ -10,9 +10,9 @@ import Foundation @objc class DetectJava : NSObject { - var hasJRE : Bool = false - var userWantJRE : Bool = false - var userAcceptOracleEULA : Bool = false + static var hasJRE : Bool = false + static var userWantJRE : Bool = false + static var userAcceptOracleEULA : Bool = false override init() { @@ -29,8 +29,9 @@ import Foundation //Called after the change didSet{ - hasJRE = true - print("DetectJava.javaHome did change from "+oldValue+" to "+self.javaHome) + 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) } }; private var testedEnv : Bool = false @@ -50,25 +51,28 @@ import Foundation * **/ @objc func findIt() { + if (DetectJava.hasJRE) { + return + } print("Start with checking environment variable") self.checkJavaEnvironmentVariable() if !(self.javaHome.isEmpty) { RouterProcessStatus.knownJavaBinPath = Optional.some(self.javaHome) - hasJRE = true + DetectJava.hasJRE = true return } print("Checking with the java_home util") self.runJavaHomeCmd() if !(self.javaHome.isEmpty) { RouterProcessStatus.knownJavaBinPath = Optional.some(self.javaHome) - hasJRE = true + DetectJava.hasJRE = true return } print("Checking default JRE install path") self.checkDefaultJREPath() if !(self.javaHome.isEmpty) { RouterProcessStatus.knownJavaBinPath = Optional.some(self.javaHome) - hasJRE = true + DetectJava.hasJRE = true return } } diff --git a/launchers/macosx/I2PLauncher/routermgmt/RouterProcessStatus.swift b/launchers/macosx/I2PLauncher/routermgmt/RouterProcessStatus.swift index 7fe785bfc5..0ee054ae40 100644 --- a/launchers/macosx/I2PLauncher/routermgmt/RouterProcessStatus.swift +++ b/launchers/macosx/I2PLauncher/routermgmt/RouterProcessStatus.swift @@ -26,7 +26,7 @@ import AppKit } @objc func getRouterIsRunning() -> Bool { - if (RouterProcessStatus.isRouterRunning == Optional.none) { + if (RouterProcessStatus.isRouterRunning) { return false; } else { let running: Bool = RouterProcessStatus.isRouterRunning diff --git a/launchers/macosx/I2PLauncher/userinterface/PopoverViewController.swift b/launchers/macosx/I2PLauncher/userinterface/PopoverViewController.swift index f81651abff..4980326c30 100644 --- a/launchers/macosx/I2PLauncher/userinterface/PopoverViewController.swift +++ b/launchers/macosx/I2PLauncher/userinterface/PopoverViewController.swift @@ -60,7 +60,7 @@ class PopoverViewController: NSViewController { override func viewWillDraw() { super.viewWillDraw() - if (RouterStatusView.instance == Optional.none) { + if (RouterStatusView.instance != nil) { RouterStatusView.instance = self } self.setRouterStatusLabelText() @@ -78,16 +78,13 @@ class PopoverViewController: NSViewController { routerStartStopButton?.target = self quickControlView?.needsDisplay = true - if (RouterProcessStatus.routerVersion == Optional.none) { - routerVersionLabel?.cell?.stringValue = "Router version: Still unknown" - // trigger a read to ensure values - let tmp = SwiftMainDelegate() - tmp.findInstalledI2PVersion() + if let version = RouterProcessStatus.routerVersion { + routerVersionLabel?.cell?.stringValue = "Router version: " + version } else { - routerVersionLabel?.cell?.stringValue = "Router version: " + RouterProcessStatus.routerVersion! + routerVersionLabel?.cell?.stringValue = "Router version: Still unknown" } - if (RouterProcessStatus.routerStartedAt != Optional.none) { - routerUptimeLabel?.cell?.stringValue = "Router has runned for " + DateTimeUtils.timeAgoSinceDate(date: NSDate(date: RouterProcessStatus.routerStartedAt!), numericDates: false) + if let routerStartTime = RouterProcessStatus.routerStartedAt { + routerUptimeLabel?.cell?.stringValue = "Router has runned for " + DateTimeUtils.timeAgoSinceDate(date: NSDate(date: routerStartTime), numericDates: false) } } diff --git a/launchers/macosx/Info.plist b/launchers/macosx/Info.plist index b63c2dbba9..f07062c055 100644 --- a/launchers/macosx/Info.plist +++ b/launchers/macosx/Info.plist @@ -9,7 +9,7 @@ NSHumanReadableCopyright Public Domain CFBundleGetInfoString - 0.9.35-experimental + 0.9.36-experimental CFBundleIconFile images/AppIcon.icns CFBundleIdentifier diff --git a/launchers/macosx/RouterTask.h b/launchers/macosx/RouterTask.h index e81cd924c7..3b8e7db184 100644 --- a/launchers/macosx/RouterTask.h +++ b/launchers/macosx/RouterTask.h @@ -16,7 +16,21 @@ class JavaRunner; typedef std::function fp_t; typedef std::function fp_proc_t; +const std::vector defaultStartupFlags { + @"-Xmx512M", + @"-Xms128m", + @"-Djava.awt.headless=true", + @"-Dwrapper.logfile=/tmp/router.log", + @"-Dwrapper.logfile.loglevel=DEBUG", + @"-Dwrapper.java.pidfile=/tmp/routerjvm.pid", + @"-Dwrapper.console.loglevel=DEBUG" +}; +const std::vector defaultFlagsForExtractorJob { + "-Xmx512M", + "-Xms128m", + "-Djava.awt.headless=true" +}; /** * @@ -30,9 +44,6 @@ public: JavaRunner(std::string& javaBin, std::string& arguments, std::string& i2pBaseDir, const fp_proc_t& executingFn, const fp_t& cb); ~JavaRunner() = default; - static const std::vector defaultStartupFlags; - static const std::vector defaultFlagsForExtractorJob; - void requestRouterShutdown(); std::future execute(); diff --git a/launchers/macosx/RouterTask.mm b/launchers/macosx/RouterTask.mm index 80a7a2956f..51b955d50d 100644 --- a/launchers/macosx/RouterTask.mm +++ b/launchers/macosx/RouterTask.mm @@ -112,23 +112,6 @@ #ifdef __cplusplus - -const std::vector JavaRunner::defaultStartupFlags { - @"-Xmx512M", - @"-Xms128m", - @"-Djava.awt.headless=true", - @"-Dwrapper.logfile=/tmp/router.log", - @"-Dwrapper.logfile.loglevel=DEBUG", - @"-Dwrapper.java.pidfile=/tmp/routerjvm.pid", - @"-Dwrapper.console.loglevel=DEBUG" -}; - -const std::vector JavaRunner::defaultFlagsForExtractorJob { - "-Xmx512M", - "-Xms128m", - "-Djava.awt.headless=true" -}; - JavaRunner::JavaRunner(std::string& javaBin, std::string& arguments, std::string& i2pBaseDir, const fp_proc_t& execFn, const fp_t& cb) : javaBinaryPath(javaBin), javaRouterArgs(arguments), _i2pBaseDir(i2pBaseDir), executingFn(execFn), exitCallbackFn(cb) { diff --git a/launchers/macosx/include/strutil.hpp b/launchers/macosx/include/strutil.hpp index de60853c58..ab52a1c01c 100644 --- a/launchers/macosx/include/strutil.hpp +++ b/launchers/macosx/include/strutil.hpp @@ -98,10 +98,10 @@ static inline std::string trim_copy(std::string s) { return s; } -#ifdef CPP17 +#if __cplusplus > 201402L -using std::experimental::optional; +using std::optional; // Use CFStringRef instead of NSString*, otherwise disable ARC inline optional optionalString(bool val) { diff --git a/launchers/macosx/main.mm b/launchers/macosx/main.mm index 2456ccd891..ba21e50d7e 100644 --- a/launchers/macosx/main.mm +++ b/launchers/macosx/main.mm @@ -33,46 +33,16 @@ #include "JavaHelper.h" #include "include/fn.h" #include "include/portcheck.h" +#import "SBridge.h" #ifdef __cplusplus -#import "SBridge.h" +#include #include "include/subprocess.hpp" #include "include/strutil.hpp" using namespace subprocess; -JvmListSharedPtr gRawJvmList = nullptr; - -maybeAnRouterRunner getGlobalRouterObject() -{ - std::lock_guard lock(globalRouterStatusMutex); - return globalRouterStatus; // Remember this might be nullptr now. -} - -void setGlobalRouterObject(I2PRouterTask* newRouter) -{ - std::lock_guard lock(globalRouterStatusMutex); - globalRouterStatus = newRouter; -} - - -pthread_mutex_t mutex; - -bool getGlobalRouterIsRunning() -{ - pthread_mutex_lock(&mutex); - bool current = isRuterRunning; - pthread_mutex_unlock(&mutex); - return current; -} -void setGlobalRouterIsRunning(bool running) -{ - pthread_mutex_lock(&mutex); - isRuterRunning = running; - pthread_mutex_unlock(&mutex); -} - #endif #define debug(format, ...) CFShow([NSString stringWithFormat:format, ## __VA_ARGS__]); @@ -90,6 +60,12 @@ void setGlobalRouterIsRunning(bool running) - (void) awakeFromNib { } + +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center + shouldPresentNotification:(NSUserNotification *)notification { + return YES; +} + #ifdef __cplusplus - (void)extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion @@ -125,7 +101,7 @@ void setGlobalRouterIsRunning(bool running) // Create directory mkdir(basePath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR); - auto cli = JavaRunner::defaultFlagsForExtractorJob; + auto cli = defaultFlagsForExtractorJob; setenv("I2PBASE", basePath.c_str(), true); setenv("ZIPPATH", zippath.c_str(), true); //setenv("DYLD_LIBRARY_PATH",".:/usr/lib:/lib:/usr/local/lib", true); @@ -143,19 +119,8 @@ void setGlobalRouterIsRunning(bool running) NSString* newString = [NSString stringWithFormat:@"file://%@", rs.getJavaHome]; NSURL *baseURL = [NSURL fileURLWithPath:newString]; - NSLog(@"MEEH URL PATH: %s", [baseURL fileSystemRepresentation]); - - auto charCli = map(cli, [](std::string str){ return str.c_str(); }); std::string execStr = std::string([rs.getJavaHome UTF8String]); - // TODO: Cheap hack, make it better. - replace(execStr, "Internet Plug-Ins", "Internet\\ Plug-Ins"); - replace(execStr, "\n", ""); - NSLog(@"Java path1 = %s", execStr.c_str()); - [rs setJavaHome: [NSString stringWithFormat:@"%s", execStr.c_str()]]; for_each(cli, [&execStr](std::string str){ execStr += std::string(" ") + str; }); - - //execStr = replace(execStr, "\\\\ ", "\\ "); - //NSLog(@"Java path2 = %s", execStr.c_str()); NSLog(@"Trying cmd: %@", [NSString stringWithUTF8String:execStr.c_str()]); try { @@ -197,15 +162,6 @@ void setGlobalRouterIsRunning(bool running) } -#endif - -- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center - shouldPresentNotification:(NSUserNotification *)notification { - return YES; -} - - -#ifdef __cplusplus - (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList { @@ -227,33 +183,19 @@ void setGlobalRouterIsRunning(bool running) return userResult; } - -- (void)userChooseJavaHome { - listAllJavaInstallsAvailable(); - std::shared_ptr appContext = std::shared_ptr( new JvmHomeContext() ); - for (auto item : *appContext->getJvmList()) { - printf("JVM %s (Version: %s, Directory: %s)\n", item->JVMName.c_str(), item->JVMPlatformVersion.c_str(), item->JVMHomePath.c_str()); - } - JvmListPtr rawJvmList = appContext->getJvmList(); - NSString * userJavaHome = [self userSelectJavaHome: rawJvmList]; - // TODO: Add logic so user can set preferred JVM -} - -#endif - - (void)setApplicationDefaultPreferences { auto defaultJVMHome = check_output({"/usr/libexec/java_home","-v",DEF_MIN_JVM_VER}); auto tmpStdStr = std::string(defaultJVMHome.buf.data()); trim(tmpStdStr); auto cfDefaultHome = CFStringCreateWithCString(NULL, const_cast(tmpStdStr.c_str()), kCFStringEncodingUTF8); - /*[self.userPreferences registerDefaults:@{ + [self.userPreferences registerDefaults:@{ @"javaHome" : (NSString *)cfDefaultHome, @"lastI2PVersion" : (NSString *)CFSTR(DEF_I2P_VERSION), @"enableLogging": @YES, @"enableVerboseLogging": @YES, @"autoStartRouter": @YES, @"i2pBaseDirectory": (NSString *)CFStringCreateWithCString(NULL, const_cast(getDefaultBaseDir().c_str()), kCFStringEncodingUTF8) - }];*/ + }]; if (self.enableVerboseLogging) NSLog(@"Default JVM home preference set to: %@", cfDefaultHome); auto dict = [self.userPreferences dictionaryRepresentation]; @@ -263,53 +205,40 @@ void setGlobalRouterIsRunning(bool running) CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); if (self.enableVerboseLogging) NSLog(@"Default preferences stored!"); +#endif } + + - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // Init application here self.swiftRuntime = [[SwiftMainDelegate alloc] init]; + // This setup allows the application to send notifications [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self]; + + // Start with user preferences self.userPreferences = [NSUserDefaults standardUserDefaults]; [self setApplicationDefaultPreferences]; self.enableLogging = [self.userPreferences boolForKey:@"enableLogging"]; self.enableVerboseLogging = [self.userPreferences boolForKey:@"enableVerboseLogging"]; - - -#ifdef __cplusplus - gRawJvmList = std::make_shared >(std::list()); -#endif // In case we are unbundled, make us a proper UI application [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; [NSApp activateIgnoringOtherApps:YES]; - // TODO: Also check for new installations from time to time. - + #ifdef __cplusplus - auto javaHomePref = [self.userPreferences stringForKey:@"javaHome"]; - if (self.enableVerboseLogging) - { - NSLog(@"Java home from preferences: %@", javaHomePref); - } + //gRawJvmList = std::make_shared >(std::list()); - if (self.enableVerboseLogging) - { - NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; - NSLog(@"Appdomain is: %@", appDomain); - } - - NSLog(@"We should have started the statusbar object by now..."); RouterProcessStatus* routerStatus = [[RouterProcessStatus alloc] init]; - std::string i2pBaseDir(getDefaultBaseDir()); - - auto pref = self.userPreferences; - + NSLog(@"i2pBaseDir = %s", i2pBaseDir.c_str()); bool shouldAutoStartRouter = false; - + + // TODO: Make the port a setting which defaults to 7657 if (port_check(7657) != 0) { NSLog(@"Seems i2p is already running - I will not start the router (port 7657 is in use..)"); @@ -324,44 +253,26 @@ void setGlobalRouterIsRunning(bool running) if (self.enableVerboseLogging) NSLog(@"processinfo %@", [[NSProcessInfo processInfo] arguments]); - auto getJavaBin = [&pref,&self]() -> std::string { - // Get Java home - /*NSString* val = @""; - val = [pref stringForKey:@"javaHome"]; - if (val == NULL) val = @""; - if (self.enableVerboseLogging) NSLog(@"Javahome: %@", val); - auto javaHome = std::string([val UTF8String]); - //trim(javaHome); // Trim to remove endline - auto javaBin = std::string(javaHome); - javaBin += "/bin/java"; // Append java binary to path. - return javaBin;*/ - DetectJava *dt = [[DetectJava alloc] init]; - [dt findIt]; - if ([dt isJavaFound]) { - return [dt.javaHome UTF8String]; - } else { - throw new std::runtime_error("Java home fatal error"); - } - }; - NSBundle *launcherBundle = [NSBundle mainBundle]; - auto jarResPath = [launcherBundle pathForResource:@"launcher" ofType:@"jar"]; - NSLog(@"Trying to load launcher.jar from url = %@", jarResPath); - + // Helper object to hold statefull path information self.metaInfo = [[ExtractMetaInfo alloc] init]; - //self.metaInfo.i2pBase = [NSString stringWithUTF8String:i2pBaseDir.c_str()]; - self.metaInfo.javaBinary = [NSString stringWithUTF8String:getJavaBin().c_str()]; + self.metaInfo.i2pBase = [NSString stringWithUTF8String:i2pBaseDir.c_str()]; + self.metaInfo.javaBinary = [routerStatus getJavaHome]; self.metaInfo.jarFile = [launcherBundle pathForResource:@"launcher" ofType:@"jar"]; self.metaInfo.zipFile = [launcherBundle pathForResource:@"base" ofType:@"zip"]; std::string basearg("-Di2p.dir.base="); - //basearg += i2pBaseDir; + basearg += i2pBaseDir; std::string jarfile("-cp "); jarfile += [self.metaInfo.zipFile UTF8String]; + auto sBridge = [[SBridge alloc] init]; + + // Initialize the Swift environment (the UI components) + [self.swiftRuntime applicationDidFinishLaunching]; struct stat sb; if ( !(stat(i2pBaseDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) ) @@ -369,17 +280,25 @@ void setGlobalRouterIsRunning(bool running) // I2P is not extracted. if (self.enableVerboseLogging) NSLog(@"I2P Directory don't exists!"); + // Might be hard to read if you're not used to Objective-C + // But this is a "function call" that contains a "callback function" [self extractI2PBaseDir:^(BOOL success, NSError *error) { sendUserNotification(@"I2P is done extracting", @"I2P is now installed and ready to run!"); - [self.swiftRuntime applicationDidFinishLaunching]; NSLog(@"Done extracting I2P"); - if (shouldAutoStartRouter) [self startupI2PRouter]; + + if (shouldAutoStartRouter) { + [sBridge startupI2PRouter:self.metaInfo.i2pBase javaBinPath:self.metaInfo.javaBinary]; + [routerStatus setRouterRanByUs: true]; + } }]; } else { - if (self.enableVerboseLogging) NSLog(@"I2P directory found!"); - if (shouldAutoStartRouter) [self startupI2PRouter]; - [self.swiftRuntime applicationDidFinishLaunching]; + // I2P was already found extracted + + if (shouldAutoStartRouter) { + [sBridge startupI2PRouter:self.metaInfo.i2pBase javaBinPath:self.metaInfo.javaBinary]; + [routerStatus setRouterRanByUs: true]; + } } #endif @@ -411,15 +330,20 @@ void setGlobalRouterIsRunning(bool running) int main(int argc, const char **argv) { NSApplication *app = [NSApplication sharedApplication]; - //NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; AppDelegate *appDelegate = [[AppDelegate alloc] initWithArgc:argc argv:argv]; app.delegate = appDelegate; + auto mainBundle = [NSBundle mainBundle]; + NSString* stringNameBundle = [mainBundle objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey]; + if ([[NSRunningApplication runningApplicationsWithBundleIdentifier:[mainBundle bundleIdentifier]] count] > 1) { + [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Another copy of %@ is already running.",stringNameBundle] + defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"This copy will now quit."] runModal]; + + [NSApp terminate:nil]; + } [NSBundle loadNibNamed:@"I2Launcher" owner:NSApp]; [NSApp run]; - // Handle any errors - //[pool drain]; return 0; }