From f375f8a9d8f6db9161e3e61a1082f175e743d239 Mon Sep 17 00:00:00 2001 From: Florian Kostenzer Date: Thu, 14 May 2020 11:53:33 +0200 Subject: [PATCH 1/9] MacLessManager first version --- .github/workflows/lint.yml | 8 + .github/workflows/swift.yml | 31 ++++ .gitignore | 5 + MacLessManager/.swiftlint.yml | 8 + MacLessManager/Package.swift | 20 +++ .../MacLessManager/MacLessManager.swift | 153 ++++++++++++++++++ .../Sources/MacLessManager/main.swift | 64 ++++++++ 7 files changed, 289 insertions(+) create mode 100644 MacLessManager/.swiftlint.yml create mode 100644 MacLessManager/Package.swift create mode 100644 MacLessManager/Sources/MacLessManager/MacLessManager.swift create mode 100644 MacLessManager/Sources/MacLessManager/main.swift diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 209d2dd6..3407be10 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,3 +19,11 @@ jobs: uses: norio-nomura/action-swiftlint@3.1.0 with: args: --strict --path Manager + MacLessManagerSwiftLint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: GitHub Action for SwiftLint with --strict for MacLessManagerSwiftLint + uses: norio-nomura/action-swiftlint@3.1.0 + with: + args: --strict --path MacLessManager diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 34a050cc..e8936b31 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -45,3 +45,34 @@ jobs: - name: Build working-directory: Manager run: swift build -v -c release +MacLessManagerBuildDebug: + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + - name: Resolve + working-directory: MacLessManager + run: swift package resolve + - uses: actions/cache@v1 + with: + path: MacLessManager/.build + key: ${{ runner.os }}-debug-spm-${{ hashFiles('MacLessManager/Package.resolved') }} + - name: Build + working-directory: MacLessManager + run: swift build -v -c debug +# - name: Test +# working-directory: MacLessManager +# run: swift test -v -c debug + MacLessManagerBuildRelease: + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + - name: Resolve + working-directory: MacLessManager + run: swift package resolve + - uses: actions/cache@v1 + with: + path: MacLessManager/.build + key: ${{ runner.os }}-release-spm-${{ hashFiles('MacLessManager/Package.resolved') }} + - name: Build + working-directory: MacLessManager + run: swift build -v -c release diff --git a/.gitignore b/.gitignore index d2a92f5b..ac6d9348 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,11 @@ RealDeviceMap-UIControl.xcodeproj/project.xcworkspace/xcuserdata/* Manager/RDM-UIC-Manager.xcodeproj/ Manager/.build/ Manager/Package.resolved +Manager/.swiftpm/ +MacLessManager/MacLessManager.xcodeproj/ +MacLessManager/.build/ +MacLessManager/Package.resolved +MacLessManager/.swiftpm/ # Pods Podfile.lock diff --git a/MacLessManager/.swiftlint.yml b/MacLessManager/.swiftlint.yml new file mode 100644 index 00000000..b07b509c --- /dev/null +++ b/MacLessManager/.swiftlint.yml @@ -0,0 +1,8 @@ +included: + - Sources +disabled_rules: +- nesting +identifier_name: + excluded: + - i + - id diff --git a/MacLessManager/Package.swift b/MacLessManager/Package.swift new file mode 100644 index 00000000..966ff7fb --- /dev/null +++ b/MacLessManager/Package.swift @@ -0,0 +1,20 @@ +// swift-tools-version:5.0 + +import PackageDescription + +let package = Package( + name: "MacLessManager", + dependencies: [ + .package(url: "https://github.com/apple/swift-log", .upToNextMinor(from: "1.2.0")), + .package(url: "https://github.com/JohnSundell/ShellOut", .upToNextMinor(from: "2.3.0")) + ], + targets: [ + .target( + name: "MacLessManager", + dependencies: [ + .product(name: "Logging", package: "swift-log"), + .product(name: "ShellOut", package: "ShellOut") + ] + ) + ] +) diff --git a/MacLessManager/Sources/MacLessManager/MacLessManager.swift b/MacLessManager/Sources/MacLessManager/MacLessManager.swift new file mode 100644 index 00000000..e9ed440f --- /dev/null +++ b/MacLessManager/Sources/MacLessManager/MacLessManager.swift @@ -0,0 +1,153 @@ +import Foundation +import Logging +import ShellOut + +class MacLessManager { + + let logger: Logger + let id: String + let backendURL: URL + let username: String + let password: String + let restartAfter: Double + let restartLockout: Double + + let queue: DispatchQueue + + let runningLock = NSLock() + var running: Bool = false + var deviceRestarts = [String: Date]() + + init(backendURL: String, username: String, password: String, restartAfter: Int, restartLockout: Int) { + self.id = UUID().uuidString + self.logger = Logger(label: "MacLessManager-\(id)") + self.backendURL = URL(string: "\(backendURL)/api/get_data?show_devices=true")! + self.password = password + self.username = username + self.restartAfter = Double(restartAfter) + self.restartLockout = Double(restartLockout) + self.queue = DispatchQueue(label: "MacLessManager-\(id)") + } + + func start() { + runningLock.lock() + if !running { + running = true + runningLock.unlock() + logger.notice("Starting Manager") + queue.async { + self.run() + } + } else { + runningLock.unlock() + logger.info("Already Started") + } + } + + func stop() { + runningLock.lock() + if running { + logger.notice("Stopping Manager") + running = false + } else { + logger.info("Already Stopped") + } + runningLock.unlock() + } + + private func run() { + runningLock.lock() + while running { + runningLock.unlock() + guard let devices = try? getAllDevices(), !devices.isEmpty else { + self.logger.error("Failed to laod devices (or none connected)") + sleep(1) + continue + } + self.logger.info("\(devices.count) devices connected") + guard let statusse = try? getAllDeviceStatusse(), !statusse.isEmpty else { + self.logger.error("Failed to laod statusse") + sleep(1) + continue + } + self.logger.info("Loaded \(statusse.count) statusse") + for device in devices { + guard let status = statusse[device.value] else { + self.logger.error("No status for: \(device.value)") + continue + } + if Date().timeIntervalSince(status) >= restartAfter { + if let lastRestart = deviceRestarts[device.key], + Date().timeIntervalSince(lastRestart) <= restartLockout { + continue + } + do { + self.logger.notice( + "Restarting \(device.value). Last seen: \(Int(Date().timeIntervalSince(status)))s ago." + ) + try restart(uuid: device.key) + deviceRestarts[device.key] = Date() + } catch { + self.logger.error("No status for: \(device.value)") + } + } + } + sleep(30) + runningLock.lock() + } + runningLock.unlock() + } + + private func getAllDeviceStatusse() throws -> [String: Date] { + var request = URLRequest(url: backendURL) + let token = "\(username):\(password)".data(using: .utf8)!.base64EncodedString() + request.addValue("Basic \(token)", forHTTPHeaderField: "Authorization") + let configuration = URLSessionConfiguration.default + configuration.httpCookieStorage = .shared + configuration.httpShouldSetCookies = true + + var statusse = [String: Date]() + let semaphore = DispatchSemaphore(value: 0) + let task = URLSession(configuration: configuration).dataTask(with: request) { data, _, _ in + if let data = data, + let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let jsonData = json["data"] as? [String: Any], + let devices = jsonData["devices"] as? [[String: Any]] { + for device in devices { + if let uuid = device["uuid"] as? String, let seen = device["last_seen"] as? UInt32 { + statusse[uuid] = Date(timeIntervalSince1970: Double(seen)) + } + } + } + semaphore.signal() + } + task.resume() + semaphore.wait() + return statusse + } + + private func getAllDevices() throws -> [String: String] { + var devices = [String: String]() + let uuids = try getAllDeciceUUIDs() + for uuid in uuids { + let name = try shellOut(to: "idevicename", arguments: ["--udid", uuid]) + devices[uuid] = name + } + return devices + } + + private func getAllDeciceUUIDs() throws -> [String] { + let idString = try shellOut(to: "idevice_id", arguments: ["--list"]) + guard !idString.isEmpty else { + return [] + } + return idString.components(separatedBy: .newlines).map { (uuid) -> String in + return uuid.trimmingCharacters(in: .whitespaces) + } + } + + private func restart(uuid: String) throws { + try shellOut(to: "idevicediagnostics", arguments: ["restart", "--udid", uuid]) + } + +} diff --git a/MacLessManager/Sources/MacLessManager/main.swift b/MacLessManager/Sources/MacLessManager/main.swift new file mode 100644 index 00000000..6990be68 --- /dev/null +++ b/MacLessManager/Sources/MacLessManager/main.swift @@ -0,0 +1,64 @@ +import Foundation +import Logging + +if CommandLine.arguments.contains("--help") || + CommandLine.arguments.contains("-h") { + print(""" + The following flags are available: + `--backend url` (required) [The URL of the RDM frontend] + `--username username` (required) [The username of a RDM user with admin permission] + `--password password` (required) [The password of a RDM user with admin permission] + `--after time` (in seconds, default 120) [The time before an unseen device gets restarted] + `--lockout time` (in seconds, default 300) [The time to wait after restart untill another restart] + """) + exit(0) +} + +guard let backendURLIndex = CommandLine.arguments.firstIndex(of: "--backend"), + CommandLine.arguments.count > backendURLIndex + 1 else { + fatalError("--backend not set but is required") +} +let backendURL = CommandLine.arguments[backendURLIndex + 1] + +guard let usernameIndex = CommandLine.arguments.firstIndex(of: "--username"), + CommandLine.arguments.count > usernameIndex + 1 else { + fatalError("--backend not set but is required") +} +let username = CommandLine.arguments[usernameIndex + 1] + +guard let passwordIndex = CommandLine.arguments.firstIndex(of: "--password"), + CommandLine.arguments.count > passwordIndex + 1 else { + fatalError("--backend not set but is required") +} +let password = CommandLine.arguments[passwordIndex + 1] + +let restartAfter: Int +if let index = CommandLine.arguments.firstIndex(of: "--after"), + CommandLine.arguments.count > passwordIndex + 1, + let after = Int(CommandLine.arguments[index + 1]) { + restartAfter = after +} else { + restartAfter = 120 +} + +let restartLockout: Int +if let index = CommandLine.arguments.firstIndex(of: "--lockout"), + CommandLine.arguments.count > passwordIndex + 1, + let lockout = Int(CommandLine.arguments[index + 1]) { + restartLockout = lockout +} else { + restartLockout = 300 +} + +let manager = MacLessManager( + backendURL: backendURL, + username: username, + password: password, + restartAfter: restartAfter, + restartLockout: restartLockout +) +manager.start() + +while true { + sleep(UInt32.max) +} From f97a9f1ee4e078032fad8515d99d81a9b18898bf Mon Sep 17 00:00:00 2001 From: Florian Kostenzer Date: Thu, 14 May 2020 11:55:48 +0200 Subject: [PATCH 2/9] frontend not backend --- .../Sources/MacLessManager/MacLessManager.swift | 8 ++++---- MacLessManager/Sources/MacLessManager/main.swift | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/MacLessManager/Sources/MacLessManager/MacLessManager.swift b/MacLessManager/Sources/MacLessManager/MacLessManager.swift index e9ed440f..2fc31056 100644 --- a/MacLessManager/Sources/MacLessManager/MacLessManager.swift +++ b/MacLessManager/Sources/MacLessManager/MacLessManager.swift @@ -6,7 +6,7 @@ class MacLessManager { let logger: Logger let id: String - let backendURL: URL + let frontendURL: URL let username: String let password: String let restartAfter: Double @@ -18,10 +18,10 @@ class MacLessManager { var running: Bool = false var deviceRestarts = [String: Date]() - init(backendURL: String, username: String, password: String, restartAfter: Int, restartLockout: Int) { + init(frontendURL: String, username: String, password: String, restartAfter: Int, restartLockout: Int) { self.id = UUID().uuidString self.logger = Logger(label: "MacLessManager-\(id)") - self.backendURL = URL(string: "\(backendURL)/api/get_data?show_devices=true")! + self.frontendURL = URL(string: "\(frontendURL)/api/get_data?show_devices=true")! self.password = password self.username = username self.restartAfter = Double(restartAfter) @@ -99,7 +99,7 @@ class MacLessManager { } private func getAllDeviceStatusse() throws -> [String: Date] { - var request = URLRequest(url: backendURL) + var request = URLRequest(url: frontendURL) let token = "\(username):\(password)".data(using: .utf8)!.base64EncodedString() request.addValue("Basic \(token)", forHTTPHeaderField: "Authorization") let configuration = URLSessionConfiguration.default diff --git a/MacLessManager/Sources/MacLessManager/main.swift b/MacLessManager/Sources/MacLessManager/main.swift index 6990be68..eca977ab 100644 --- a/MacLessManager/Sources/MacLessManager/main.swift +++ b/MacLessManager/Sources/MacLessManager/main.swift @@ -5,7 +5,7 @@ if CommandLine.arguments.contains("--help") || CommandLine.arguments.contains("-h") { print(""" The following flags are available: - `--backend url` (required) [The URL of the RDM frontend] + `--frontend url` (required) [The URL of the RDM frontend] `--username username` (required) [The username of a RDM user with admin permission] `--password password` (required) [The password of a RDM user with admin permission] `--after time` (in seconds, default 120) [The time before an unseen device gets restarted] @@ -14,21 +14,21 @@ if CommandLine.arguments.contains("--help") || exit(0) } -guard let backendURLIndex = CommandLine.arguments.firstIndex(of: "--backend"), - CommandLine.arguments.count > backendURLIndex + 1 else { - fatalError("--backend not set but is required") +guard let frontendURLIndex = CommandLine.arguments.firstIndex(of: "--frontend"), + CommandLine.arguments.count > frontendURLIndex + 1 else { + fatalError("--frontend not set but is required") } -let backendURL = CommandLine.arguments[backendURLIndex + 1] +let frontendURL = CommandLine.arguments[frontendURLIndex + 1] guard let usernameIndex = CommandLine.arguments.firstIndex(of: "--username"), CommandLine.arguments.count > usernameIndex + 1 else { - fatalError("--backend not set but is required") + fatalError("--frontend not set but is required") } let username = CommandLine.arguments[usernameIndex + 1] guard let passwordIndex = CommandLine.arguments.firstIndex(of: "--password"), CommandLine.arguments.count > passwordIndex + 1 else { - fatalError("--backend not set but is required") + fatalError("--frontend not set but is required") } let password = CommandLine.arguments[passwordIndex + 1] @@ -51,7 +51,7 @@ if let index = CommandLine.arguments.firstIndex(of: "--lockout"), } let manager = MacLessManager( - backendURL: backendURL, + frontendURL: frontendURL, username: username, password: password, restartAfter: restartAfter, From d4d41fa47621212a9b2f2678089af198ccfb7907 Mon Sep 17 00:00:00 2001 From: Florian Kostenzer Date: Thu, 14 May 2020 12:00:49 +0200 Subject: [PATCH 3/9] fix wrong error message --- MacLessManager/Sources/MacLessManager/main.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MacLessManager/Sources/MacLessManager/main.swift b/MacLessManager/Sources/MacLessManager/main.swift index eca977ab..b3743f31 100644 --- a/MacLessManager/Sources/MacLessManager/main.swift +++ b/MacLessManager/Sources/MacLessManager/main.swift @@ -22,13 +22,13 @@ let frontendURL = CommandLine.arguments[frontendURLIndex + 1] guard let usernameIndex = CommandLine.arguments.firstIndex(of: "--username"), CommandLine.arguments.count > usernameIndex + 1 else { - fatalError("--frontend not set but is required") + fatalError("--username not set but is required") } let username = CommandLine.arguments[usernameIndex + 1] guard let passwordIndex = CommandLine.arguments.firstIndex(of: "--password"), CommandLine.arguments.count > passwordIndex + 1 else { - fatalError("--frontend not set but is required") + fatalError("--password not set but is required") } let password = CommandLine.arguments[passwordIndex + 1] From 535ac76c40ae7616b96100fb90ecb5b9cab4d24c Mon Sep 17 00:00:00 2001 From: Florian Kostenzer Date: Thu, 14 May 2020 12:02:56 +0200 Subject: [PATCH 4/9] fix workflow --- .github/workflows/swift.yml | 56 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index e8936b31..dd0dacd2 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -45,34 +45,34 @@ jobs: - name: Build working-directory: Manager run: swift build -v -c release -MacLessManagerBuildDebug: - runs-on: macos-latest - steps: - - uses: actions/checkout@v1 - - name: Resolve - working-directory: MacLessManager - run: swift package resolve - - uses: actions/cache@v1 - with: - path: MacLessManager/.build - key: ${{ runner.os }}-debug-spm-${{ hashFiles('MacLessManager/Package.resolved') }} - - name: Build - working-directory: MacLessManager - run: swift build -v -c debug + MacLessManagerBuildDebug: + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + - name: Resolve + working-directory: MacLessManager + run: swift package resolve + - uses: actions/cache@v1 + with: + path: MacLessManager/.build + key: ${{ runner.os }}-debug-spm-${{ hashFiles('MacLessManager/Package.resolved') }} + - name: Build + working-directory: MacLessManager + run: swift build -v -c debug # - name: Test # working-directory: MacLessManager # run: swift test -v -c debug - MacLessManagerBuildRelease: - runs-on: macos-latest - steps: - - uses: actions/checkout@v1 - - name: Resolve - working-directory: MacLessManager - run: swift package resolve - - uses: actions/cache@v1 - with: - path: MacLessManager/.build - key: ${{ runner.os }}-release-spm-${{ hashFiles('MacLessManager/Package.resolved') }} - - name: Build - working-directory: MacLessManager - run: swift build -v -c release + MacLessManagerBuildRelease: + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + - name: Resolve + working-directory: MacLessManager + run: swift package resolve + - uses: actions/cache@v1 + with: + path: MacLessManager/.build + key: ${{ runner.os }}-release-spm-${{ hashFiles('MacLessManager/Package.resolved') }} + - name: Build + working-directory: MacLessManager + run: swift build -v -c release From 110a78be79ccc0af9a573f84a6e0c53d62d19b6b Mon Sep 17 00:00:00 2001 From: Florian Kostenzer Date: Thu, 14 May 2020 18:37:41 +0200 Subject: [PATCH 5/9] Create .swift-version --- MacLessManager/.swift-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 MacLessManager/.swift-version diff --git a/MacLessManager/.swift-version b/MacLessManager/.swift-version new file mode 100644 index 00000000..819e07a2 --- /dev/null +++ b/MacLessManager/.swift-version @@ -0,0 +1 @@ +5.0 From 921762460bea8827408a2217531d93e245a44697 Mon Sep 17 00:00:00 2001 From: Florian Kostenzer Date: Tue, 4 Aug 2020 16:32:06 +0200 Subject: [PATCH 6/9] Update Package.swift --- MacLessManager/Package.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MacLessManager/Package.swift b/MacLessManager/Package.swift index 966ff7fb..b658c9fa 100644 --- a/MacLessManager/Package.swift +++ b/MacLessManager/Package.swift @@ -5,8 +5,8 @@ import PackageDescription let package = Package( name: "MacLessManager", dependencies: [ - .package(url: "https://github.com/apple/swift-log", .upToNextMinor(from: "1.2.0")), - .package(url: "https://github.com/JohnSundell/ShellOut", .upToNextMinor(from: "2.3.0")) + .package(url: "https://github.com/apple/swift-log", from: "1.2.0"), + .package(url: "https://github.com/JohnSundell/ShellOut", from: "2.3.0") ], targets: [ .target( From 59276b8fc7b535211d984aa537ddfaf24fa6947f Mon Sep 17 00:00:00 2001 From: Florian Kostenzer Date: Fri, 29 Jan 2021 14:17:36 +0100 Subject: [PATCH 7/9] support Swift 5.3 --- MacLessManager/Sources/MacLessManager/main.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MacLessManager/Sources/MacLessManager/main.swift b/MacLessManager/Sources/MacLessManager/main.swift index b3743f31..a67fc141 100644 --- a/MacLessManager/Sources/MacLessManager/main.swift +++ b/MacLessManager/Sources/MacLessManager/main.swift @@ -18,19 +18,19 @@ guard let frontendURLIndex = CommandLine.arguments.firstIndex(of: "--frontend"), CommandLine.arguments.count > frontendURLIndex + 1 else { fatalError("--frontend not set but is required") } -let frontendURL = CommandLine.arguments[frontendURLIndex + 1] +let frontendURL: String = CommandLine.arguments[Int(frontendURLIndex) + 1] guard let usernameIndex = CommandLine.arguments.firstIndex(of: "--username"), CommandLine.arguments.count > usernameIndex + 1 else { fatalError("--username not set but is required") } -let username = CommandLine.arguments[usernameIndex + 1] +let username: String = CommandLine.arguments[usernameIndex + 1] guard let passwordIndex = CommandLine.arguments.firstIndex(of: "--password"), CommandLine.arguments.count > passwordIndex + 1 else { fatalError("--password not set but is required") } -let password = CommandLine.arguments[passwordIndex + 1] +let password: String = CommandLine.arguments[passwordIndex + 1] let restartAfter: Int if let index = CommandLine.arguments.firstIndex(of: "--after"), From 48fa4594cb58efa3d955c2c6dd9a793206c92c10 Mon Sep 17 00:00:00 2001 From: Fabio1988 <35898099+Fabio1988@users.noreply.github.com> Date: Sat, 22 Jan 2022 19:55:45 +0100 Subject: [PATCH 8/9] fix idevicename command Sometimes it's not possible to fetch a name from a single udid from idevice-id --list. This causes error like "Failed to load devices (or none connected)" --- .../Sources/MacLessManager/MacLessManager.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/MacLessManager/Sources/MacLessManager/MacLessManager.swift b/MacLessManager/Sources/MacLessManager/MacLessManager.swift index 2fc31056..008a14cd 100644 --- a/MacLessManager/Sources/MacLessManager/MacLessManager.swift +++ b/MacLessManager/Sources/MacLessManager/MacLessManager.swift @@ -60,13 +60,13 @@ class MacLessManager { while running { runningLock.unlock() guard let devices = try? getAllDevices(), !devices.isEmpty else { - self.logger.error("Failed to laod devices (or none connected)") + self.logger.error("Failed to load devices (or none connected)") sleep(1) continue } self.logger.info("\(devices.count) devices connected") guard let statusse = try? getAllDeviceStatusse(), !statusse.isEmpty else { - self.logger.error("Failed to laod statusse") + self.logger.error("Failed to load status") sleep(1) continue } @@ -130,8 +130,10 @@ class MacLessManager { var devices = [String: String]() let uuids = try getAllDeciceUUIDs() for uuid in uuids { - let name = try shellOut(to: "idevicename", arguments: ["--udid", uuid]) - devices[uuid] = name + let name = try? shellOut(to: "idevicename", arguments: ["--udid", uuid]) + if name != nil { + devices[uuid] = name + } } return devices } From 288826d455146d4c2d005c559a00f1676e47667c Mon Sep 17 00:00:00 2001 From: Fabio1988 <35898099+Fabio1988@users.noreply.github.com> Date: Sat, 22 Jan 2022 20:26:21 +0100 Subject: [PATCH 9/9] Example Script MacOS For running in endless loop to handle NSException --- MacLessManager/run.command | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 MacLessManager/run.command diff --git a/MacLessManager/run.command b/MacLessManager/run.command new file mode 100644 index 00000000..ba537230 --- /dev/null +++ b/MacLessManager/run.command @@ -0,0 +1,5 @@ +cd "`dirname "$0"`" +while true +do + swift run MacLessManager --backend https://map.url --username DeviceManager --password pass123 +done