// // Copyright 2026 Aarav Ravindra Kharade // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // import Foundation // ═══════════════════════════════════════════════════════════════════ // ArkOS Command Router // ═══════════════════════════════════════════════════════════════════ // Routes incoming IPC requests to the appropriate system handler. // Uses a binary header protocol for efficient, type-safe communication // without string parsing overhead. // // Wire Format (all multi-byte values are big-endian): // Request: [Command_ID (2B)][Payload_Length (4B)][Payload_Data] // Response: [Command_ID (2B)][Payload_Length (4B)][Response_Data] // // Command Registry: // 100 — Reserved // 101 — GET_TIME → Current date/time string // 102 — GET_IP → Primary IPv4 address // 103 — GET_BATTERY → Battery percentage and charge state // 104 — GET_BLUETOOTH → Bluetooth adapter status // 105 — SHUTDOWN → Initiate system shutdown // 106 — DUMP_LOGS → Retrieve system log buffer // 107 — GET_INTERFACES → List all network interfaces with state // 108 — GET_SERVICES → Service status report // 109 — RESTART_SERVICE → Restart a named service // 110 — GET_SYSTEM_INFO → CPU, RAM, uptime summary // ═══════════════════════════════════════════════════════════════════ // ── Public API Namespaces ───────────────────────────────────────── /// The `ark` namespace provides the developer-facing API surface /// for querying OS state. These are high-level wrappers around the /// raw managers in KernelBridge.swift. public struct ark { public struct system { /// Time and uptime queries. public struct time { /// Returns the current date and time as a formatted string. public static func getCurrentTime() -> String { let formatter = DateFormatter() formatter.timeStyle = .medium formatter.dateStyle = .medium return formatter.string(from: Date()) } /// Returns the system uptime in seconds by reading /proc/uptime. public static func getUptime() -> Double { if let data = try? String(contentsOfFile: "/proc/uptime") { let parts = data.split(separator: " ") if let first = parts.first, let uptimeVal = Double(first) { return uptimeVal } } return 0.0 } /// Returns uptime as a human-readable string (e.g. "2h 15m 30s"). public static func getUptimeFormatted() -> String { let total = Int(getUptime()) let hours = total / 3600 let minutes = (total % 3600) / 60 let seconds = total % 60 if hours > 0 { return "\(hours)h \(minutes)m \(seconds)s" } else if minutes > 0 { return "\(minutes)m \(seconds)s" } return "\(seconds)s" } } /// Network state queries. public struct network { /// Returns the primary IPv4 address. public static func getIPAddress() -> String { return NetworkManager.getInterfaceIP() } /// Returns names of all non-loopback interfaces. public static func getInterfaceNames() -> [String] { return NetworkManager.getInterfaceNames() } /// Returns true if any interface has connectivity. public static func isConnected() -> Bool { return NetworkManager.isConnected() } /// Returns detailed info for all interfaces. public static func getAllInterfaces() -> [NetworkManager.InterfaceInfo] { return NetworkManager.getAllInterfaces() } } /// Raw input queries (for pre-display-server console mode). public struct input { public static func readKey() -> Character? { return InputManager.readKeyStroke() } } } } // ── Command ID Constants ────────────────────────────────────────── /// All supported IPC command identifiers. public enum CommandID: UInt16 { case getTime = 101 case getIP = 102 case getBattery = 103 case getBluetooth = 104 case shutdown = 105 case dumpLogs = 106 case getInterfaces = 107 case getServices = 108 case restartService = 109 case getSystemInfo = 110 } // ── Command Router ──────────────────────────────────────────────── public struct CommandRouter { /// Routes an incoming binary request to the appropriate handler /// and returns a binary response frame. /// /// - Parameter requestBuffer: Raw bytes of the incoming request. /// - Returns: Binary response frame with header and payload. public static func route(requestBuffer: UnsafeRawBufferPointer) -> Data { // Validate minimum header size (2B command + 4B length = 6B) guard requestBuffer.count >= 6 else { LogManager.log("Router: Invalid header (\(requestBuffer.count) bytes, need >= 6)", level: .warn) return makeResponse(cmdId: 0, payload: "ERROR:INVALID_HEADER") } // Parse header fields (zero-copy, big-endian) let rawCmdId = requestBuffer.load(fromByteOffset: 0, as: UInt16.self).bigEndian let payloadLen = requestBuffer.load(fromByteOffset: 2, as: UInt32.self).bigEndian // Validate payload length guard requestBuffer.count >= 6 + Int(payloadLen) else { LogManager.log("Router: Payload truncated (declared=\(payloadLen), actual=\(requestBuffer.count - 6))", level: .warn) return makeResponse(cmdId: rawCmdId, payload: "ERROR:SIZE_MISMATCH") } // Extract payload string (zero-copy slice) let payloadBytes = UnsafeRawBufferPointer(rebasing: requestBuffer[6..<(6 + Int(payloadLen))]) let payloadString = String(decoding: payloadBytes, as: UTF8.self) LogManager.log("Router: CMD=\(rawCmdId) payload_len=\(payloadLen)") // Dispatch to the appropriate handler let responseString: String if let cmdId = CommandID(rawValue: rawCmdId) { switch cmdId { case .getTime: responseString = ark.system.time.getCurrentTime() case .getIP: responseString = ark.system.network.getIPAddress() case .getBattery: let pct = PowerManager.getBatteryPercentage() let state = PowerManager.getChargeState() let ac = PowerManager.isACConnected() ? "AC" : "Battery" responseString = "\(pct)%|\(state.rawValue)|\(ac)" case .getBluetooth: let enabled = BluetoothManager.isEnabled() let adapters = BluetoothManager.getAdapters() responseString = enabled ? "ACTIVE|\(adapters.joined(separator: ","))" : "INACTIVE" case .shutdown: responseString = "SHUTTING_DOWN" // Dispatch shutdown asynchronously so the response is sent first ResourceController.executeOnCorePool { sleep(1) PowerManager.shutdown() } case .dumpLogs: responseString = LogManager.dumpLogs().joined(separator: "\n") case .getInterfaces: let interfaces = NetworkManager.getAllInterfaces() let lines = interfaces.map { iface in "\(iface.name)|\(iface.isUp ? "UP" : "DOWN")|\(iface.hasCarrier ? "CARRIER" : "NO_CARRIER")|\(iface.ipAddress ?? "none")|\(iface.macAddress ?? "unknown")" } responseString = lines.joined(separator: "\n") case .getServices: responseString = ServiceManager.shared.getStatusReport() case .restartService: if !payloadString.isEmpty { ServiceManager.shared.stopService(name: payloadString) ServiceManager.shared.startService(name: payloadString) responseString = "OK:RESTARTED:\(payloadString)" } else { responseString = "ERROR:MISSING_SERVICE_NAME" } case .getSystemInfo: let cores = ResourceController.getCPUCoreCount() let totalMem = ResourceController.getTotalMemory() / 1024 / 1024 let usedMem = ResourceController.checkMemoryUsage() / 1024 / 1024 let uptime = ark.system.time.getUptimeFormatted() let connected = NetworkManager.isConnected() ? "Connected" : "Disconnected" responseString = "cores=\(cores)|ram=\(usedMem)/\(totalMem)MB|uptime=\(uptime)|net=\(connected)" } } else { LogManager.log("Router: Unknown command ID \(rawCmdId)", level: .warn) responseString = "ERROR:UNKNOWN_COMMAND" } return makeResponse(cmdId: rawCmdId, payload: responseString) } // ── Response Builder ────────────────────────────────────────── /// Constructs a binary response frame from a command ID and string payload. private static func makeResponse(cmdId: UInt16, payload: String) -> Data { let payloadData = payload.data(using: .utf8) ?? Data() var response = Data() response.reserveCapacity(6 + payloadData.count) var bigEndianCmd = cmdId.bigEndian var bigEndianLen = UInt32(payloadData.count).bigEndian withUnsafeBytes(of: &bigEndianCmd) { response.append(contentsOf: $0) } withUnsafeBytes(of: &bigEndianLen) { response.append(contentsOf: $0) } response.append(payloadData) return response } }