diff --git a/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift b/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift index ed5ef7857c36c09d45dec6c33fd2333275ce28f2..719e0ed0120a70ddb71ab118e49a2eedd002fae5 100644 --- a/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift +++ b/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift @@ -93,7 +93,8 @@ class Logger { } @objc static func openLink(url: String) { - SBridge.sharedInstance().openUrl(url) + NSLog("Trying to open \(url)") + NSWorkspace.shared().open(NSURL(string: url)! as URL) } @objc func applicationWillTerminate() { diff --git a/launchers/macosx/I2PLauncher/Utils/FolderContentMonitor.swift b/launchers/macosx/I2PLauncher/Utils/FolderContentMonitor.swift new file mode 100644 index 0000000000000000000000000000000000000000..168989344a900e2635b76e780c537c3afaea5533 --- /dev/null +++ b/launchers/macosx/I2PLauncher/Utils/FolderContentMonitor.swift @@ -0,0 +1,111 @@ +// +// FolderContentMonitor.swift +// I2PLauncher +// +// Created by Mikal Villa on 28/09/2018. +// Copyright © 2018 The I2P Project. All rights reserved. +// + +import Foundation + +/* + infix operator ~> + + private let queue = DispatchQueue(label: "background") + + func ~> <R> ( + backgroundClosure: @escaping () -> R, + mainClosure: @escaping (_ result: R) -> ()) + { + queue.async { + let result = backgroundClosure() + DispatchQueue.main.async(execute: { + mainClosure(result) + }) + } + } + */ + +public struct Event: CustomStringConvertible { + + public let eventId: FSEventStreamEventId + public let eventPath: String + public let eventFlags: FSEventStreamEventFlags + + public var description: String { + return "\(eventId) - \(eventFlags) - \(eventPath)" + } +} + +public class FolderContentMonitor { + + let callback: (Event) -> Void + + public init(pathsToWatch: [String], sinceWhen: FSEventStreamEventId = FSEventStreamEventId(kFSEventStreamEventIdSinceNow), callback: @escaping (Event) -> Void) { + + self.lastEventId = sinceWhen + self.pathsToWatch = pathsToWatch + self.callback = callback + } + + deinit { + stop() + } + + // MARK: - Private Properties + private let eventCallback: FSEventStreamCallback = { + (stream: ConstFSEventStreamRef, + contextInfo: UnsafeMutableRawPointer?, + numEvents: Int, + eventPaths: UnsafeMutableRawPointer, + eventFlags: UnsafePointer<FSEventStreamEventFlags>, + eventIds: UnsafePointer<FSEventStreamEventId>) in + + let fileSystemWatcher: FolderContentMonitor = unsafeBitCast(contextInfo, to: FolderContentMonitor.self) + + guard let paths = unsafeBitCast(eventPaths, to: NSArray.self) as? [String] else { return } + + for index in 0..<numEvents { + fileSystemWatcher.processEvent(eventId: eventIds[index], eventPath: paths[index], eventFlags: eventFlags[index]) + } + + fileSystemWatcher.lastEventId = eventIds[numEvents - 1] + } + + private let pathsToWatch: [String] + private var started = false + private var streamRef: FSEventStreamRef! + + private func processEvent(eventId: FSEventStreamEventId, eventPath: String, eventFlags: FSEventStreamEventFlags) { + + let event = Event(eventId: eventId, eventPath: eventPath, eventFlags: eventFlags) + callback(event) + } + + public private(set) var lastEventId: FSEventStreamEventId + + public func start() { + guard started == false else { return } + + var context = FSEventStreamContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) + context.info = Unmanaged.passUnretained(self).toOpaque() + let flags = UInt32(kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagFileEvents) + streamRef = FSEventStreamCreate(kCFAllocatorDefault, eventCallback, &context, pathsToWatch as CFArray, lastEventId, 0, flags) + + FSEventStreamScheduleWithRunLoop(streamRef, CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue) + FSEventStreamStart(streamRef) + + started = true + } + + public func stop() { + guard started == true else { return } + + FSEventStreamStop(streamRef) + FSEventStreamInvalidate(streamRef) + FSEventStreamRelease(streamRef) + streamRef = nil + + started = false + } +} diff --git a/launchers/macosx/I2PLauncher/logger_c.h b/launchers/macosx/I2PLauncher/logger_c.h index 95b11c82363013138623792c33a7695c571e3193..102204b38f18bde1584459a1ecf24724739d9a32 100644 --- a/launchers/macosx/I2PLauncher/logger_c.h +++ b/launchers/macosx/I2PLauncher/logger_c.h @@ -82,4 +82,7 @@ inline void MLog(int loglevel, NSString* format, ...) } + +#define MMLog(format_string,...) ((MLog(1, [NSString stringWithFormat:format_string,##__VA_ARGS__]))) + #endif /* logger_c_h */ diff --git a/launchers/macosx/I2PLauncher/routermgmt/RouterManager.swift b/launchers/macosx/I2PLauncher/routermgmt/RouterManager.swift index e274d2017e2d57447d699094b3813cf3688b7a92..4776b84a377cd64c707a9a48f747ab67d28a14ec 100644 --- a/launchers/macosx/I2PLauncher/routermgmt/RouterManager.swift +++ b/launchers/macosx/I2PLauncher/routermgmt/RouterManager.swift @@ -25,6 +25,7 @@ class RouterManager : NSObject { let routerRunner = RouterRunner() var logViewStorage: NSTextStorage? + var lastRouterPid : String? = nil private static func handleRouterException(information:Any?) { Logger.MLog(level:1,"event! - handle router exception") @@ -94,6 +95,27 @@ class RouterManager : NSObject { routerManager.eventManager.listenTo(eventName: "router_already_running", action: handleRouterAlreadyStarted) routerManager.eventManager.listenTo(eventName: "router_can_start", action: routerManager.routerRunner.StartAgent) routerManager.eventManager.listenTo(eventName: "router_can_setup", action: routerManager.routerRunner.SetupAgent) + + //routerManager.eventManager.listenTo(eventName: "launch_agent_running", action: routerManager.routerRunner.onLoadedAgent) + + routerManager.eventManager.listenTo(eventName: "launch_agent_running", action: { + let agent = RouterRunner.launchAgent! + let agentStatus = agent.status() + switch agentStatus { + case .running(let pid): + DispatchQueue.main.async { + routerManager.eventManager.trigger(eventName: "router_start", information: String(pid)) + routerManager.routerRunner.routerStatus.setRouterStatus(true) + routerManager.routerRunner.routerStatus.setRouterRanByUs(true) + routerManager.eventManager.trigger(eventName: "router_pid", information: String(pid)) + } + break + + default: + NSLog("wtf, agent status = \(agentStatus)") + break + } + }) return routerManager }() diff --git a/launchers/macosx/I2PLauncher/routermgmt/RouterRunner.swift b/launchers/macosx/I2PLauncher/routermgmt/RouterRunner.swift index aa416f7709d8a9576a8e41c4051f9c2215277414..3f11a0c9a9b4f364380e78b3fad2fd492ab75f63 100644 --- a/launchers/macosx/I2PLauncher/routermgmt/RouterRunner.swift +++ b/launchers/macosx/I2PLauncher/routermgmt/RouterRunner.swift @@ -22,7 +22,7 @@ class RouterRunner: NSObject { let domainLabel = "net.i2p.macosx.I2PRouter" - let plistName = "net.i2p.macosx.I2PRouterAgent.plist" + let plistName = "net.i2p.macosx.I2PRouter.plist" let defaultStartupCommand:String = "/usr/libexec/java_home" @@ -35,6 +35,10 @@ class RouterRunner: NSObject { let appSupportPath = FileManager.default.urls(for: FileManager.SearchPathDirectory.applicationSupportDirectory, in: FileManager.SearchPathDomainMask.userDomainMask) + override init() { + super.init() + } + func SetupAgent() { let agent = SetupAndReturnAgent() RouterRunner.launchAgent = agent @@ -106,78 +110,70 @@ class RouterRunner: NSObject { let shouldStartupAtLogin = userPreferences.bool(forKey: "startRouterAtLogin") agent.runAtLoad = shouldStartupAtLogin agent.keepAlive = true - - do { - - try LaunchAgentManager.shared.write(agent, called: self.plistName) - sleep(1) - try LaunchAgentManager.shared.load(agent) - sleep(1) - - let agentStatus = LaunchAgentManager.shared.status(agent) - switch agentStatus { - case .running: - break - case .loaded: - break - case .unloaded: - sleep(2) - break + DispatchQueue(label: "background_starter").async { + do { + try LaunchAgentManager.shared.write(agent, called: self.plistName) + sleep(1) + try LaunchAgentManager.shared.load(agent) + sleep(1) + + let agentStatus = LaunchAgentManager.shared.status(agent) + switch agentStatus { + case .running: + break + case .loaded: + DispatchQueue.main.async { + RouterManager.shared().eventManager.trigger(eventName: "router_can_start", information: agent) + } + break + case .unloaded: + break + } + } catch { + DispatchQueue.main.async { + RouterManager.shared().eventManager.trigger(eventName: "router_setup_error", information: "\(error)") + } } - - - RouterManager.shared().eventManager.trigger(eventName: "router_can_start", information: agent) - } catch { - RouterManager.shared().eventManager.trigger(eventName: "router_setup_error", information: "\(error)") } return agent } - func StartAgent(information:Any?) { - let agent = RouterRunner.launchAgent! - LaunchAgentManager.shared.start(agent) - sleep(1) - let agentStatus = agent.status() - switch agentStatus { - case .running(let pid): - RouterManager.shared().eventManager.trigger(eventName: "router_start", information: String(pid)) - routerStatus.setRouterStatus(true) - routerStatus.setRouterRanByUs(true) - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - // Delayed message to ensure UI has been initialized. - RouterManager.shared().eventManager.trigger(eventName: "router_pid", information: String(pid)) - } - break - - default: break + + func StartAgent(_ information:Any? = nil) { + let agent = RouterRunner.launchAgent ?? information as! LaunchAgent + DispatchQueue(label: "background_block").async { + LaunchAgentManager.shared.start(agent, { (proc) in + NSLog("Will call onLaunchdStarted") + }) } } - func StopAgent() { - var agentStatus = LaunchAgentManager.shared.status(RouterRunner.launchAgent!) - switch agentStatus { - case .running: - LaunchAgentManager.shared.stop(RouterRunner.launchAgent!) - break - case .loaded, .unloaded: - try! LaunchAgentManager.shared.load(RouterRunner.launchAgent!) - routerStatus.setRouterStatus(false) - routerStatus.setRouterRanByUs(false) - RouterManager.shared().eventManager.trigger(eventName: "router_stop", information: "ok") - return; - break - default: break - } - sleep(1) - agentStatus = LaunchAgentManager.shared.status(RouterRunner.launchAgent!) - switch agentStatus { - case .loaded, .unloaded: - try! LaunchAgentManager.shared.load(RouterRunner.launchAgent!) - routerStatus.setRouterStatus(false) - routerStatus.setRouterRanByUs(false) - RouterManager.shared().eventManager.trigger(eventName: "router_stop", information: "ok") - break - default: break + func StopAgent(_ callback: @escaping () -> () = {}) { + let agentStatus = LaunchAgentManager.shared.status(RouterRunner.launchAgent!) + DispatchQueue(label: "background_block").async { + do { + switch agentStatus { + case .running: + // For now we need to use unload to stop it. + try LaunchAgentManager.shared.unload(RouterRunner.launchAgent!, { (proc) in + // Called when stop is actually executed + proc.waitUntilExit() + DispatchQueue.main.async { + RouterManager.shared().eventManager.trigger(eventName: "router_stop", information: "ok") + callback() + } + }) + try LaunchAgentManager.shared.load(RouterRunner.launchAgent!) + break + case .unloaded: + // Seems it sometimes get unloaded on stop, we load it again. + try! LaunchAgentManager.shared.load(RouterRunner.launchAgent!) + return + default: break + } + } catch { + NSLog("Error \(error)") + } } }