1→import Foundation 2→import Combine 3→ 4→/// Manages starting and stopping backend services 5→@MainActor 6→final class ServiceManager: ObservableObject { 7→ static let shared = ServiceManager() 8→ 9→ // MARK: - Published State 10→ @Published private(set) var managedProcesses: [String: ManagedProcessInfo] = [:] 11→ @Published private(set) var isOperationInProgress = false 12→ @Published var lastError: String? 13→ 14→ // MARK: - Service Definitions 15→ static let serviceDefinitions: [ServiceDefinition] = [ 16→ ServiceDefinition( 17→ name: "Log Server", 18→ port: 9001, 19→ command: "/usr/bin/python3", 20→ arguments: ["\(NSHomeDirectory())/Services/servers/log-server.py"], 21→ workingDirectory: "\(NSHomeDirectory())/Services/servers", 22→ healthEndpoint: "/health" 23→ ), 24→ ServiceDefinition( 25→ name: "System Monitor", 26→ port: 5555, 27→ command: "/usr/bin/python3", 28→ arguments: ["\(NSHomeDirectory())/Services/servers/system-monitor-server.py"], 29→ workingDirectory: "\(NSHomeDirectory())/Services/servers", 30→ healthEndpoint: "/api/system/all" 31→ ), 32→ ServiceDefinition( 33→ name: "WiFi API", 34→ port: 9002, 35→ command: "/usr/bin/python3", 36→ arguments: ["\(NSHomeDirectory())/Services/servers/wifi-api-server.py"], 37→ workingDirectory: "\(NSHomeDirectory())/Services/servers", 38→ healthEndpoint: "/health" 39→ ), 40→ ServiceDefinition( 41→ name: "Workspace API", 42→ port: 9004, 43→ command: "/bin/bash", 44→ arguments: ["\(NSHomeDirectory())/Scripts/system/start-workspace-api.sh"], 45→ workingDirectory: NSHomeDirectory(), 46→ healthEndpoint: "/health" 47→ ) 48→ ] 49→ 50→ // MARK: - Start Service 51→ 52→ func startService(_ service: ServiceStatus) async -> Bool { 53→ guard let definition = Self.serviceDefinitions.first(where: { $0.name == service.name }) else { 54→ lastError = "Service definition not found for \(service.name)" 55→ return false 56→ } 57→ 58→ return await startService(definition) 59→ } 60→ 61→ func startService(_ definition: ServiceDefinition) async -> Bool { 62→ guard !isOperationInProgress else { 63→ lastError = "Another operation is in progress" 64→ return false 65→ } 66→ 67→ isOperationInProgress = true 68→ defer { isOperationInProgress = false } 69→ 70→ // Check if already running 71→ if await isServiceRunning(port: definition.port) { 72→ lastError = "\(definition.name) is already running" 73→ return false 74→ } 75→ 76→ do { 77→ let process = Process() 78→ process.executableURL = URL(fileURLWithPath: definition.command) 79→ process.arguments = definition.arguments 80→ process.currentDirectoryURL = URL(fileURLWithPath: definition.workingDirectory) 81→ 82→ // Set up environment 83→ var environment = Foundation.ProcessInfo.processInfo.environment 84→ environment["PYTHONUNBUFFERED"] = "1" 85→ process.environment = environment 86→ 87→ // Capture output 88→ let outputPipe = Pipe() 89→ let errorPipe = Pipe() 90→ process.standardOutput = outputPipe 91→ process.standardError = errorPipe 92→ 93→ // Start process 94→ try process.run() 95→ 96→ // Store process info 97→ let info = ManagedProcessInfo( 98→ process: process, 99→ definition: definition, 100→ startTime: Date(), 101→ outputPipe: outputPipe, 102→ errorPipe: errorPipe 103→ ) 104→ managedProcesses[definition.name] = info 105→ 106→ // Wait a moment for service to start 107→ try await Task.sleep(for: .seconds(2)) 108→ 109→ // Verify it's running 110→ if await isServiceRunning(port: definition.port) { 111→ lastError = nil 112→ return true 113→ } else { 114→ // Read error output 115→ let errorData = errorPipe.fileHandleForReading.availableData 116→ let errorString = String(data: errorData, encoding: .utf8) ?? "" 117→ lastError = "Service failed to start: \(errorString)" 118→ managedProcesses.removeValue(forKey: definition.name) 119→ return false 120→ } 121→ } catch { 122→ lastError = "Failed to start \(definition.name): \(error.localizedDescription)" 123→ return false 124→ } 125→ } 126→ 127→ // MARK: - Stop Service 128→ 129→ func stopService(_ service: ServiceStatus) async -> Bool { 130→ guard let definition = Self.serviceDefinitions.first(where: { $0.name == service.name }) else { 131→ lastError = "Service definition not found for \(service.name)" 132→ return false 133→ } 134→ 135→ return await stopService(definition) 136→ } 137→ 138→ func stopService(_ definition: ServiceDefinition) async -> Bool { 139→ guard !isOperationInProgress else { 140→ lastError = "Another operation is in progress" 141→ return false 142→ } 143→ 144→ isOperationInProgress = true 145→ defer { isOperationInProgress = false } 146→ 147→ // First try to stop our managed process 148→ if let info = managedProcesses[definition.name] { 149→ info.process.terminate() 150→ managedProcesses.removeValue(forKey: definition.name) 151→ 152→ try? await Task.sleep(for: .seconds(1)) 153→ 154→ if !(await isServiceRunning(port: definition.port)) { 155→ lastError = nil 156→ return true 157→ } 158→ } 159→ 160→ // If still running, find and kill by port 161→ do { 162→ let success = try await killProcessOnPort(definition.port) 163→ if success { 164→ lastError = nil 165→ return true 166→ } else { 167→ lastError = "Could not stop service on port \(definition.port)" 168→ return false 169→ } 170→ } catch { 171→ lastError = "Error stopping service: \(error.localizedDescription)" 172→ return false 173→ } 174→ } 175→ 176→ // MARK: - Restart Service 177→ 178→ func restartService(_ service: ServiceStatus) async -> Bool { 179→ let stopped = await stopService(service) 180→ if !stopped { 181→ // Try to continue anyway, service might not have been running 182→ } 183→ 184→ try? await Task.sleep(for: .seconds(1)) 185→ 186→ return await startService(service) 187→ } 188→ 189→ // MARK: - Helper Methods 190→ 191→ func isServiceRunning(port: Int) async -> Bool { 192→ guard let url = URL(string: "http://localhost:\(port)/") else { return false } 193→ 194→ do { 195→ var request = URLRequest(url: url) 196→ request.timeoutInterval = 2 197→ let (_, response) = try await URLSession.shared.data(for: request) 198→ if let httpResponse = response as? HTTPURLResponse { 199→ return (200...499).contains(httpResponse.statusCode) 200→ } 201→ return false 202→ } catch { 203→ return false 204→ } 205→ } 206→ 207→ private func killProcessOnPort(_ port: Int) async throws -> Bool { 208→ // Find PID using lsof 209→ let findProcess = Process() 210→ findProcess.executableURL = URL(fileURLWithPath: "/usr/sbin/lsof") 211→ findProcess.arguments = ["-t", "-i", ":\(port)"] 212→ 213→ let outputPipe = Pipe() 214→ findProcess.standardOutput = outputPipe 215→ 216→ try findProcess.run() 217→ findProcess.waitUntilExit() 218→ 219→ let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() 220→ let outputString = String(data: outputData, encoding: .utf8) ?? "" 221→ 222→ let pids = outputString.components(separatedBy: .newlines) 223→ .compactMap { Int($0.trimmingCharacters(in: .whitespaces)) } 224→ 225→ if pids.isEmpty { 226→ return true // Nothing to kill 227→ } 228→ 229→ // Kill each PID 230→ for pid in pids { 231→ let killProcess = Process() 232→ killProcess.executableURL = URL(fileURLWithPath: "/bin/kill") 233→ killProcess.arguments = ["-9", String(pid)] 234→ 235→ try killProcess.run() 236→ killProcess.waitUntilExit() 237→ } 238→ 239→ // Wait and verify 240→ try await Task.sleep(for: .seconds(1)) 241→ return !(await isServiceRunning(port: port)) 242→ } 243→ 244→ // MARK: - Get Service Definition 245→ 246→ func getDefinition(for service: ServiceStatus) -> ServiceDefinition? { 247→ Self.serviceDefinitions.first { $0.name == service.name } 248→ } 249→ 250→ func getDefinition(forPort port: Int) -> ServiceDefinition? { 251→ Self.serviceDefinitions.first { $0.port == port } 252→ } 253→} 254→ 255→// MARK: - Supporting Types 256→ 257→struct ServiceDefinition: Identifiable { 258→ let id = UUID() 259→ let name: String 260→ let port: Int 261→ let command: String 262→ let arguments: [String] 263→ let workingDirectory: String 264→ let healthEndpoint: String 265→ 266→ var fullCommand: String { 267→ "\(command) \(arguments.joined(separator: " "))" 268→ } 269→} 270→ 271→class ManagedProcessInfo { 272→ let process: Process 273→ let definition: ServiceDefinition 274→ let startTime: Date 275→ let outputPipe: Pipe 276→ let errorPipe: Pipe 277→ 278→ var isRunning: Bool { 279→ process.isRunning 280→ } 281→ 282→ var uptime: TimeInterval { 283→ Date().timeIntervalSince(startTime) 284→ } 285→ 286→ init(process: Process, definition: ServiceDefinition, startTime: Date, outputPipe: Pipe, errorPipe: Pipe) { 287→ self.process = process 288→ self.definition = definition 289→ self.startTime = startTime 290→ self.outputPipe = outputPipe 291→ self.errorPipe = errorPipe 292→ } 293→} 294→ Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.