Fixed a load of issues related to my "sync" approach, and Mac OSX's "async" API.

This commit is contained in:
meeh
2018-07-13 06:30:16 +00:00
parent 21b3864dfd
commit 6d0f80fa1e
14 changed files with 498 additions and 163 deletions

View File

@ -62,7 +62,7 @@ object RouterLauncherApp extends App {
val configPath = Option(System.getProperty("i2p.dir.config")).getOrElse(System.getenv("I2PCONFIG")) val configPath = Option(System.getProperty("i2p.dir.config")).getOrElse(System.getenv("I2PCONFIG"))
println(s"basePath => ${basePath}\nconfigPath => ${configPath}") println(s"basePath => ${basePath}\nconfigPath => ${configPath}")
/*
object ErrorUtils { object ErrorUtils {
def errorMessageInJson(message: String, solution: String) : JObject = JObject( def errorMessageInJson(message: String, solution: String) : JObject = JObject(
List( 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(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") 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) val deployer = new DeployProfile(configPath,basePath)
deployer.verifyExistenceOfConfig() deployer.verifyExistenceOfConfig()

View File

@ -40,8 +40,8 @@ lazy val browserbundle = (project in file("browserbundle"))
lazy val macosx = (project in file("macosx")) lazy val macosx = (project in file("macosx"))
.settings( .settings(
commonSettings, commonSettings,
name := "RouterPack.jar", name := "routerLauncher",
mainClass in assembly := Some("net.i2p.launchers.osx.LauncherAppMain") mainClass in assembly := Some("net.i2p.launchers.SimpleOSXLauncher")
).dependsOn(common) ).dependsOn(common)

View File

@ -21,21 +21,23 @@ resourceDirectory in Compile := baseDirectory.value / ".." / ".." / "installer"
// Unmanaged base will be included in a fat jar // Unmanaged base will be included in a fat jar
unmanagedBase in Compile := baseDirectory.value / ".." / ".." / "pkg-temp" / "lib" unmanagedBase in Compile := baseDirectory.value / ".." / ".." / "pkg-temp" / "lib"
// Unmanaged classpath will be available at compile time // Unmanaged classpath will be available at compile time
unmanagedClasspath in Compile ++= Seq( 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) assemblyOption in assembly := (assemblyOption in assembly).value.copy(includeScala = false, includeDependency = false)
assemblyJarName in assembly := s"launcher.jar"
assemblyExcludedJars in assembly := { assemblyExcludedJars in assembly := {
val cp = (fullClasspath in assembly).value val cp = (fullClasspath in assembly).value
cp filter { c => jarsForCopy.toList.contains(c.data.getName) } 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. // 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. // 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 :)") println(s"Zip placed into bundle :)")
} }
*/

View File

@ -9,9 +9,45 @@
#include "StatusItemButton.h" #include "StatusItemButton.h"
#include "JavaHelper.h" #include "JavaHelper.h"
#include "RouterTask.h"
#include "neither/maybe.hpp"
#include "optional.hpp"
#include "subprocess.hpp"
#include <glob.h>
#include <vector>
using namespace neither;
extern JvmListSharedPtr gRawJvmList; extern JvmListSharedPtr gRawJvmList;
// DO NOT ACCESS THIS GLOBAL VARIABLE DIRECTLY.
maybeAnRouterRunner globalRouterStatus = maybeAnRouterRunner{};
maybeAnRouterRunner getGlobalRouterObject()
{
std::lock_guard<std::mutex> lock(globalRouterStatusMutex);
return globalRouterStatus;
}
void setGlobalRouterObject(RouterTask* newRouter)
{
std::lock_guard<std::mutex> lock(globalRouterStatusMutex);
globalRouterStatus.emplace(newRouter);
}
std::vector<std::string> globVector(const std::string& pattern){
glob_t glob_result;
glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result);
std::vector<std::string> files;
for(unsigned int i=0;i<glob_result.gl_pathc;++i){
files.push_back(std::string(glob_result.gl_pathv[i]));
}
globfree(&glob_result);
return files;
}
@interface MenuBarCtrl : NSObject <StatusItemButtonDelegate, NSMenuDelegate> @interface MenuBarCtrl : NSObject <StatusItemButtonDelegate, NSMenuDelegate>
@property BOOL enableLogging; @property BOOL enableLogging;
@property BOOL enableVerboseLogging; @property BOOL enableVerboseLogging;
@ -55,6 +91,8 @@ extern JvmListSharedPtr gRawJvmList;
- (void)userChooseJavaHome; - (void)userChooseJavaHome;
- (AppDelegate *)initWithArgc:(int)argc argv:(const char **)argv; - (AppDelegate *)initWithArgc:(int)argc argv:(const char **)argv;
- (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList; - (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList;
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
shouldPresentNotification:(NSUserNotification *)notification;
@end @end

View File

@ -26,6 +26,8 @@
<string>I2P</string> <string>I2P</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>0.0.1</string> <string>0.0.1</string>
<key>NSUserNotificationAlertStyle</key>
<string>alert</string>
<key>NSAppleScriptEnabled</key> <key>NSAppleScriptEnabled</key>
<true/> <true/>
<key>CGDisableCoalescedUpdates</key> <key>CGDisableCoalescedUpdates</key>

View File

@ -7,7 +7,6 @@
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
#include <list> #include <list>
#include <experimental/optional>
#include <stdlib.h> #include <stdlib.h>
#include <Foundation/Foundation.h> #include <Foundation/Foundation.h>
@ -18,10 +17,19 @@
#include <CoreFoundation/CFArray.h> #include <CoreFoundation/CFArray.h>
#include <CoreFoundation/CFString.h> #include <CoreFoundation/CFString.h>
#include "optional.hpp"
#include "strutil.hpp" #include "strutil.hpp"
#include "subprocess.hpp" #include "subprocess.hpp"
#include "neither/maybe.hpp"
#include "RouterTask.h"
using namespace subprocess; using namespace subprocess;
using namespace neither;
using maybeAnRouterRunner = std::experimental::optional<RouterTask*>;
extern std::mutex globalRouterStatusMutex;
extern maybeAnRouterRunner globalRouterStatus;
#define DEF_MIN_JVM_VER "1.7+" #define DEF_MIN_JVM_VER "1.7+"
@ -133,7 +141,7 @@ static void processJvmPlistEntries (const void* item, void* context) {
auto d = extractString((CFStringRef)value); auto d = extractString((CFStringRef)value);
currentJvm->JVMPlatformVersion = trim_copy(d); 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"}); 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())); CFDataRef javaHomes = CFDataCreate(NULL, (const UInt8 *)javaHomeRes.buf.data(), strlen(javaHomeRes.buf.data()));
//CFErrorRef err; //CFErrorRef err;
CFPropertyListRef propertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, javaHomes, kCFPropertyListImmutable, NULL, NULL); CFPropertyListRef propertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, javaHomes, kCFPropertyListImmutable, NULL, NULL);
/*if (err) /*if (err)

View File

@ -1,34 +0,0 @@
#include "JavaRunner.h"
#include <dispatch/dispatch.h>
#include <subprocess.hpp>
#include <future>
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<Popen>(new Popen({javaBin.c_str(), "-version"}, defer_spawn{true}));
}
optional<std::future<int> > 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;
}
}

View File

@ -1,72 +0,0 @@
#pragma once
#include <dispatch/dispatch.h>
#include <functional>
#include <memory>
#include <string>
#include <list>
#include <subprocess.hpp>
#include <optional.hpp>
using namespace subprocess;
using namespace std::experimental;
class JavaRunner;
struct CRouterState
{
enum State {
C_STOPPED = 0,
C_STARTED,
C_RUNNING
};
};
typedef std::function<void(void)> fp_t;
typedef std::function<void(JavaRunner *ptr)> 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<std::string> 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<std::string> defaultFlagsForExtractorJob {
"-Xmx512M",
"-Xms128m",
"-Djava.awt.headless=true",
"-Di2p.dir.base=$BASEPATH",
"-Di2p.dir.zip=$ZIPPATH",
"net.i2p.launchers.BaseExtractor",
"extract"
};
optional<std::future<int> > execute();
std::shared_ptr<Popen> javaProcess;
std::string javaBinaryPath;
private:
const fp_proc_t& executingFn;
const fp_t& exitCallbackFn;
};

View File

@ -0,0 +1,75 @@
#pragma once
#include <dispatch/dispatch.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <Cocoa/Cocoa.h>
#import <AppKit/AppKit.h>
#include "optional.hpp"
#include "subprocess.hpp"
@class RTaskOptions;
@interface RTaskOptions : NSObject
@property (strong) NSString* binPath;
@property (strong) NSArray<NSString *>* 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<void(void)> fp_t;
typedef std::function<void(JavaRunner *ptr)> 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<NSString*> defaultStartupFlags;
static const std::vector<std::string> defaultFlagsForExtractorJob;
void requestRouterShutdown();
std::experimental::optional<std::future<int> > execute();
std::shared_ptr<Popen> javaProcess;
std::string javaBinaryPath;
std::string javaRouterArgs;
std::string execLine;
std::string _i2pBaseDir;
private:
const fp_proc_t& executingFn;
const fp_t& exitCallbackFn;
};

View File

@ -0,0 +1,141 @@
#include "RouterTask.h"
#include <dispatch/dispatch.h>
#include <future>
#include <stdlib.h>
#include "optional.hpp"
#include "subprocess.hpp"
#import <AppKit/AppKit.h>
@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<NSString*> 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<std::string> 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<Popen>(new Popen(execLine, environment{{
{"I2PBASE", _i2pBaseDir},
{"JAVA_OPTS", getenv("JAVA_OPTS")}
}}, defer_spawn{true}));
}
void JavaRunner::requestRouterShutdown()
{
// SIGHUP
javaProcess->kill(1);
}
std::experimental::optional<std::future<int> > 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;
}
}

View File

@ -1,5 +1,5 @@
cxx = clang++ 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 ldflags = -framework CoreFoundation -framework Foundation -framework Cocoa -g -rdynamic
@ -26,7 +26,8 @@ rule cleanup
rule bundledir rule bundledir
command = mkdir -p I2PLauncher.app/Contents/{MacOS,Resources,Frameworks} $ command = mkdir -p I2PLauncher.app/Contents/{MacOS,Resources,Frameworks} $
&& cp Info.plist I2PLauncher.app/Contents/Info.plist $ && 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 rule copytobundledir
command = cp clauncher I2PLauncher.app/Contents/MacOS/I2PLauncher command = cp clauncher I2PLauncher.app/Contents/MacOS/I2PLauncher
@ -39,14 +40,14 @@ rule builddir
build main.o: cxx main.mm build main.o: cxx main.mm
build StatusItemButton.o: cxx StatusItemButton.mm build StatusItemButton.o: cxx StatusItemButton.mm
build JavaRunner.o: cxx JavaRunner.cpp build RouterTask.o: cxx RouterTask.mm
build clean: cleanup build clean: cleanup
build bundle: bundledir build bundle: bundledir
build copytobundle: copytobundledir | bundle clauncher 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 build appbundle: copyimgtobundle | clauncher bundle copytobundle

View File

@ -8,7 +8,7 @@
#include <iostream> #include <iostream>
#include <cctype> #include <cctype>
#include <locale> #include <locale>
#include <experimental/optional> #include "optional.hpp"
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFArray.h> #include <CoreFoundation/CFArray.h>

View File

@ -4,7 +4,10 @@
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <list> #include <list>
#include <experimental/optional> #include <sys/stat.h>
#include <stdlib.h>
#include <future>
#include <vector>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@ -23,8 +26,10 @@
#include "AppDelegate.h" #include "AppDelegate.h"
#include "StatusItemButton.h" #include "StatusItemButton.h"
#include "JavaRunner.h" #include "RouterTask.h"
#include "JavaHelper.h" #include "JavaHelper.h"
#include "fn.h"
#include "optional.hpp"
#define DEF_I2P_VERSION "0.9.35" #define DEF_I2P_VERSION "0.9.35"
#define APPDOMAIN "net.i2p.launcher" #define APPDOMAIN "net.i2p.launcher"
@ -43,6 +48,55 @@ JvmListSharedPtr gRawJvmList = nullptr;
@end @end
std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* 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 @implementation MenuBarCtrl
- (void) statusItemButtonLeftClick: (StatusItemButton *) button - (void) statusItemButtonLeftClick: (StatusItemButton *) button
@ -82,6 +136,11 @@ JvmListSharedPtr gRawJvmList = nullptr;
- (void) stopJavaRouterBtnHandler: (NSMenuItem *) menuItem - (void) stopJavaRouterBtnHandler: (NSMenuItem *) menuItem
{ {
NSLog(@"Clicked stopJavaRouterBtnHandler"); NSLog(@"Clicked stopJavaRouterBtnHandler");
if (getGlobalRouterObject().has_value())
{
//getGlobalRouterObject().value()->requestRouterShutdown();
NSLog(@"Requested shutdown");
}
} }
- (void) quitWrapperBtnHandler: (NSMenuItem *) menuItem - (void) quitWrapperBtnHandler: (NSMenuItem *) menuItem
@ -172,6 +231,11 @@ JvmListSharedPtr gRawJvmList = nullptr;
@implementation AppDelegate @implementation AppDelegate
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
shouldPresentNotification:(NSUserNotification *)notification {
return YES;
}
- (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList - (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList
{ {
NSString *appleScriptString = @"set jvmlist to {\"Newest\""; NSString *appleScriptString = @"set jvmlist to {\"Newest\"";
@ -224,13 +288,14 @@ JvmListSharedPtr gRawJvmList = nullptr;
CFPreferencesSetMultiple((CFDictionaryRef)dict, NULL, CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); CFPreferencesSetMultiple((CFDictionaryRef)dict, NULL, CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
//CFPreferencesSetAppValue(@"javaHome", (CFPropertyListRef)cfDefaultHome, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); //CFPreferencesSetAppValue(@"javaHome", (CFPropertyListRef)cfDefaultHome, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
if (self.enableVerboseLogging) NSLog(@"Default preferences stored!"); if (self.enableVerboseLogging) NSLog(@"Default preferences stored!");
} }
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Init application here // Init application here
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
// Start with user preferences // Start with user preferences
self.userPreferences = [NSUserDefaults standardUserDefaults]; self.userPreferences = [NSUserDefaults standardUserDefaults];
[self setApplicationDefaultPreferences]; [self setApplicationDefaultPreferences];
@ -246,7 +311,6 @@ JvmListSharedPtr gRawJvmList = nullptr;
auto javaHomePref = [self.userPreferences stringForKey:@"javaHome"]; auto javaHomePref = [self.userPreferences stringForKey:@"javaHome"];
if (self.enableVerboseLogging) NSLog(@"Java home from preferences: %@", javaHomePref); 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. // This is the only GUI the user experience on a regular basis.
self.menuBarCtrl = [[MenuBarCtrl alloc] init]; self.menuBarCtrl = [[MenuBarCtrl alloc] init];
@ -255,6 +319,16 @@ JvmListSharedPtr gRawJvmList = nullptr;
if (self.enableVerboseLogging) NSLog(@"Appdomain is: %@", appDomain); if (self.enableVerboseLogging) NSLog(@"Appdomain is: %@", appDomain);
NSLog(@"We should have started the statusbar object by now..."); 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:)]; //[statusBarButton setAction:@selector(itemClicked:)];
//dispatch_async(dispatch_get_main_queue(), ^{ //dispatch_async(dispatch_get_main_queue(), ^{
//}); //});
@ -273,33 +347,121 @@ JvmListSharedPtr gRawJvmList = nullptr;
return std::string([val UTF8String]);; return std::string([val UTF8String]);;
}; };
auto getJavaBin = [&getJavaHomeLambda]() -> std::string {
auto launchLambda = [&pref](JavaRunner *javaRun) { // Get Java home
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 javaHome = getJavaHomeLambda(); auto javaHome = getJavaHomeLambda();
trim(javaHome); // Trim to remove endline trim(javaHome); // Trim to remove endline
auto javaBin = std::string(javaHome); auto javaBin = std::string(javaHome);
javaBin += "/bin/java"; // Append java binary to path. javaBin += "/bin/java"; // Append java binary to path.
//printf("hello world: %s\n", javaBin.c_str()); return javaBin;
if (self.enableVerboseLogging) NSLog(@"Defaults: %@", [pref dictionaryRepresentation]); };
auto r = new JavaRunner{ javaBin, launchLambda, callbackAfterExit }; auto buildClassPath = [](std::string basePath) -> std::vector<std::string> {
r->execute(); 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) { } catch (std::exception &err) {
std::cerr << "Exception: " << err.what() << std::endl; std::cerr << "Exception: " << err.what() << std::endl;
} }
} }

View File

@ -21,45 +21,56 @@ import java.util.zip.ZipFile;
*/ */
public class BaseExtractor extends EnvCheck { 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)) { try(ZipFile file = new ZipFile(zipFilename)) {
FileSystem fileSystem = FileSystems.getDefault(); FileSystem fileSystem = FileSystems.getDefault();
Enumeration<? extends ZipEntry> entries = file.entries(); Enumeration<? extends ZipEntry> 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()) { while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement(); ZipEntry entry = entries.nextElement();
if (printDebug) System.out.println("Found entry: "+entry.toString());
if (entry.isDirectory()) { if (entry.isDirectory()) {
System.out.println("Creating Directory:" + destinationPath + entry.getName()); if (printDebug) System.out.println("Creating Directory:" + destinationPath + "/" + entry.getName());
Files.createDirectories(fileSystem.getPath(destinationPath + entry.getName())); Files.createDirectories(fileSystem.getPath(destinationPath + "/" + entry.getName()));
} else { } else {
InputStream is = file.getInputStream(entry); InputStream is = file.getInputStream(entry);
BufferedInputStream bis = new BufferedInputStream(is); BufferedInputStream bis = new BufferedInputStream(is);
String uncompressedFileName = destinationPath + entry.getName(); String uncompressedFileName = destinationPath + "/" + entry.getName();
Path uncompressedFilePath = fileSystem.getPath(uncompressedFileName); Path uncompressedFilePath = fileSystem.getPath(uncompressedFileName);
Files.createFile(uncompressedFilePath); Files.createFile(uncompressedFilePath);
FileOutputStream fileOutput = new FileOutputStream(uncompressedFileName); FileOutputStream fileOutput = new FileOutputStream(uncompressedFileName);
while (bis.available() > 0) fileOutput.write(bis.read()); while (bis.available() > 0) fileOutput.write(bis.read());
fileOutput.close(); fileOutput.close();
System.out.println("Written :" + entry.getName()); if (printDebug) System.out.println("Written :" + entry.getName());
} }
} }
} catch (IOException e) { } catch (IOException e) {
// //
System.err.println("Exception in extractor!");
System.err.println(e.toString());
} }
} }
public BaseExtractor(String[] args) { public BaseExtractor(String[] args) {
super(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) { 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"));
} }
} }