diff --git a/mac/Focalboard.xcodeproj/project.pbxproj b/mac/Focalboard.xcodeproj/project.pbxproj index a991d9d9c..9b930d12a 100644 --- a/mac/Focalboard.xcodeproj/project.pbxproj +++ b/mac/Focalboard.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 8014951C261598D600A51700 /* PortUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8014951B261598D600A51700 /* PortUtils.swift */; }; 80D6DEBB252E13CB00AEED9E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80D6DEBA252E13CB00AEED9E /* AppDelegate.swift */; }; 80D6DEBD252E13CB00AEED9E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80D6DEBC252E13CB00AEED9E /* ViewController.swift */; }; 80D6DEBF252E13CD00AEED9E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 80D6DEBE252E13CD00AEED9E /* Assets.xcassets */; }; @@ -35,6 +36,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 8014951B261598D600A51700 /* PortUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortUtils.swift; sourceTree = ""; }; 80D6DEB7252E13CB00AEED9E /* Focalboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Focalboard.app; sourceTree = BUILT_PRODUCTS_DIR; }; 80D6DEBA252E13CB00AEED9E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 80D6DEBC252E13CB00AEED9E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -103,6 +105,7 @@ isa = PBXGroup; children = ( 80D6DEBA252E13CB00AEED9E /* AppDelegate.swift */, + 8014951B261598D600A51700 /* PortUtils.swift */, 80D6DEBC252E13CB00AEED9E /* ViewController.swift */, 80D6DF17252F9BDE00AEED9E /* AutoSaveWindowController.swift */, 80D6DEBE252E13CD00AEED9E /* Assets.xcassets */, @@ -283,6 +286,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8014951C261598D600A51700 /* PortUtils.swift in Sources */, 80D6DF18252F9BDE00AEED9E /* AutoSaveWindowController.swift in Sources */, 80D6DEBD252E13CB00AEED9E /* ViewController.swift in Sources */, 80D6DEBB252E13CB00AEED9E /* AppDelegate.swift in Sources */, diff --git a/mac/Focalboard/AppDelegate.swift b/mac/Focalboard/AppDelegate.swift index 6b9b0eff2..ea2f25adb 100644 --- a/mac/Focalboard/AppDelegate.swift +++ b/mac/Focalboard/AppDelegate.swift @@ -88,7 +88,16 @@ class AppDelegate: NSObject, NSApplicationDelegate { return "su-" + randomNumber } + private func getFreePort() { + if PortUtils.isPortFree(in_port_t(serverPort)) { + return + } + + serverPort = Int(PortUtils.getFreePort()) + } + private func startServer() { + getFreePort() sessionToken = generateSessionToken() let cwdUrl = webFolder() diff --git a/mac/Focalboard/Base.lproj/Main.storyboard b/mac/Focalboard/Base.lproj/Main.storyboard index 1ac221a83..be5f285ce 100644 --- a/mac/Focalboard/Base.lproj/Main.storyboard +++ b/mac/Focalboard/Base.lproj/Main.storyboard @@ -578,6 +578,13 @@ + + + + + + + diff --git a/mac/Focalboard/PortUtils.swift b/mac/Focalboard/PortUtils.swift new file mode 100644 index 000000000..697f22ac4 --- /dev/null +++ b/mac/Focalboard/PortUtils.swift @@ -0,0 +1,52 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import Foundation + +class PortUtils { + static func isPortFree(_ port: in_port_t) -> Bool { + let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0) + if socketFileDescriptor == -1 { + return false + } + + var addr = sockaddr_in() + let sizeOfSockkAddr = MemoryLayout.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("127.0.0.1")) + 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 { + release(socket: socketFileDescriptor) + return false + } + if listen(socketFileDescriptor, SOMAXCONN ) == -1 { + release(socket: socketFileDescriptor) + return false + } + release(socket: socketFileDescriptor) + return true + } + + private static func release(socket: Int32) { + Darwin.shutdown(socket, SHUT_RDWR) + close(socket) + } + + static func getFreePort() -> in_port_t { + var portNum: in_port_t = 0 + for i in 50000..<65000 { + let isFree = isPortFree(in_port_t(i)) + if isFree { + portNum = in_port_t(i) + return portNum + } + } + + return in_port_t(0) + } +} diff --git a/mac/Focalboard/ViewController.swift b/mac/Focalboard/ViewController.swift index 96010bf35..02e3d9957 100644 --- a/mac/Focalboard/ViewController.swift +++ b/mac/Focalboard/ViewController.swift @@ -45,6 +45,17 @@ class ViewController: WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes as! Set, modifiedSince: date, completionHandler:{ }) } + @IBAction func showDiagnosticsInfo(_ sender: NSObject) { + let appDelegate = NSApplication.shared.delegate as! AppDelegate + + let alert: NSAlert = NSAlert() + alert.messageText = "Diagnostics info" + alert.informativeText = "Port: \(appDelegate.serverPort)" + alert.alertStyle = .informational + alert.addButton(withTitle: "OK") + alert.runModal() + } + @objc func onServerStarted() { NSLog("onServerStarted") DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {