source: launchers/macosx/main.mm @ 2233f7f

Last change on this file since 2233f7f was 2233f7f, checked in by meeh <meeh@…>, 20 months ago

Mac OSX Launcher:

  • Update readme about event manager
  • RouterTask? can now detect a running router by scanning processes&arguments for i2p.jar
  • The logger will log to OSX's default: ~/Library/Logs?/I2P/[whatever].log
  • Property mode set to 100644
File size: 10.9 KB
Line 
1#include <functional>
2#include <memory>
3#include <iostream>
4#include <algorithm>
5#include <string>
6#include <list>
7#include <sys/stat.h>
8#include <stdlib.h>
9#include <future>
10#include <vector>
11
12#import <Foundation/Foundation.h>
13#import <Foundation/NSFileManager.h>
14#include <CoreFoundation/CFPreferences.h>
15
16#import <objc/Object.h>
17#import <Cocoa/Cocoa.h>
18#import <AppKit/AppKit.h>
19#import <AppKit/NSApplication.h>
20
21#import "I2PLauncher-Swift.h"
22
23#include "AppDelegate.h"
24#include "RouterTask.h"
25#include "include/fn.h"
26#include "include/portcheck.h"
27#import "SBridge.h"
28
29#ifdef __cplusplus
30#include <string>
31
32#include "include/subprocess.hpp"
33#include "include/strutil.hpp"
34
35#include "Logger.h"
36#include "LoggerWorker.hpp"
37
38using namespace subprocess;
39#endif
40
41#define debug(format, ...) CFShow([NSString stringWithFormat:format, ## __VA_ARGS__]);
42
43
44
45@interface AppDelegate () <NSUserNotificationCenterDelegate, NSApplicationDelegate>
46@end
47
48
49@implementation ExtractMetaInfo : NSObject
50@end
51
52
53@implementation AppDelegate
54
55- (void) awakeFromNib {
56}
57
58
59- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
60     shouldPresentNotification:(NSUserNotification *)notification {
61  return YES;
62}
63
64#ifdef __cplusplus
65
66- (void)extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion
67{
68 
69  NSBundle *launcherBundle = [NSBundle mainBundle];
70  auto homeDir = RealHomeDirectory();
71  NSLog(@"Home directory is %s", homeDir);
72 
73  std::string basePath(homeDir);
74  basePath.append("/Library/I2P");
75 
76  auto jarResPath = [launcherBundle pathForResource:@"launcher" ofType:@"jar"];
77  NSLog(@"Trying to load launcher.jar from url = %@", jarResPath);
78  self.metaInfo.jarFile = jarResPath;
79  self.metaInfo.zipFile = [launcherBundle pathForResource:@"base" ofType:@"zip"];
80 
81  NSParameterAssert(basePath.c_str());
82  NSError *error = NULL;
83  BOOL success = NO;
84  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
85
86
87    try {
88      std::string basearg("-Di2p.dir.base=");
89      basearg += basePath;
90
91      std::string zippath("-Di2p.base.zip=");
92      zippath += [self.metaInfo.zipFile UTF8String];
93
94      std::string jarfile("-cp ");
95      jarfile += [self.metaInfo.jarFile UTF8String];
96
97      // Create directory
98      mkdir(basePath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
99
100      auto cli = defaultFlagsForExtractorJob;
101      setenv("I2PBASE", basePath.c_str(), true);
102      setenv("ZIPPATH", zippath.c_str(), true);
103      //setenv("DYLD_LIBRARY_PATH",".:/usr/lib:/lib:/usr/local/lib", true);
104
105      cli.push_back(basearg);
106      cli.push_back(zippath);
107      cli.push_back(jarfile);
108      cli.push_back("net.i2p.launchers.BaseExtractor");
109      auto rs = [[RouterProcessStatus alloc] init];
110     
111      std::string execStr = std::string([rs.getJavaHome UTF8String]);
112      for_each(cli, [&execStr](std::string str){ execStr += std::string(" ") + str; });
113
114      NSLog(@"Trying cmd: %@", [NSString stringWithUTF8String:execStr.c_str()]);
115      sendUserNotification(APP_IDSTR, @"Please hold on while we extract I2P. You'll get a new message once done!");
116      int extractStatus = Popen(execStr.c_str(), environment{{
117        {"ZIPPATH", zippath.c_str()},
118        {"I2PBASE", basePath.c_str()}
119      }}).wait();
120      NSLog(@"Extraction exit code %@",[NSString stringWithUTF8String:(std::to_string(extractStatus)).c_str()]);
121      if (extractStatus == 0) {
122        NSLog(@"Extraction process done");
123      } else {
124        NSLog(@"Something went wrong");
125      }
126
127      // All done. Assume success and error are already set.
128      dispatch_async(dispatch_get_main_queue(), ^{
129        if (completion) {
130          completion(success, error);
131        }
132      });
133     
134     
135    } catch (OSError &err) {
136      auto errMsg = [NSString stringWithUTF8String:err.what()];
137      NSLog(@"Exception: %@", errMsg);
138      sendUserNotification(APP_IDSTR, [NSString stringWithFormat:@"Error: %@", errMsg]);
139    }
140  });
141}
142
143- (void)setApplicationDefaultPreferences {
144  [self.userPreferences registerDefaults:@{
145    @"enableLogging": @YES,
146    @"enableVerboseLogging": @YES,
147    @"autoStartRouter": @YES,
148    @"i2pBaseDirectory": (NSString *)CFStringCreateWithCString(NULL, const_cast<const char *>(getDefaultBaseDir().c_str()), kCFStringEncodingUTF8)
149  }];
150
151  auto dict = [self.userPreferences dictionaryRepresentation];
152  [self.userPreferences setPersistentDomain:dict forName:NSAPPDOMAIN];
153
154  CFPreferencesSetMultiple((CFDictionaryRef)dict, NULL, CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
155  CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
156
157  if (self.enableVerboseLogging) NSLog(@"Default preferences stored!");
158}
159
160#endif
161
162
163- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
164  // Init application here
165 
166  self.swiftRuntime = [[SwiftMainDelegate alloc] init];
167 
168  // This setup allows the application to send notifications
169  [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
170 
171 
172  // Start with user preferences
173  self.userPreferences = [NSUserDefaults standardUserDefaults];
174  [self setApplicationDefaultPreferences];
175  self.enableLogging = [self.userPreferences boolForKey:@"enableLogging"];
176  self.enableVerboseLogging = [self.userPreferences boolForKey:@"enableVerboseLogging"];
177  // In case we are unbundled, make us a proper UI application
178  [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
179  [NSApp activateIgnoringOtherApps:YES];
180
181
182#ifdef __cplusplus
183
184  RouterProcessStatus* routerStatus = [[RouterProcessStatus alloc] init];
185  std::string i2pBaseDir(getDefaultBaseDir());
186  NSLog(@"i2pBaseDir = %s", i2pBaseDir.c_str());
187  bool shouldAutoStartRouter = false;
188 
189  // TODO: Make the port a setting which defaults to 7657
190  if (port_check(7657) != 0)
191  {
192    NSLog(@"Seems i2p is already running - I will not start the router (port 7657 is in use..)");
193    sendUserNotification(@"Found already running router", @"TCP port 7657 seem to be used by another i2p instance.");
194   
195    [routerStatus setRouterStatus: true];
196    [routerStatus setRouterRanByUs: false];
197    shouldAutoStartRouter = false;
198  } else {
199    shouldAutoStartRouter = true;
200  }
201
202  NSBundle *launcherBundle = [NSBundle mainBundle];
203 
204 
205  // Helper object to hold statefull path information
206  self.metaInfo = [[ExtractMetaInfo alloc] init];
207  self.metaInfo.i2pBase = [NSString stringWithUTF8String:i2pBaseDir.c_str()];
208  self.metaInfo.javaBinary = [routerStatus getJavaHome];
209  self.metaInfo.jarFile = [launcherBundle pathForResource:@"launcher" ofType:@"jar"];
210  self.metaInfo.zipFile = [launcherBundle pathForResource:@"base" ofType:@"zip"];
211
212  std::string basearg("-Di2p.dir.base=");
213  basearg += i2pBaseDir;
214
215  std::string jarfile("-cp ");
216  jarfile += [self.metaInfo.zipFile UTF8String];
217 
218  // Might be hard to read if you're not used to Objective-C
219  // But this is a "function call" that contains a "callback function"
220  [routerStatus listenForEventWithEventName:@"router_can_start" callbackActionFn:^(NSString* information) {
221    NSLog(@"Got signal, router can be started");
222    [[SBridge sharedInstance] startupI2PRouter:self.metaInfo.i2pBase javaBinPath:self.metaInfo.javaBinary];
223  }];
224 
225  // This will trigger the router start after an upgrade.
226  [routerStatus listenForEventWithEventName:@"router_must_upgrade" callbackActionFn:^(NSString* information) {
227    NSLog(@"Got signal, router must be deployed from base.zip");
228    [self extractI2PBaseDir:^(BOOL success, NSError *error) {
229      if (success && error != nil) {
230        sendUserNotification(@"I2P is done extracting", @"I2P is now installed and ready to run!");
231        NSLog(@"Done extracting I2P");
232        [routerStatus triggerEventWithEn:@"extract_completed" details:@"upgrade complete"];
233      } else {
234        NSLog(@"Error while extracting I2P");
235        [routerStatus triggerEventWithEn:@"extract_errored" details:[NSString stringWithFormat:@"%@", error]];
236      }
237    }];
238  }];
239 
240  // Initialize the Swift environment (the UI components)
241  [self.swiftRuntime applicationDidFinishLaunching];
242 
243  NSString *nsI2PBaseStr = [NSString stringWithUTF8String:i2pBaseDir.c_str()];
244
245 
246  //struct stat sb;
247  //if ( !(stat(i2pBaseDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) )
248  BOOL shouldBeTrueOnReturnDir = YES;
249  if (! [NSFileManager.defaultManager fileExistsAtPath: nsI2PBaseStr isDirectory: &shouldBeTrueOnReturnDir])
250  {
251    // I2P is not extracted.
252    if (shouldBeTrueOnReturnDir) {
253      if (self.enableVerboseLogging) NSLog(@"I2P Directory don't exists!");
254      [routerStatus triggerEventWithEn:@"router_must_upgrade" details:@"deploy needed"];
255    } else {
256      // TODO: handle if i2p path exists but it's not a dir.
257    }
258  } else {
259    // I2P was already found extracted
260    NSString *nsI2pJar = [NSString stringWithFormat:@"%@/lib/i2p.jar", nsI2PBaseStr];
261   
262    // But does I2PBASE/lib/i2p.jar exists?
263    if ([NSFileManager.defaultManager fileExistsAtPath:nsI2pJar]) {
264      NSLog(@"Time to detect I2P version in install directory");
265      [self.swiftRuntime findInstalledI2PVersion];
266    } else {
267      // The directory exists, but not i2p.jar - most likely we're in mid-extraction state.
268      [routerStatus listenForEventWithEventName:@"extract_completed" callbackActionFn:^(NSString* information) {
269        NSLog(@"Time to detect I2P version in install directory");
270        [self.swiftRuntime findInstalledI2PVersion];
271      }];
272    }
273  }
274 
275#endif
276}
277
278/**
279 *
280 * Exit sequence
281 *
282 **/
283- (void)applicationWillTerminate:(NSNotification *)aNotification {
284  // Tear down here
285  NSString *string = @"applicationWillTerminate executed";
286  NSLog(@"%@", string);
287  [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil];
288}
289
290
291/* wrapper for main */
292- (AppDelegate *)initWithArgc:(int)argc argv:(const char **)argv {
293  return self;
294}
295@end
296
297#ifdef __cplusplus
298namespace {
299  const std::string logDirectory = getDefaultLogDir();
300}
301#endif
302
303int main(int argc, const char **argv)
304{
305  NSApplication *app = [NSApplication sharedApplication];
306
307#ifdef __cplusplus
308  mkdir(logDirectory.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
309 
310  SharedLogWorker logger("I2PLauncher", logDirectory);
311  MeehLog::initializeLogging(&logger);
312 
313  MLOG(INFO) << "Application is starting up";
314#endif
315 
316  AppDelegate *appDelegate = [[AppDelegate alloc] initWithArgc:argc argv:argv];
317  app.delegate = appDelegate;
318  auto mainBundle = [NSBundle mainBundle];
319  NSString* stringNameBundle = [mainBundle objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey];
320  if ([[NSRunningApplication runningApplicationsWithBundleIdentifier:[mainBundle bundleIdentifier]] count] > 1) {
321    [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Another copy of %@ is already running.",stringNameBundle]
322                     defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"This copy will now quit."] runModal];
323   
324    [NSApp terminate:nil];
325  }
326#pragma GCC diagnostic push
327#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
328  [NSBundle loadNibNamed:@"I2Launcher" owner:NSApp];
329#pragma GCC diagnostic pop
330 
331  [NSApp run];
332  return 0;
333}
334
335
336
Note: See TracBrowser for help on using the repository browser.