diff --git a/launchers/browserbundle/src/main/scala/net/i2p/RouterLauncherApp.scala b/launchers/browserbundle/src/main/scala/net/i2p/RouterLauncherApp.scala index 28b232dd27..092471f717 100644 --- a/launchers/browserbundle/src/main/scala/net/i2p/RouterLauncherApp.scala +++ b/launchers/browserbundle/src/main/scala/net/i2p/RouterLauncherApp.scala @@ -62,7 +62,7 @@ object RouterLauncherApp extends App { val configPath = Option(System.getProperty("i2p.dir.config")).getOrElse(System.getenv("I2PCONFIG")) println(s"basePath => ${basePath}\nconfigPath => ${configPath}") - +/* object ErrorUtils { def errorMessageInJson(message: String, solution: String) : JObject = JObject( List( @@ -91,7 +91,7 @@ object RouterLauncherApp extends App { if (!new File(basePath).exists()) ErrorUtils.printErrorAndExit("I2P Base path don't exist", "Reinstall the Browser Bundle") if (!new File(configPath).exists()) ErrorUtils.printErrorAndExit("I2P Config path don't exist", "Delete your config directory for the Browser Bundle") - +*/ val deployer = new DeployProfile(configPath,basePath) deployer.verifyExistenceOfConfig() diff --git a/launchers/build.sbt b/launchers/build.sbt index b609d21720..65892bcba0 100644 --- a/launchers/build.sbt +++ b/launchers/build.sbt @@ -40,8 +40,8 @@ lazy val browserbundle = (project in file("browserbundle")) lazy val macosx = (project in file("macosx")) .settings( commonSettings, - name := "RouterPack.jar", - mainClass in assembly := Some("net.i2p.launchers.osx.LauncherAppMain") + name := "routerLauncher", + mainClass in assembly := Some("net.i2p.launchers.SimpleOSXLauncher") ).dependsOn(common) diff --git a/launchers/macosx/build.sbt b/launchers/macosx/build.sbt index 300102eab2..bedbe667d0 100644 --- a/launchers/macosx/build.sbt +++ b/launchers/macosx/build.sbt @@ -21,21 +21,23 @@ resourceDirectory in Compile := baseDirectory.value / ".." / ".." / "installer" // Unmanaged base will be included in a fat jar unmanagedBase in Compile := baseDirectory.value / ".." / ".." / "pkg-temp" / "lib" + // Unmanaged classpath will be available at compile time unmanagedClasspath in Compile ++= Seq( - baseDirectory.value / ".." / ".." / "pkg-temp" / "lib" / "*.jar" + baseDirectory.value / ".." / ".." / "pkg-temp" / "lib" / "*.jar" ) assemblyOption in assembly := (assemblyOption in assembly).value.copy(includeScala = false, includeDependency = false) - -assemblyJarName in assembly := s"launcher.jar" - assemblyExcludedJars in assembly := { - val cp = (fullClasspath in assembly).value - cp filter { c => jarsForCopy.toList.contains(c.data.getName) } + val cp = (fullClasspath in assembly).value + cp filter { c => jarsForCopy.toList.contains(c.data.getName) } } +/* + +assemblyJarName in assembly := s"package.jar" + // TODO: MEEH: Add assemblyExcludedJars and load the router from own jar files, to handle upgrades better. // In fact, most likely the bundle never would need an update except for the router jars/wars. @@ -104,3 +106,4 @@ buildAppBundleTask := { println(s"Zip placed into bundle :)") } +*/ diff --git a/launchers/macosx/obj-cpp/AppDelegate.h b/launchers/macosx/obj-cpp/AppDelegate.h index 8dec8c8e75..066be4b001 100644 --- a/launchers/macosx/obj-cpp/AppDelegate.h +++ b/launchers/macosx/obj-cpp/AppDelegate.h @@ -9,9 +9,45 @@ #include "StatusItemButton.h" #include "JavaHelper.h" +#include "RouterTask.h" +#include "neither/maybe.hpp" +#include "optional.hpp" +#include "subprocess.hpp" +#include +#include + +using namespace neither; extern JvmListSharedPtr gRawJvmList; +// DO NOT ACCESS THIS GLOBAL VARIABLE DIRECTLY. +maybeAnRouterRunner globalRouterStatus = maybeAnRouterRunner{}; + +maybeAnRouterRunner getGlobalRouterObject() +{ + std::lock_guard lock(globalRouterStatusMutex); + return globalRouterStatus; +} + +void setGlobalRouterObject(RouterTask* newRouter) +{ + std::lock_guard lock(globalRouterStatusMutex); + globalRouterStatus.emplace(newRouter); +} + + + +std::vector globVector(const std::string& pattern){ + glob_t glob_result; + glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result); + std::vector files; + for(unsigned int i=0;i @property BOOL enableLogging; @property BOOL enableVerboseLogging; @@ -55,6 +91,8 @@ extern JvmListSharedPtr gRawJvmList; - (void)userChooseJavaHome; - (AppDelegate *)initWithArgc:(int)argc argv:(const char **)argv; - (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList; +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center + shouldPresentNotification:(NSUserNotification *)notification; @end diff --git a/launchers/macosx/obj-cpp/Info.plist b/launchers/macosx/obj-cpp/Info.plist index e0197e9a29..79a7a00dad 100644 --- a/launchers/macosx/obj-cpp/Info.plist +++ b/launchers/macosx/obj-cpp/Info.plist @@ -26,6 +26,8 @@ I2P CFBundleVersion 0.0.1 + NSUserNotificationAlertStyle + alert NSAppleScriptEnabled CGDisableCoalescedUpdates diff --git a/launchers/macosx/obj-cpp/JavaHelper.h b/launchers/macosx/obj-cpp/JavaHelper.h index 2ede52689b..dc8080757c 100644 --- a/launchers/macosx/obj-cpp/JavaHelper.h +++ b/launchers/macosx/obj-cpp/JavaHelper.h @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -18,10 +17,19 @@ #include #include +#include "optional.hpp" #include "strutil.hpp" #include "subprocess.hpp" +#include "neither/maybe.hpp" +#include "RouterTask.h" using namespace subprocess; +using namespace neither; + +using maybeAnRouterRunner = std::experimental::optional; + +extern std::mutex globalRouterStatusMutex; +extern maybeAnRouterRunner globalRouterStatus; #define DEF_MIN_JVM_VER "1.7+" @@ -133,7 +141,7 @@ static void processJvmPlistEntries (const void* item, void* context) { auto d = extractString((CFStringRef)value); currentJvm->JVMPlatformVersion = trim_copy(d); } - + } }; @@ -150,7 +158,7 @@ static void listAllJavaInstallsAvailable() { auto javaHomeRes = check_output({"/usr/libexec/java_home","-v",DEF_MIN_JVM_VER,"-X"}); CFDataRef javaHomes = CFDataCreate(NULL, (const UInt8 *)javaHomeRes.buf.data(), strlen(javaHomeRes.buf.data())); - + //CFErrorRef err; CFPropertyListRef propertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, javaHomes, kCFPropertyListImmutable, NULL, NULL); /*if (err) diff --git a/launchers/macosx/obj-cpp/JavaRunner.cpp b/launchers/macosx/obj-cpp/JavaRunner.cpp deleted file mode 100644 index 6b1ec673ac..0000000000 --- a/launchers/macosx/obj-cpp/JavaRunner.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "JavaRunner.h" - -#include -#include -#include - -using namespace subprocess; -using namespace std::experimental; - -JavaRunner::JavaRunner(std::string javaBin, const fp_proc_t& execFn, const fp_t& cb) - : javaBinaryPath(javaBin), executingFn(execFn), exitCallbackFn(cb) -{ - javaProcess = std::shared_ptr(new Popen({javaBin.c_str(), "-version"}, defer_spawn{true})); -} - -optional > JavaRunner::execute() -{ - try { - auto executingFn = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{ - this->executingFn(this); - }); - dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), executingFn); - dispatch_block_wait(executingFn, DISPATCH_TIME_FOREVER); - - // Here, the process is done executing. - - printf("Finished executingFn - Runs callbackFn\n"); - this->exitCallbackFn(); - return std::async(std::launch::async, []{ return 0; }); - } catch (std::exception* ex) { - printf("ERROR: %s\n", ex->what()); - return nullopt; - } -} diff --git a/launchers/macosx/obj-cpp/JavaRunner.h b/launchers/macosx/obj-cpp/JavaRunner.h deleted file mode 100644 index 78f1ba17a2..0000000000 --- a/launchers/macosx/obj-cpp/JavaRunner.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include - -using namespace subprocess; -using namespace std::experimental; - -class JavaRunner; - -struct CRouterState -{ - enum State { - C_STOPPED = 0, - C_STARTED, - C_RUNNING - }; -}; - -typedef std::function fp_t; -typedef std::function fp_proc_t; - -/** - * - * class JavaRunner - * - **/ -class JavaRunner -{ -public: - // copy fn - JavaRunner(std::string javaBin, const fp_proc_t& executingFn, const fp_t& cb); - ~JavaRunner() = default; - - const std::list 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", - "-Di2p.dir.base=$BASEPATH", - "-Djava.library.path=$BASEPATH", - "$JAVA_OPTS", - "net.i2p.launchers.SimpleOSXLauncher" - }; - - const std::list defaultFlagsForExtractorJob { - "-Xmx512M", - "-Xms128m", - "-Djava.awt.headless=true", - "-Di2p.dir.base=$BASEPATH", - "-Di2p.dir.zip=$ZIPPATH", - "net.i2p.launchers.BaseExtractor", - "extract" - }; - - optional > execute(); - std::shared_ptr javaProcess; - std::string javaBinaryPath; -private: - const fp_proc_t& executingFn; - const fp_t& exitCallbackFn; -}; - diff --git a/launchers/macosx/obj-cpp/RouterTask.h b/launchers/macosx/obj-cpp/RouterTask.h new file mode 100644 index 0000000000..cc93fa7d71 --- /dev/null +++ b/launchers/macosx/obj-cpp/RouterTask.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#import + +#include "optional.hpp" +#include "subprocess.hpp" + + +@class RTaskOptions; + +@interface RTaskOptions : NSObject +@property (strong) NSString* binPath; +@property (strong) NSArray* arguments; +@property (strong) NSString* i2pBaseDir; +@end + +@class RouterTask; + +@interface RouterTask : NSObject +@property (strong) NSTask* routerTask; +@property (strong) NSUserDefaults *userPreferences; +@property (strong) NSFileHandle *readLogHandle; +@property (strong) NSMutableData *totalLogData; +@property (strong) NSPipe *processPipe; +@property (strong) NSFileHandle *input; +- (instancetype) initWithOptions : (RTaskOptions*) options; +- (int) execute; +- (int) getPID; +@end + + + +using namespace subprocess; + +class JavaRunner; + +typedef std::function fp_t; +typedef std::function fp_proc_t; + +/** + * + * class JavaRunner + * + **/ +class JavaRunner +{ +public: + // copy fn + 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::experimental::optional > execute(); + std::shared_ptr javaProcess; + std::string javaBinaryPath; + std::string javaRouterArgs; + std::string execLine; + std::string _i2pBaseDir; +private: + const fp_proc_t& executingFn; + const fp_t& exitCallbackFn; +}; + + diff --git a/launchers/macosx/obj-cpp/RouterTask.mm b/launchers/macosx/obj-cpp/RouterTask.mm new file mode 100644 index 0000000000..e3f6772124 --- /dev/null +++ b/launchers/macosx/obj-cpp/RouterTask.mm @@ -0,0 +1,141 @@ +#include "RouterTask.h" + +#include +#include +#include + +#include "optional.hpp" +#include "subprocess.hpp" + +#import + +@implementation RTaskOptions +@end + +@implementation RouterTask + +- (instancetype) initWithOptions : (RTaskOptions*) options +{ + self.input = [NSFileHandle fileHandleWithStandardInput]; + self.routerTask = [NSTask new]; + self.processPipe = [NSPipe new]; + [self.routerTask setLaunchPath:options.binPath]; + [self.routerTask setArguments:options.arguments]; + NSDictionary *envDict = @{ + @"I2PBASE": options.i2pBaseDir + }; + [self.routerTask setEnvironment: envDict]; + [self.routerTask setStandardOutput:self.processPipe]; + [self.routerTask setStandardError:self.processPipe]; +/* + self.readLogHandle = [self.processPipe fileHandleForReading]; + NSData *inData = nil; + self.totalLogData = [[[NSMutableData alloc] init] autorelease]; + + while ((inData = [self.readLogHandle availableData]) && + [inData length]) { + [self.totalLogData appendData:inData]; + } +*/ + return self; +} + +- (int) execute +{ + //@try { + [self.routerTask launch]; + [self.input waitForDataInBackgroundAndNotify]; + [[self.processPipe fileHandleForReading] waitForDataInBackgroundAndNotify]; + [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification + object:[self.processPipe fileHandleForReading] queue:nil + usingBlock:^(NSNotification *note) + { + // Read from shell output + NSData *outData = [[self.processPipe fileHandleForReading] availableData]; + NSString *outStr = [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding]; + if ([outStr length] > 1) { + NSLog(@"output: %@", outStr); + } + + // Continue waiting for shell output. + [[self.processPipe fileHandleForReading] waitForDataInBackgroundAndNotify]; + }]; + //[self.routerTask waitUntilExit]; + //NSThread *thr = [[NSThread alloc] initWithTarget:self.routerTask selector:@selector(launch) object:nil]; + //[self.routerTask waitUntilExit]; + return 1; + /*} + @catch (NSException *e) + { + NSLog(@"Expection occurred %@", [e reason]); + return 0; + }*/ +} + +- (int) getPID +{ + return [self.routerTask processIdentifier]; +} + +@end + + + + +using namespace subprocess; + +std::mutex globalRouterStatusMutex; + +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) +{ + execLine = javaBinaryPath; + execLine += " " + std::string(javaRouterArgs.c_str()); + printf("CLI: %s\n",execLine.c_str()); + javaProcess = std::shared_ptr(new Popen(execLine, environment{{ + {"I2PBASE", _i2pBaseDir}, + {"JAVA_OPTS", getenv("JAVA_OPTS")} + }}, defer_spawn{true})); +} + +void JavaRunner::requestRouterShutdown() +{ + // SIGHUP + javaProcess->kill(1); +} + +std::experimental::optional > JavaRunner::execute() +{ + try { + auto executingFn = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{ + this->executingFn(this); + }); + dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), executingFn); + dispatch_block_wait(executingFn, DISPATCH_TIME_FOREVER); + + // Here, the process is done executing. + + printf("Finished executingFn - Runs callbackFn\n"); + this->exitCallbackFn(); + return std::async(std::launch::async, []{ return 0; }); + } catch (std::exception* ex) { + printf("ERROR: %s\n", ex->what()); + return std::experimental::nullopt; + } +} diff --git a/launchers/macosx/obj-cpp/build.ninja b/launchers/macosx/obj-cpp/build.ninja index 2fe451b73b..d5e9aa8f8f 100644 --- a/launchers/macosx/obj-cpp/build.ninja +++ b/launchers/macosx/obj-cpp/build.ninja @@ -1,5 +1,5 @@ cxx = clang++ -cflags = -std=c++14 -g -Wall -I./include -I/usr/local/include -I/usr/include -Wno-unused-variable -mmacosx-version-min=10.10 +cflags = -std=c++14 -g -Wall -I./include -I./include/neither -I/usr/local/include -I/usr/include -Wno-unused-variable -mmacosx-version-min=10.10 ldflags = -framework CoreFoundation -framework Foundation -framework Cocoa -g -rdynamic @@ -26,7 +26,8 @@ rule cleanup rule bundledir command = mkdir -p I2PLauncher.app/Contents/{MacOS,Resources,Frameworks} $ && cp Info.plist I2PLauncher.app/Contents/Info.plist $ - && cp base.zip I2PLauncher.app/Contents/Resources/base.zip + && cp base.zip I2PLauncher.app/Contents/Resources/base.zip $ + && cp ../target/scala-2.11/routerLauncher-assembly-0.1.0-SNAPSHOT.jar I2PLauncher.app/Contents/Resources/launcher.jar rule copytobundledir command = cp clauncher I2PLauncher.app/Contents/MacOS/I2PLauncher @@ -39,14 +40,14 @@ rule builddir build main.o: cxx main.mm build StatusItemButton.o: cxx StatusItemButton.mm -build JavaRunner.o: cxx JavaRunner.cpp +build RouterTask.o: cxx RouterTask.mm build clean: cleanup build bundle: bundledir build copytobundle: copytobundledir | bundle clauncher -build clauncher: link main.o StatusItemButton.o JavaRunner.o +build clauncher: link main.o StatusItemButton.o RouterTask.o build appbundle: copyimgtobundle | clauncher bundle copytobundle diff --git a/launchers/macosx/obj-cpp/include/strutil.hpp b/launchers/macosx/obj-cpp/include/strutil.hpp index 5ec4ef9ef7..a906b46cce 100644 --- a/launchers/macosx/obj-cpp/include/strutil.hpp +++ b/launchers/macosx/obj-cpp/include/strutil.hpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include "optional.hpp" #include #include diff --git a/launchers/macosx/obj-cpp/main.mm b/launchers/macosx/obj-cpp/main.mm index 0d978f12cf..345c53e7d1 100644 --- a/launchers/macosx/obj-cpp/main.mm +++ b/launchers/macosx/obj-cpp/main.mm @@ -4,7 +4,10 @@ #include #include #include -#include +#include +#include +#include +#include #import @@ -23,8 +26,10 @@ #include "AppDelegate.h" #include "StatusItemButton.h" -#include "JavaRunner.h" +#include "RouterTask.h" #include "JavaHelper.h" +#include "fn.h" +#include "optional.hpp" #define DEF_I2P_VERSION "0.9.35" #define APPDOMAIN "net.i2p.launcher" @@ -43,6 +48,55 @@ JvmListSharedPtr gRawJvmList = nullptr; @end +std::future startupRouter(NSString* javaBin, NSArray* arguments, NSString* i2pBaseDir) { +/* + NSLog(@"Arguments: %@", [NSString stringWithUTF8String:arguments.c_str()]); + auto launchLambda = [](JavaRunner *javaRun) { + javaRun->javaProcess->start_process(); + auto pid = javaRun->javaProcess->pid(); + std::cout << "I2P Router process id = " << pid << std::endl; + + // Blocking + javaRun->javaProcess->wait(); + }; + auto callbackAfterExit = [](){ + printf("Callback after exit\n"); + }; + NSLog(@"Still fine!"); + + setGlobalRouterObject(new JavaRunner{ javaBin, arguments, i2pBaseDir, std::move(launchLambda), std::move(callbackAfterExit) }); + + NSLog(@"Still fine!"); + return std::async(std::launch::async, [&]{ + getGlobalRouterObject().value()->execute(); + return 0; + }); +*/ + CFShow(arguments); + + @try { + RTaskOptions* options = [RTaskOptions alloc]; + options.binPath = javaBin; + options.arguments = arguments; + options.i2pBaseDir = i2pBaseDir; + auto instance = [[[RouterTask alloc] initWithOptions: options] autorelease]; + //auto pid = [instance execute]; + //NSThread *thr = [[NSThread alloc] initWithTarget:instance selector:@selector(execute) object:nil]; + [instance execute]; + return std::async(std::launch::async, [&instance]{ + return 1;//[instance getPID]; + }); + } + @catch (NSException *e) + { + NSLog(@"Expection occurred %@", [e reason]); + return std::async(std::launch::async, [&]{ + return 0; + }); + } +} + + @implementation MenuBarCtrl - (void) statusItemButtonLeftClick: (StatusItemButton *) button @@ -82,6 +136,11 @@ JvmListSharedPtr gRawJvmList = nullptr; - (void) stopJavaRouterBtnHandler: (NSMenuItem *) menuItem { NSLog(@"Clicked stopJavaRouterBtnHandler"); + if (getGlobalRouterObject().has_value()) + { + //getGlobalRouterObject().value()->requestRouterShutdown(); + NSLog(@"Requested shutdown"); + } } - (void) quitWrapperBtnHandler: (NSMenuItem *) menuItem @@ -172,6 +231,11 @@ JvmListSharedPtr gRawJvmList = nullptr; @implementation AppDelegate +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center + shouldPresentNotification:(NSUserNotification *)notification { + return YES; +} + - (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList { NSString *appleScriptString = @"set jvmlist to {\"Newest\""; @@ -224,13 +288,14 @@ JvmListSharedPtr gRawJvmList = nullptr; CFPreferencesSetMultiple((CFDictionaryRef)dict, NULL, CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); //CFPreferencesSetAppValue(@"javaHome", (CFPropertyListRef)cfDefaultHome, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); - + if (self.enableVerboseLogging) NSLog(@"Default preferences stored!"); } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // Init application here + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self]; // Start with user preferences self.userPreferences = [NSUserDefaults standardUserDefaults]; [self setApplicationDefaultPreferences]; @@ -246,7 +311,6 @@ JvmListSharedPtr gRawJvmList = nullptr; auto javaHomePref = [self.userPreferences stringForKey:@"javaHome"]; if (self.enableVerboseLogging) NSLog(@"Java home from preferences: %@", javaHomePref); - [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self]; // This is the only GUI the user experience on a regular basis. self.menuBarCtrl = [[MenuBarCtrl alloc] init]; @@ -255,6 +319,16 @@ JvmListSharedPtr gRawJvmList = nullptr; if (self.enableVerboseLogging) NSLog(@"Appdomain is: %@", appDomain); NSLog(@"We should have started the statusbar object by now..."); + + // Figure out base directory + const char* pathFromHome = "/Users/%s/Library/I2P"; + auto username = getenv("USER"); + char buffer[strlen(pathFromHome)+strlen(username)]; + sprintf(buffer, pathFromHome, username); + std::string i2pBaseDir(buffer); + if (self.enableVerboseLogging) printf("Home directory is: %s\n", buffer); + + //[statusBarButton setAction:@selector(itemClicked:)]; //dispatch_async(dispatch_get_main_queue(), ^{ //}); @@ -273,33 +347,121 @@ JvmListSharedPtr gRawJvmList = nullptr; return std::string([val UTF8String]);; }; - - auto launchLambda = [&pref](JavaRunner *javaRun) { - javaRun->javaProcess->start_process(); - auto pid = javaRun->javaProcess->pid(); - std::cout << "I2P Router process id = " << pid << std::endl; - - // Blocking - javaRun->javaProcess->wait(); - }; - auto callbackAfterExit = [=](){ - printf("Callback after exit\n"); - }; - - try { - - // Get Java home + auto getJavaBin = [&getJavaHomeLambda]() -> std::string { + // Get Java home auto javaHome = getJavaHomeLambda(); trim(javaHome); // Trim to remove endline auto javaBin = std::string(javaHome); javaBin += "/bin/java"; // Append java binary to path. - //printf("hello world: %s\n", javaBin.c_str()); - if (self.enableVerboseLogging) NSLog(@"Defaults: %@", [pref dictionaryRepresentation]); + return javaBin; + }; - auto r = new JavaRunner{ javaBin, launchLambda, callbackAfterExit }; - r->execute(); + auto buildClassPath = [](std::string basePath) -> std::vector { + return globVector(basePath+std::string("/lib/*.jar")); + }; + + auto sendUserNotification = [&](NSString* title, NSString* informativeText) -> void { + NSUserNotification *userNotification = [[[NSUserNotification alloc] init] autorelease]; + + userNotification.title = title; + userNotification.informativeText = informativeText; + userNotification.soundName = NSUserNotificationDefaultSoundName; + + [[NSUserNotificationCenter defaultUserNotificationCenter] scheduleNotification:userNotification]; + }; + + + // Get paths + NSBundle *launcherBundle = [NSBundle mainBundle]; + + std::string basearg("-Di2p.dir.base="); + basearg += i2pBaseDir; + + std::string zippath("-Di2p.base.zip="); + zippath += [[launcherBundle pathForResource:@"base" ofType:@"zip"] UTF8String]; + + std::string jarfile("-cp "); + jarfile += [[launcherBundle pathForResource:@"launcher" ofType:@"jar"] UTF8String]; + + struct stat sb; + if ( !(stat(buffer, &sb) == 0 && S_ISDIR(sb.st_mode)) ) + { + // I2P is not extracted. + if (self.enableVerboseLogging) printf("I2P Directory don't exists!\n"); + + // Create directory + mkdir(buffer, S_IRUSR | S_IWUSR | S_IXUSR); + + auto cli = JavaRunner::defaultFlagsForExtractorJob; + setenv("I2PBASE", buffer, true); + setenv("ZIPPATH", zippath.c_str(), true); + //setenv("DYLD_LIBRARY_PATH",".:/usr/lib:/lib:/usr/local/lib", true); + + cli.push_back(basearg); + cli.push_back(zippath); + cli.push_back(jarfile); + cli.push_back("net.i2p.launchers.BaseExtractor"); + + //auto charCli = map(cli, [](std::string str){ return str.c_str(); }); + std::string execStr = getJavaBin(); + for_each(cli, [&execStr](std::string str){ execStr += std::string(" ") + str; }); + + printf("\n\nTrying cmd: %s\n\n", execStr.c_str()); + try { + sendUserNotification((NSString*)CFSTR("I2P Extraction"), (NSString*)CFSTR("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", buffer} + }}).wait(); + printf("Extraction exit code %d\n",extractStatus); + sendUserNotification((NSString*)CFSTR("I2P Extraction"), (NSString*)CFSTR("Extraction complete!")); + } catch (subprocess::OSError &err) { + printf("Something bad happened: %s\n", err.what()); + } + + } else { + if (self.enableVerboseLogging) printf("I2P directory found!\n"); + } + + // Expect base to be extracted by now. + + auto jarList = buildClassPath(std::string(buffer)); + std::string classpathStrHead = "-classpath"; + std::string classpathStr = ""; + classpathStr += [[launcherBundle pathForResource:@"launcher" ofType:@"jar"] UTF8String]; + std::string prefix(i2pBaseDir); + prefix += "/lib/"; + for_each(jarList, [&classpathStr](std::string str){ classpathStr += std::string(":") + str; }); + //if (self.enableVerboseLogging) NSLog(@"Classpath: %@\n",[NSString stringWithUTF8String:classpathStr.c_str()]); + + + + try { + auto argList = JavaRunner::defaultStartupFlags; + + std::string baseDirArg("-Di2p.dir.base="); + baseDirArg += i2pBaseDir; + std::string javaLibArg("-Djava.library.path="); + javaLibArg += i2pBaseDir; + // TODO: pass this to JVM + auto java_opts = getenv("JAVA_OPTS"); + + argList.push_back([NSString stringWithUTF8String:baseDirArg.c_str()]); + argList.push_back([NSString stringWithUTF8String:javaLibArg.c_str()]); + argList.push_back([NSString stringWithUTF8String:classpathStrHead.c_str()]); + argList.push_back([NSString stringWithUTF8String:classpathStr.c_str()]); + argList.push_back(@"net.i2p.router.Router"); + auto javaBin = getJavaBin(); + + + sendUserNotification(@"I2P Launcher", @"I2P Router is starting up!"); + auto nsJavaBin = [NSString stringWithUTF8String:javaBin.c_str()]; + auto nsBasePath = [NSString stringWithUTF8String:i2pBaseDir.c_str()]; + NSArray* arrArguments = [NSArray arrayWithObjects:&argList[0] count:argList.size()]; + startupRouter(nsJavaBin, arrArguments, nsBasePath); + //if (self.enableVerboseLogging) NSLog(@"Defaults: %@", [pref dictionaryRepresentation]); } catch (std::exception &err) { - std::cerr << "Exception: " << err.what() << std::endl; + std::cerr << "Exception: " << err.what() << std::endl; } } diff --git a/launchers/macosx/src/main/java/net/i2p/launchers/BaseExtractor.java b/launchers/macosx/src/main/java/net/i2p/launchers/BaseExtractor.java index b1251cc782..44b7352cf6 100644 --- a/launchers/macosx/src/main/java/net/i2p/launchers/BaseExtractor.java +++ b/launchers/macosx/src/main/java/net/i2p/launchers/BaseExtractor.java @@ -21,45 +21,56 @@ import java.util.zip.ZipFile; */ public class BaseExtractor extends EnvCheck { - private void runExtract(String zipFilename, String destinationPath) { + public boolean printDebug = false; + + public void runExtract(String zipFilename) { + String destinationPath = this.baseDirPath; try(ZipFile file = new ZipFile(zipFilename)) { FileSystem fileSystem = FileSystems.getDefault(); Enumeration entries = file.entries(); - Files.createDirectory(fileSystem.getPath(destinationPath)); + + try { + Files.createDirectory(fileSystem.getPath(destinationPath)); + } catch (IOException e) { + // It's OK to fail here. + } + while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); + if (printDebug) System.out.println("Found entry: "+entry.toString()); if (entry.isDirectory()) { - System.out.println("Creating Directory:" + destinationPath + entry.getName()); - Files.createDirectories(fileSystem.getPath(destinationPath + entry.getName())); + if (printDebug) System.out.println("Creating Directory:" + destinationPath + "/" + entry.getName()); + Files.createDirectories(fileSystem.getPath(destinationPath + "/" + entry.getName())); } else { InputStream is = file.getInputStream(entry); BufferedInputStream bis = new BufferedInputStream(is); - String uncompressedFileName = destinationPath + entry.getName(); + String uncompressedFileName = destinationPath + "/" + entry.getName(); Path uncompressedFilePath = fileSystem.getPath(uncompressedFileName); Files.createFile(uncompressedFilePath); FileOutputStream fileOutput = new FileOutputStream(uncompressedFileName); while (bis.available() > 0) fileOutput.write(bis.read()); fileOutput.close(); - System.out.println("Written :" + entry.getName()); + if (printDebug) System.out.println("Written :" + entry.getName()); } } } catch (IOException e) { // + System.err.println("Exception in extractor!"); + System.err.println(e.toString()); } } public BaseExtractor(String[] args) { super(args); - - if (args.length == 2) { - if ("extract".equals(args[0])) { - // Start extract - - }this.runExtract(System.getProperty("i2p.base.zip"),this.baseDirPath); - } } public static void main(String[] args) { - new BaseExtractor(args); + System.out.println("Starting extraction"); + BaseExtractor be = new BaseExtractor(args); + String debug = System.getProperty("print.debug"); + if (debug != null) { + be.printDebug = true; + } + be.runExtract(System.getProperty("i2p.base.zip")); } }