| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- //
- // 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
- }
- }
|