diff --git a/launchers/macosx/AppDelegate.h b/launchers/macosx/AppDelegate.h index c1ea98d460e3d1c310252727fc60450b752e60f5..171c6b9d31f82c246aa12b09711bfa7f6b6cc628 100644 --- a/launchers/macosx/AppDelegate.h +++ b/launchers/macosx/AppDelegate.h @@ -21,6 +21,7 @@ #include "version.h" @class SwiftMainDelegate; +@class I2PDeployer; @protocol SwiftMainDelegateProto - (void)applicationDidFinishLaunching:(NSNotification *)aNotification; @@ -92,6 +93,7 @@ inline void sendUserNotification(NSString* title, NSString* informativeText, boo @property (assign) SwiftMainDelegate *swiftRuntime; @property (assign) NSUserDefaults *userPreferences; @property (assign) ExtractMetaInfo *metaInfo; +@property (assign) I2PDeployer *deployer; @property (copy) NSImage *contentImage NS_AVAILABLE(10_9, NA); - (void) extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion; diff --git a/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib b/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib index bc21f8b438470a00e994826f7bded656bfc630c6..22333743948e4ab59a92db1dda9ac771da6703dd 100644 --- a/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib +++ b/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib @@ -37,6 +37,5 @@ </items> <point key="canvasLocation" x="17" y="167"/> </menu> - <customObject id="xTS-ll-kWI" customClass="SUUpdater"/> </objects> </document> diff --git a/launchers/macosx/I2PLauncher/Storyboard.storyboard b/launchers/macosx/I2PLauncher/Storyboard.storyboard index 3363ad9d2983e6314b14a032d82b52bc31124c9f..cd53b65b4ac506b618c7ed5a7546945f2d14fd2a 100644 --- a/launchers/macosx/I2PLauncher/Storyboard.storyboard +++ b/launchers/macosx/I2PLauncher/Storyboard.storyboard @@ -90,16 +90,8 @@ <font key="font" metaFont="system"/> </buttonCell> </button> - <button identifier="restartRouterButton" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C1m-1t-Xiq" userLabel="restart-button"> - <rect key="frame" x="10" y="150" width="128" height="32"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> - <buttonCell key="cell" type="push" title="Restart Router" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="sih-uF-V06"> - <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> - <font key="font" metaFont="system"/> - </buttonCell> - </button> <button identifier="openConsoleButton" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XZi-oz-5gy" userLabel="open-console-button"> - <rect key="frame" x="11" y="123" width="126" height="32"/> + <rect key="frame" x="10" y="150" width="126" height="32"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <buttonCell key="cell" type="push" title="Open Console" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Yh8-lj-JFi"> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> @@ -133,7 +125,7 @@ <autoresizingMask key="autoresizingMask"/> <clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="E5l-WA-qOn"> <rect key="frame" x="1" y="1" width="407" height="226"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <autoresizingMask key="autoresizingMask"/> <subviews> <textView identifier="LoggerTextView" ambiguous="YES" importsGraphics="NO" verticallyResizable="YES" usesFontPanel="YES" findStyle="panel" continuousSpellChecking="YES" allowsUndo="YES" usesRuler="YES" allowsNonContiguousLayout="YES" quoteSubstitution="YES" dashSubstitution="YES" spellingCorrection="YES" smartInsertDelete="YES" id="bgQ-8i-Xgb" userLabel="LoggerTextView"> <rect key="frame" x="0.0" y="0.0" width="407" height="226"/> 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/Utils/LaunchAgent+Status.swift b/launchers/macosx/I2PLauncher/Utils/LaunchAgent+Status.swift index fa7286d68a5e78c1a20ed50b3d4a372d90a4772d..8e1a19245d2bc4c16a6439e0270bec8733a88798 100644 --- a/launchers/macosx/I2PLauncher/Utils/LaunchAgent+Status.swift +++ b/launchers/macosx/I2PLauncher/Utils/LaunchAgent+Status.swift @@ -34,15 +34,15 @@ extension LaunchAgent { /// Run `launchctl start` on the agent /// /// Check the status of the job with `.status()` - public func start() { - LaunchAgentManager.shared.start(self) + public func start(_ callback: ((Process) -> Void)? = nil ) { + LaunchAgentManager.shared.start(self, callback) } /// Run `launchctl stop` on the agent /// /// Check the status of the job with `.status()` - public func stop() { - LaunchAgentManager.shared.stop(self) + public func stop(_ callback: ((Process) -> Void)? = nil ) { + LaunchAgentManager.shared.stop(self, callback) } /// Run `launchctl load` on the agent diff --git a/launchers/macosx/I2PLauncher/Utils/LaunchAgentManager.swift b/launchers/macosx/I2PLauncher/Utils/LaunchAgentManager.swift index 2eb50fe3691444c6330e0ff1b0f9d2a24f081ed4..b9c26c78f891f99d580527cfdd491a372d353baa 100644 --- a/launchers/macosx/I2PLauncher/Utils/LaunchAgentManager.swift +++ b/launchers/macosx/I2PLauncher/Utils/LaunchAgentManager.swift @@ -88,41 +88,53 @@ extension LaunchAgentManager { /// Run `launchctl start` on the agent /// /// Check the status of the job with `.status(_: LaunchAgent)` - public func start(_ agent: LaunchAgent) { + public func start(_ agent: LaunchAgent, _ termHandler: ((Process) -> Void)? = nil ) { let arguments = ["start", agent.label] - Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments) + let proc = Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments) + if ((termHandler) != nil) { + proc.terminationHandler = termHandler + } } /// Run `launchctl stop` on the agent /// /// Check the status of the job with `.status(_: LaunchAgent)` - public func stop(_ agent: LaunchAgent) { + public func stop(_ agent: LaunchAgent, _ termHandler: ((Process) -> Void)? = nil ) { let arguments = ["stop", agent.label] - Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments) + let proc = Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments) + if ((termHandler) != nil) { + proc.terminationHandler = termHandler + } } /// Run `launchctl load` on the agent /// /// Check the status of the job with `.status(_: LaunchAgent)` - public func load(_ agent: LaunchAgent) throws { + public func load(_ agent: LaunchAgent, _ termHandler: ((Process) -> Void)? = nil ) throws { guard let agentURL = agent.url else { throw LaunchAgentManagerError.urlNotSet(label: agent.label) } let arguments = ["load", agentURL.path] - Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments) + let proc = Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments) + if ((termHandler) != nil) { + proc.terminationHandler = termHandler + } } /// Run `launchctl unload` on the agent /// /// Check the status of the job with `.status(_: LaunchAgent)` - public func unload(_ agent: LaunchAgent) throws { + public func unload(_ agent: LaunchAgent, _ termHandler: ((Process) -> Void)? = nil ) throws { guard let agentURL = agent.url else { throw LaunchAgentManagerError.urlNotSet(label: agent.label) } let arguments = ["unload", agentURL.path] - Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments) + let proc = Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments) + if ((termHandler) != nil) { + proc.terminationHandler = termHandler + } } /// Retreives the status of the LaunchAgent from `launchctl` 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)") + } } } diff --git a/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift b/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift index 25885881955248a65f95813e9bec9f8729e1ece5..9f4199c31ff3411d0707c3d4393f98301fc5c645 100644 --- a/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift +++ b/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift @@ -24,63 +24,73 @@ import Cocoa @IBOutlet var routerUptimeLabel: NSTextField? @IBOutlet var routerPIDLabel: NSTextField? + @IBOutlet var quickControlView: NSView? @IBOutlet var routerStartStopButton: NSButton? @IBOutlet var openConsoleButton: NSButton? + + @objc func actionBtnOpenConsole(_ sender: Any?) { + SwiftMainDelegate.openLink(url: "http://localhost:7657") + } + @objc func actionBtnStartRouter(_ sender: Any?) { - NSLog("START ROUTER") + NSLog("Router start clicked") /*if (RouterManager.shared().getRouterTask() == nil) { SBridge.sharedInstance().startupI2PRouter(RouterProcessStatus.i2pDirectoryPath) }*/ (sender as! NSButton).isTransparent = true let routerStatus = RouterRunner.launchAgent?.status() - switch routerStatus { - case .loaded?: - RouterManager.shared().routerRunner.StartAgent(information: RouterRunner.launchAgent) - case .unloaded?: - do { - try LaunchAgentManager.shared.load(RouterRunner.launchAgent!) - RouterManager.shared().routerRunner.StartAgent(information: RouterRunner.launchAgent) - } catch { - RouterManager.shared().eventManager.trigger(eventName: "router_exception", information: error) + DispatchQueue(label: "background_start").async { + switch routerStatus { + case .loaded?: + RouterManager.shared().routerRunner.StartAgent(RouterRunner.launchAgent) + case .unloaded?: + do { + try LaunchAgentManager.shared.load(RouterRunner.launchAgent!) + RouterManager.shared().routerRunner.StartAgent(RouterRunner.launchAgent) + } catch { + RouterManager.shared().eventManager.trigger(eventName: "router_exception", information: error) + } + break + default: + break + } + DispatchQueue.main.async { + self.reEnableButton() } - break - default: - break } - self.reEnableButton() } @objc func actionBtnStopRouter(_ sender: Any?) { - NSLog("STOP ROUTER") - let routerStatus = RouterRunner.launchAgent?.status() - switch routerStatus { - case .running?: - NSLog("Found running router") - RouterManager.shared().routerRunner.StopAgent() - break - default: - break + NSLog("Router stop clicked") + DispatchQueue(label: "background_shutdown").async { + RouterManager.shared().routerRunner.StopAgent({ + RouterProcessStatus.isRouterRunning = false + RouterProcessStatus.isRouterChildProcess = false + NSLog("Router should be stopped by now.") + }) + // Wait for it to die. + } + RouterManager.shared().eventManager.trigger(eventName: "toggle_popover") self.reEnableButton() } - @objc func actionBtnRestartRouter(sender: Any?) { - if (RouterManager.shared().getRouterTask() != nil) { - //RouterManager.shared().getRouterTask()?.requestRestart() - } else { - NSLog("Can't restart a non running router, start it however...") - //SBridge.sharedInstance().startupI2PRouter(RouterProcessStatus.i2pDirectoryPath) - } + func restartFn() { + RouterManager.shared().routerRunner.StopAgent({ + sleep(30) + RouterManager.shared().routerRunner.StartAgent() + }) } func handlerRouterStart(information:Any?) { - print("Triggered handlerRouterStart") + NSLog("Triggered handlerRouterStart") NSLog("PID2! %@", information as! String) routerPIDLabel?.cell?.stringValue = "Router PID: "+(information as! String) routerPIDLabel?.needsDisplay = true routerStatusLabel?.cell?.stringValue = "Router status: Running" + RouterManager.shared().lastRouterPid = (information as? String) self.toggleSetButtonStop() self.reEnableButton() } @@ -112,10 +122,14 @@ import Cocoa RouterStatusView.instance = self } self.reEnableButton() + openConsoleButton!.cell!.action = #selector(self.actionBtnOpenConsole(_:)) + openConsoleButton!.cell!.target = self + } func handleRouterStop() { routerPIDLabel?.cell?.stringValue = "Router PID: Not running" + RouterManager.shared().lastRouterPid = nil self.toggleSetButtonStart() reEnableButton() } @@ -133,41 +147,49 @@ import Cocoa } func setRouterStatusLabelText() { - routerStartStopButton?.needsDisplay = true routerStartStopButton?.target = self - quickControlView?.needsDisplay = true + let staticStartedByLabelText = "Router started by launcher? " + let staticIsRunningLabelText = "Router status: " + let staticRouterVersionLabelText = "Router version: " + let staticRouterPidLabelText = "Router PID: " - do { - let currentStatus : AgentStatus = RouterRunner.launchAgent!.status() - if currentStatus == AgentStatus.loaded || currentStatus == AgentStatus.unloaded { - routerStatusLabel?.cell?.stringValue = "Router status: Not running" - } else { - routerStatusLabel?.cell?.stringValue = "Router status: Running" - } - } catch { - // Ensure it's set even AgentStatus is nil (uninitialized yet..) - routerStatusLabel?.cell?.stringValue = "Router status: Not running" + // Use default here to avoid any potential crashes with force unwrapping + let currentStatus : AgentStatus = RouterRunner.launchAgent?.status() ?? AgentStatus.unloaded + if currentStatus == AgentStatus.loaded || currentStatus == AgentStatus.unloaded { + routerStatusLabel?.cell?.stringValue = staticIsRunningLabelText+"Not running" + } else { + routerStatusLabel?.cell?.stringValue = staticIsRunningLabelText+"Running" } - let staticStartedByLabelText = "Router started by launcher?" if RouterProcessStatus.isRouterChildProcess { - routerStartedByLabel?.cell?.stringValue = staticStartedByLabelText+" Yes" + routerStartedByLabel?.cell?.stringValue = staticStartedByLabelText+"Yes" } else { - routerStartedByLabel?.cell?.stringValue = staticStartedByLabelText+" No" + routerStartedByLabel?.cell?.stringValue = staticStartedByLabelText+"No" } - routerStartedByLabel?.needsDisplay = true + + // Try to display PID - if not, the string behind ?? is used as "default" + let tmpPidText = RouterManager.shared().lastRouterPid ?? "Not running" + routerPIDLabel?.cell?.stringValue = staticRouterPidLabelText+tmpPidText if let version = RouterProcessStatus.routerVersion { - routerVersionLabel?.cell?.stringValue = "Router version: " + version + routerVersionLabel?.cell?.stringValue = staticRouterVersionLabelText + version } else { - routerVersionLabel?.cell?.stringValue = "Router version: Still unknown" + routerVersionLabel?.cell?.stringValue = staticRouterVersionLabelText + "Still unknown" } + if let routerStartTime = RouterProcessStatus.routerStartedAt { routerUptimeLabel?.cell?.stringValue = "Uptime: Router started " + DateTimeUtils.timeAgoSinceDate(date: NSDate(date: routerStartTime), numericDates: false) } else { routerUptimeLabel?.cell?.stringValue = "Uptime: Router isn't running" } + + // Needs display function alerts the rendrerer that the UI parts need to be re-drawed. + routerStartStopButton?.needsDisplay = true + quickControlView?.needsDisplay = true routerUptimeLabel?.needsDisplay = true + routerVersionLabel?.needsDisplay = true + routerStartedByLabel?.needsDisplay = true + routerPIDLabel?.needsDisplay = true } diff --git a/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift b/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift index ad70d9edb68e6c23b83c91fa85cd1d320c0a511f..07dc27f38bf3a61db38c7ca2758e701ace22df5d 100644 --- a/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift +++ b/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift @@ -44,19 +44,22 @@ import Cocoa let pidStr = information as! String NSLog("PID! %@", pidStr) showPopover(sender: nil) - //self.ctrl?.getRouterStatusView()?.handlerRouterStart(information: pidStr) + RouterManager.shared().lastRouterPid = pidStr self.ctrl?.getRouterStatusView()?.needsDisplay = true } + + func event_toggle(information:Any?) { + self.togglePopover(sender: self) + } override init() { super.init() self.ctrl = PopoverViewController.freshController() popover.contentViewController = self.ctrl - //updateObjectRef = SUUpdater.shared() - //updateObjectRef?.checkForUpdatesInBackground() - RouterManager.shared().eventManager.listenTo(eventName: "router_pid", action: pidReaction) + RouterManager.shared().eventManager.listenTo(eventName: "router_pid", action: pidReaction) + RouterManager.shared().eventManager.listenTo(eventName: "toggle_popover", action: event_toggle) if let button = statusItem.button { button.image = NSImage(named:"StatusBarButtonImage") diff --git a/launchers/macosx/include/strutil.hpp b/launchers/macosx/include/strutil.hpp index 085464fad48c759c32369257a4573a30cce98672..ca52442a2f2a2c1bd990e7d1ac38e4f1deb8fe48 100644 --- a/launchers/macosx/include/strutil.hpp +++ b/launchers/macosx/include/strutil.hpp @@ -100,11 +100,11 @@ static inline std::string trim_copy(std::string s) { return s; } -NSString* stdStringToNSString(std::string &stdstr) { +inline NSString* stdStringToNSString(std::string &stdstr) { return [NSString stringWithUTF8String:stdstr.c_str()]; } -std::string nsStringToStd(NSString* nsStr) { +inline std::string nsStringToStd(NSString* nsStr) { const char *charlist = [nsStr UTF8String]; return std::string(charlist); } diff --git a/launchers/macosx/main.mm b/launchers/macosx/main.mm index 06aa715c3ffc083c3beaa697bd2299898abaf9e7..60cbc5ebc86615197b11e40700384d3d7bc37b84 100644 --- a/launchers/macosx/main.mm +++ b/launchers/macosx/main.mm @@ -65,8 +65,8 @@ using namespace subprocess; - (void)extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion { - auto deployer = [[I2PDeployer alloc] initWithMetaInfo:self.metaInfo]; - [deployer extractI2PBaseDir:completion]; + self.deployer = [[I2PDeployer alloc] initWithMetaInfo:self.metaInfo]; + [self.deployer extractI2PBaseDir:completion]; } - (void)setApplicationDefaultPreferences { @@ -77,6 +77,7 @@ using namespace subprocess; @"startRouterAtLogin": @NO, @"startRouterAtStartup": @NO, @"letRouterLiveEvenLauncherDied": @NO, + @"consolePortCheckNum": @7657, @"i2pBaseDirectory": (NSString *)CFStringCreateWithCString(NULL, const_cast<const char *>(getDefaultBaseDir().c_str()), kCFStringEncodingUTF8) }]; @@ -119,10 +120,10 @@ using namespace subprocess; // Initialize the Swift environment (the UI components) [self.swiftRuntime applicationDidFinishLaunching]; - // TODO: Make the port a setting which defaults to 7657 - if (port_check(7657) != 0) + NSInteger portNum = [self.userPreferences integerForKey:@"consolePortCheckNum"]; + if (port_check((int)portNum) != 0) { - NSLog(@"Seems i2p is already running - I will not start the router (port 7657 is in use..)"); + NSLog(@"Seems i2p is already running - I will not start the router (port %d is in use..)", (int)portNum); sendUserNotification(@"Found already running router", @"TCP port 7657 seem to be used by another i2p instance."); [routerStatus setRouterStatus: true]; @@ -131,6 +132,13 @@ using namespace subprocess; } else { shouldAutoStartRouter = true; } + if (![self.userPreferences boolForKey:@"startRouterAtLogin"] && ![self.userPreferences boolForKey:@"startRouterAtStartup"]) + { + // In this case we don't want to find a running service + std::string launchdFile(RealHomeDirectory()); + launchdFile += "/Library/LaunchAgents/net.i2p.macosx.I2PRouter.plist"; + + } NSBundle *launcherBundle = [NSBundle mainBundle]; @@ -148,13 +156,6 @@ using namespace subprocess; std::string jarfile("-cp "); jarfile += [self.metaInfo.zipFile UTF8String]; - // Might be hard to read if you're not used to Objective-C - // But this is a "function call" that contains a "callback function" - /*[routerStatus listenForEventWithEventName:@"router_can_start" callbackActionFn:^(NSString* information) { - NSLog(@"Got signal, router can be started"); - [[SBridge sharedInstance] startupI2PRouter:self.metaInfo.i2pBase]; - }];*/ - // This will trigger the router start after an upgrade. [routerStatus listenForEventWithEventName:@"router_must_upgrade" callbackActionFn:^(NSString* information) { NSLog(@"Got signal, router must be deployed from base.zip"); diff --git a/launchers/macosx/osx_create_dmg.sh b/launchers/macosx/osx_create_dmg.sh index d2ebf4b96d07036bf2c5aacbb7a477460df20b76..a44946c7815e850bf355f0addb2b1c46e68efa2f 100755 --- a/launchers/macosx/osx_create_dmg.sh +++ b/launchers/macosx/osx_create_dmg.sh @@ -1,14 +1,14 @@ #!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -. .sign-secrets +source $DIR/.sign-secrets APP_NAME="I2PLauncher" -VERSION="0.9.36" -DMG_BACKGROUND_IMG="${DIR}/Background.png" +VERSION="0.9.37" +DMG_BACKGROUND_IMG=${BACKGROUND_IMG:-"Background.png"} APP_EXE="${APP_NAME}.app/Contents/MacOS/${APP_NAME}" -VOL_NAME="${APP_NAME} ${VERSION}" +VOL_NAME="${APP_NAME}-${VERSION}" DMG_TMP="${VOL_NAME}-temp.dmg" DMG_FINAL="${VOL_NAME}.dmg" STAGING_DIR="/tmp/mkdmg$$" @@ -23,13 +23,11 @@ if [ $(echo " $_BACKGROUND_IMAGE_DPI_H != 72.0 " | bc) -eq 1 -o $(echo " $_BACKG _DMG_BACKGROUND_TMP="${DMG_BACKGROUND_IMG%.*}"_dpifix."${DMG_BACKGROUND_IMG##*.}" - sips -s dpiWidth 72 -s dpiHeight 72 ${DMG_BACKGROUND_IMG} --out ${_DMG_BACKGROUND_TMP} + sips -s dpiWidth 72 -s dpiHeight 72 $DIR/${DMG_BACKGROUND_IMG} --out $DIR/${_DMG_BACKGROUND_TMP} - DMG_BACKGROUND_IMG="${_DMG_BACKGROUND_TMP}" -fi -# clear out any old data -rm -rf "${STAGING_DIR}" "${DMG_TMP}" "${DMG_FINAL}" + DMG_BACKGROUND_IMG="${_DMG_BACKGROUND_TMP}" +fi # copy over the stuff we want in the final disk image to our staging dir mkdir -p "${STAGING_DIR}" @@ -39,7 +37,7 @@ cp -rpf "${APP_NAME}.app" "${STAGING_DIR}" # figure out how big our DMG needs to be # assumes our contents are at least 1M! SIZE=`du -sh "${STAGING_DIR}" | sed 's/\([0-9\.]*\)M\(.*\)/\1/'` -SIZE=`echo "${SIZE} + 1.0" | bc | awk '{print int($1+0.5)}'` +SIZE=`echo "${SIZE} + 23.0" | bc | awk '{print int($1+0.5)}'` if [ $? -ne 0 ]; then echo "Error: Cannot compute size of staging dir" @@ -48,24 +46,28 @@ fi # create the temp DMG file hdiutil create -srcfolder "${STAGING_DIR}" -volname "${VOL_NAME}" -fs HFS+ \ - -fsargs "-c c=64,a=16,e=16" -format UDRW -size ${SIZE}M "${DMG_TMP}" + -fsargs "-c c=64,a=16,e=16" -format UDRW -size ${SIZE}M "$RELEASE_DIR/${DMG_TMP}" echo "Created DMG: ${DMG_TMP}" # mount it and save the device -DEVICE=$(hdiutil attach -readwrite -noverify "${DMG_TMP}" | \ +DEVICE=$(hdiutil attach -readwrite -noverify "$RELEASE_DIR/${DMG_TMP}" | \ egrep '^/dev/' | sed 1q | awk '{print $1}') sleep 2 + + # add a link to the Applications dir echo "Add link to /Applications" -pushd /Volumes/"${VOL_NAME}" -ln -s /Applications +cd /Volumes/"${VOL_NAME}" +ln -sf /Applications Applications # add a background image -mkdir /Volumes/"${VOL_NAME}"/.background -cp "${DMG_BACKGROUND_IMG}" /Volumes/"${VOL_NAME}"/.background/ +mkdir -p /Volumes/"${VOL_NAME}"/.background +cp "$DIR/`basename ${DMG_BACKGROUND_IMG}`" /Volumes/"${VOL_NAME}"/.background/`basename ${DMG_BACKGROUND_IMG}` + +cd $RELEASE_DIR # tell the Finder to resize the window, set the background, # change the icon size, place the icons in the right position, etc. @@ -98,14 +100,15 @@ hdiutil detach "${DEVICE}" # now make the final image a compressed disk image echo "Creating compressed image" -hdiutil convert "${DMG_TMP}" -format UDZO -imagekey zlib-level=9 -o "${DMG_FINAL}" +hdiutil convert "$RELEASE_DIR/${DMG_TMP}" -format UDZO -imagekey zlib-level=9 -o "$RELEASE_DIR/${DMG_FINAL}" -codesign --force --sign "${APPLE_CODE_SIGNER_ID}" "${DMG_FINAL}" +codesign --force --deep --sign "${APPLE_CODE_SIGNER_ID}" "$RELEASE_DIR/${DMG_FINAL}" # clean up -rm -rf "${DMG_TMP}" +rm -rf "$RELEASE_DIR/${DMG_TMP}" rm -rf "${STAGING_DIR}" + echo 'Done.' exit