source: launchers/macosx/main.mm @ 7dbf568

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

OSX Launcher: Refactor deployment code to own file, + code cleanups.

  • Property mode set to 100644
File size: 8.6 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#import "Deployer.h"
29#include "logger_c.h"
30
31#ifdef __cplusplus
32#include <string>
33
34#include "include/subprocess.hpp"
35#include "include/strutil.hpp"
36
37#include "Logger.h"
38#include "LoggerWorker.hpp"
39
40using namespace subprocess;
41#endif
42
43#define debug(format, ...) CFShow([NSString stringWithFormat:format, ## __VA_ARGS__]);
44
45
46
47@interface AppDelegate () <NSUserNotificationCenterDelegate, NSApplicationDelegate>
48@end
49
50
51@implementation ExtractMetaInfo : NSObject
52@end
53
54
55@implementation AppDelegate
56
57- (void) awakeFromNib {
58}
59
60
61- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
62     shouldPresentNotification:(NSUserNotification *)notification {
63  return YES;
64}
65
66- (void)extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion
67{
68  auto deployer = [[I2PDeployer alloc] initWithMetaInfo:self.metaInfo];
69  [deployer extractI2PBaseDir:completion];
70}
71
72- (void)setApplicationDefaultPreferences {
73  [self.userPreferences registerDefaults:@{
74    @"enableLogging": @YES,
75    @"enableVerboseLogging": @YES,
76    @"autoStartRouter": @YES,
77    @"startRouterAtLogin": @NO,
78    @"startRouterAtStartup": @NO,
79    @"letRouterLiveEvenLauncherDied": @NO,
80    @"i2pBaseDirectory": (NSString *)CFStringCreateWithCString(NULL, const_cast<const char *>(getDefaultBaseDir().c_str()), kCFStringEncodingUTF8)
81  }];
82
83  auto dict = [self.userPreferences dictionaryRepresentation];
84  [self.userPreferences setPersistentDomain:dict forName:NSAPPDOMAIN];
85
86  CFPreferencesSetMultiple((CFDictionaryRef)dict, NULL, CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
87  CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
88
89  if (self.enableVerboseLogging) NSLog(@"Default preferences stored!");
90}
91
92
93- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
94  // Init application here
95 
96  self.swiftRuntime = [[SwiftMainDelegate alloc] init];
97 
98  // This setup allows the application to send notifications
99  [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
100 
101 
102  // Start with user preferences
103  self.userPreferences = [NSUserDefaults standardUserDefaults];
104  [self setApplicationDefaultPreferences];
105  self.enableLogging = [self.userPreferences boolForKey:@"enableLogging"];
106  self.enableVerboseLogging = [self.userPreferences boolForKey:@"enableVerboseLogging"];
107  // In case we are unbundled, make us a proper UI application
108  [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
109  [NSApp activateIgnoringOtherApps:YES];
110
111
112#ifdef __cplusplus
113
114  RouterProcessStatus* routerStatus = [[RouterProcessStatus alloc] init];
115  std::string i2pBaseDir(getDefaultBaseDir());
116  MLOG(INFO) << "i2pBaseDir = " << i2pBaseDir.c_str();
117  bool shouldAutoStartRouter = false;
118 
119  // Initialize the Swift environment (the UI components)
120  [self.swiftRuntime applicationDidFinishLaunching];
121 
122  // TODO: Make the port a setting which defaults to 7657
123  if (port_check(7657) != 0)
124  {
125    NSLog(@"Seems i2p is already running - I will not start the router (port 7657 is in use..)");
126    sendUserNotification(@"Found already running router", @"TCP port 7657 seem to be used by another i2p instance.");
127   
128    [routerStatus setRouterStatus: true];
129    [routerStatus setRouterRanByUs: false];
130    shouldAutoStartRouter = false;
131  } else {
132    shouldAutoStartRouter = true;
133  }
134
135  NSBundle *launcherBundle = [NSBundle mainBundle];
136 
137 
138  // Helper object to hold statefull path information
139  self.metaInfo = [[ExtractMetaInfo alloc] init];
140  self.metaInfo.i2pBase = [NSString stringWithUTF8String:i2pBaseDir.c_str()];
141  self.metaInfo.javaBinary = [routerStatus getJavaHome];
142  self.metaInfo.jarFile = [launcherBundle pathForResource:@"launcher" ofType:@"jar"];
143  self.metaInfo.zipFile = [launcherBundle pathForResource:@"base" ofType:@"zip"];
144
145  std::string basearg("-Di2p.dir.base=");
146  basearg += i2pBaseDir;
147
148  std::string jarfile("-cp ");
149  jarfile += [self.metaInfo.zipFile UTF8String];
150 
151  // Might be hard to read if you're not used to Objective-C
152  // But this is a "function call" that contains a "callback function"
153  /*[routerStatus listenForEventWithEventName:@"router_can_start" callbackActionFn:^(NSString* information) {
154    NSLog(@"Got signal, router can be started");
155    [[SBridge sharedInstance] startupI2PRouter:self.metaInfo.i2pBase];
156  }];*/
157 
158  // This will trigger the router start after an upgrade.
159  [routerStatus listenForEventWithEventName:@"router_must_upgrade" callbackActionFn:^(NSString* information) {
160    NSLog(@"Got signal, router must be deployed from base.zip");
161    [self extractI2PBaseDir:^(BOOL success, NSError *error) {
162      if (success) {
163        sendUserNotification(@"I2P is done extracting", @"I2P is now installed and ready to run!");
164        NSLog(@"Done extracting I2P");
165        [routerStatus triggerEventWithEn:@"extract_completed" details:@"upgrade complete"];
166      } else {
167        NSLog(@"Error while extracting I2P");
168        [routerStatus triggerEventWithEn:@"extract_errored" details:[NSString stringWithFormat:@"%@", error]];
169      }
170    }];
171  }];
172 
173  NSString *nsI2PBaseStr = [NSString stringWithUTF8String:i2pBaseDir.c_str()];
174
175  [routerStatus listenForEventWithEventName:@"extract_completed" callbackActionFn:^(NSString* information) {
176    NSLog(@"Time to detect I2P version in install directory");
177    [self.swiftRuntime findInstalledI2PVersion];
178  }];
179 
180  //struct stat sb;
181  //if ( !(stat(i2pBaseDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) )
182  BOOL shouldBeTrueOnReturnDir = YES;
183  if (! [NSFileManager.defaultManager fileExistsAtPath: nsI2PBaseStr isDirectory: &shouldBeTrueOnReturnDir])
184  {
185    // I2P is not extracted.
186    if (shouldBeTrueOnReturnDir) {
187      if (self.enableVerboseLogging) NSLog(@"I2P Directory don't exists!");
188      [routerStatus triggerEventWithEn:@"router_must_upgrade" details:@"deploy needed"];
189    } else {
190      // TODO: handle if i2p path exists but it's not a dir.
191    }
192  } else {
193    // I2P was already found extracted
194    NSString *nsI2pJar = [NSString stringWithFormat:@"%@/lib/i2p.jar", nsI2PBaseStr];
195   
196    // But does I2PBASE/lib/i2p.jar exists?
197    if ([NSFileManager.defaultManager fileExistsAtPath:nsI2pJar]) {
198      NSLog(@"Time to detect I2P version in install directory");
199      [self.swiftRuntime findInstalledI2PVersion];
200    } else {
201      // The directory exists, but not i2p.jar - most likely we're in mid-extraction state.
202      [routerStatus triggerEventWithEn:@"router_must_upgrade" details:@"deploy needed"];
203    }
204  }
205 
206#endif
207}
208
209/**
210 *
211 * Exit sequence
212 *
213 **/
214- (void)applicationWillTerminate:(NSNotification *)aNotification {
215  // Tear down here
216  [self.swiftRuntime applicationWillTerminate];
217  NSString *string = @"applicationWillTerminate executed";
218  NSLog(@"%@", string);
219  [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil];
220}
221
222
223/* wrapper for main */
224- (AppDelegate *)initWithArgc:(int)argc argv:(const char **)argv {
225  return self;
226}
227@end
228
229#ifdef __cplusplus
230namespace {
231  const std::string logDirectory = getDefaultLogDir();
232}
233#endif
234
235int main(int argc, const char **argv)
236{
237  NSApplication *app = [NSApplication sharedApplication];
238
239#ifdef __cplusplus
240  mkdir(logDirectory.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
241 
242  SharedLogWorker logger("I2PLauncher", logDirectory);
243  MeehLog::initializeLogging(&logger);
244 
245  MLOG(INFO) << "Application is starting up";
246#endif
247 
248  AppDelegate *appDelegate = [[AppDelegate alloc] initWithArgc:argc argv:argv];
249  app.delegate = appDelegate;
250  auto mainBundle = [NSBundle mainBundle];
251  NSString* stringNameBundle = [mainBundle objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey];
252  if ([[NSRunningApplication runningApplicationsWithBundleIdentifier:[mainBundle bundleIdentifier]] count] > 1) {
253    [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Another copy of %@ is already running.",stringNameBundle]
254                     defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"This copy will now quit."] runModal];
255   
256    [NSApp terminate:nil];
257  }
258#pragma GCC diagnostic push
259#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
260  [NSBundle loadNibNamed:@"I2Launcher" owner:NSApp];
261#pragma GCC diagnostic pop
262 
263  [NSApp run];
264  return 0;
265}
266
267
268
Note: See TracBrowser for help on using the repository browser.