forked from I2P_Developers/i2p.i2p
Mac OSX Launcher: Router management/healthcheck code updates.
This commit is contained in:
50
launchers/macosx/I2PLauncher/routermgmt/NetworkUtil.swift
Normal file
50
launchers/macosx/I2PLauncher/routermgmt/NetworkUtil.swift
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// NetworkUtil.swift
|
||||
// I2PLauncher
|
||||
//
|
||||
// Created by Mikal Villa on 07/04/2019.
|
||||
// Copyright © 2019 The I2P Project. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class NetworkUtil {
|
||||
static func checkTcpPortForListen(host: String = "127.0.0.1", port: in_port_t = 7657) -> (Bool, descr: String){
|
||||
|
||||
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
|
||||
if socketFileDescriptor == -1 {
|
||||
return (false, "SocketCreationFailed, \(descriptionOfLastError())")
|
||||
}
|
||||
|
||||
var addr = sockaddr_in()
|
||||
let sizeOfSockkAddr = MemoryLayout<sockaddr_in>.size
|
||||
addr.sin_len = __uint8_t(sizeOfSockkAddr)
|
||||
addr.sin_family = sa_family_t(AF_INET)
|
||||
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
|
||||
addr.sin_addr = in_addr(s_addr: inet_addr(host))
|
||||
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
|
||||
var bind_addr = sockaddr()
|
||||
memcpy(&bind_addr, &addr, Int(sizeOfSockkAddr))
|
||||
|
||||
if Darwin.bind(socketFileDescriptor, &bind_addr, socklen_t(sizeOfSockkAddr)) == -1 {
|
||||
let details = descriptionOfLastError()
|
||||
release(socket: socketFileDescriptor)
|
||||
return (false, "\(port), BindFailed, \(details)")
|
||||
}
|
||||
if listen(socketFileDescriptor, SOMAXCONN ) == -1 {
|
||||
let details = descriptionOfLastError()
|
||||
release(socket: socketFileDescriptor)
|
||||
return (false, "\(port), ListenFailed, \(details)")
|
||||
}
|
||||
release(socket: socketFileDescriptor)
|
||||
return (true, "\(port) is free for use")
|
||||
}
|
||||
|
||||
static func release(socket: Int32) {
|
||||
Darwin.shutdown(socket, SHUT_RDWR)
|
||||
close(socket)
|
||||
}
|
||||
static func descriptionOfLastError() -> String {
|
||||
return String(cString: UnsafePointer(strerror(errno)))
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
//
|
||||
// HttpTunnelService.swift
|
||||
// I2PLauncher
|
||||
//
|
||||
// Created by Mikal Villa on 03/04/2019.
|
||||
// Copyright © 2019 The I2P Project. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Kanna
|
||||
|
||||
class HttpTunnelService : Service {
|
||||
|
||||
let dataURL: URL = URL(string: "http://127.0.0.1:7657/i2ptunnel/")!
|
||||
|
||||
override func updateStatus(callback: @escaping (BaseService) -> Void) {
|
||||
URLSession.shared.dataTask(with: dataURL) { [weak self] data, _, error in
|
||||
guard let strongSelf = self else { return }
|
||||
defer { callback(strongSelf) }
|
||||
|
||||
guard let doc = try? HTML(html: data!, encoding: .utf8) else { return /*strongSelf._fail("Couldn't parse response")*/ }
|
||||
|
||||
_ = doc.css("table#clientTunnels > tr.tunnelProperties > td.tunnelStatus").first
|
||||
let maxStatus: ServiceStatus = .started
|
||||
strongSelf.status = maxStatus
|
||||
|
||||
switch maxStatus {
|
||||
case .waiting:
|
||||
strongSelf.message = "Waiting on router"
|
||||
case .started:
|
||||
strongSelf.message = "Started"
|
||||
case .stopped:
|
||||
strongSelf.message = "Stopped"
|
||||
case .undetermined:
|
||||
strongSelf.message = "Undetermined"
|
||||
default:
|
||||
strongSelf.message = "Undetermined" /*downComponents.map { $0["name"] as? String }.compactMap { $0 }.joined(separator: ", ")*/
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
//
|
||||
// I2PRouterService.swift
|
||||
// I2PLauncher
|
||||
//
|
||||
// Created by Mikal Villa on 03/04/2019.
|
||||
// Copyright © 2019 The I2P Project. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class I2PRouterService : Service {
|
||||
|
||||
override func updateStatus(callback: @escaping (BaseService) -> Void) {
|
||||
//guard let strongSelf = self else { return }
|
||||
defer { callback(self) }
|
||||
DispatchQueue.main.async {
|
||||
self.status = ServiceStatus(rawValue: 0)!
|
||||
self.message = "Dead"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
//
|
||||
// IrcTunnelService.swift
|
||||
// I2PLauncher
|
||||
//
|
||||
// Created by Mikal Villa on 03/04/2019.
|
||||
// Copyright © 2019 The I2P Project. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class IrcTunnelService : Service {
|
||||
|
||||
override func updateStatus(callback: @escaping (BaseService) -> Void) {
|
||||
defer { callback(self) }
|
||||
|
||||
self.status = ServiceStatus(rawValue: 0)!
|
||||
self.message = "Dead"
|
||||
}
|
||||
|
||||
}
|
132
launchers/macosx/I2PLauncher/routermgmt/Service.swift
Normal file
132
launchers/macosx/I2PLauncher/routermgmt/Service.swift
Normal file
@ -0,0 +1,132 @@
|
||||
//
|
||||
// Service.swift
|
||||
// I2PLauncher
|
||||
//
|
||||
// Created by Mikal Villa on 12/04/2019.
|
||||
// Copyright © 2019 The I2P Project. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum ServiceStatus: Int, Comparable {
|
||||
case undetermined
|
||||
case waiting
|
||||
case started
|
||||
case notice
|
||||
case killed
|
||||
case crashed
|
||||
case stopped
|
||||
case restarting
|
||||
|
||||
public static func < (lhs: ServiceStatus, rhs: ServiceStatus) -> Bool {
|
||||
return lhs.rawValue < rhs.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
protocol ComparableStatus: Comparable {
|
||||
var serviceStatus: ServiceStatus { get }
|
||||
}
|
||||
|
||||
extension ComparableStatus {
|
||||
public static func < (lhs: Self, rhs: Self) -> Bool {
|
||||
return lhs.serviceStatus < rhs.serviceStatus
|
||||
}
|
||||
}
|
||||
|
||||
typealias Service = BaseService & RequiredServiceProperties
|
||||
|
||||
protocol RequiredServiceProperties {
|
||||
var name: String { get }
|
||||
//var url: URL { get }
|
||||
}
|
||||
|
||||
extension RequiredServiceProperties {
|
||||
// Default implementation of the property `name` is to return the class name
|
||||
var name: String { return "\(type(of: self))" }
|
||||
}
|
||||
|
||||
public class BaseService {
|
||||
public var status: ServiceStatus = .undetermined {
|
||||
didSet {
|
||||
if oldValue == .undetermined || status == .undetermined || oldValue == status {
|
||||
self.shouldNotify = false
|
||||
} else if Preferences.shared().notifyOnStatusChange {
|
||||
self.shouldNotify = true
|
||||
}
|
||||
}
|
||||
}
|
||||
var message: String = "Loading…"
|
||||
var shouldNotify = false
|
||||
|
||||
public static func all() -> [BaseService] {
|
||||
guard let servicesPlist = Bundle.main.path(forResource: "RouterServices", ofType: "plist"),
|
||||
let services = NSDictionary(contentsOfFile: servicesPlist)?["services"] as? [String] else {
|
||||
fatalError("The RouterServices.plist file does not exist. The build phase script might have failed.")
|
||||
}
|
||||
|
||||
return services.map(BaseService.named).compactMap { $0 }
|
||||
}
|
||||
|
||||
static func named(_ name: String) -> BaseService? {
|
||||
return (NSClassFromString("I2PLauncher.\(name)") as? Service.Type)?.init()
|
||||
}
|
||||
|
||||
public required init() {}
|
||||
|
||||
public func updateStatus(callback: @escaping (BaseService) -> Void) {}
|
||||
|
||||
func _fail(_ error: Error?) {
|
||||
self.status = .undetermined
|
||||
self.message = error.debugDescription// ?? "Unexpected error"
|
||||
}
|
||||
|
||||
func _fail(_ message: String) {
|
||||
self.status = .undetermined
|
||||
self.message = message
|
||||
}
|
||||
|
||||
func notifyIfNecessary() {
|
||||
guard let realSelf = self as? Service else { fatalError("BaseService should not be used directly.") }
|
||||
|
||||
guard shouldNotify else { return }
|
||||
|
||||
self.shouldNotify = false
|
||||
|
||||
let notification = NSUserNotification()
|
||||
let possessiveS = realSelf.name.hasSuffix("s") ? "'" : "'s"
|
||||
notification.title = "\(realSelf.name)\(possessiveS) status has changed"
|
||||
notification.informativeText = message
|
||||
|
||||
NSUserNotificationCenter.default.deliver(notification)
|
||||
}
|
||||
}
|
||||
|
||||
extension BaseService: Equatable {
|
||||
public static func == (lhs: BaseService, rhs: BaseService) -> Bool {
|
||||
guard
|
||||
let lhs = lhs as? Service,
|
||||
let rhs = rhs as? Service
|
||||
else {
|
||||
fatalError("BaseService should not be used directly.")
|
||||
}
|
||||
|
||||
return lhs.name == rhs.name
|
||||
}
|
||||
}
|
||||
|
||||
extension BaseService: Comparable {
|
||||
public static func < (lhs: BaseService, rhs: BaseService) -> Bool {
|
||||
guard
|
||||
let lhs = lhs as? Service,
|
||||
let rhs = rhs as? Service
|
||||
else {
|
||||
fatalError("BaseService should not be used directly.")
|
||||
}
|
||||
|
||||
let sameStatus = lhs.status == rhs.status
|
||||
let differentStatus =
|
||||
lhs.status != .started && lhs.status != .notice
|
||||
&& rhs.status == .started || rhs.status == .notice
|
||||
return ((lhs.name < rhs.name) && sameStatus) || differentStatus
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user