mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-09-02 02:34:29 +00:00
The library the consolidated CLI is built on: - bundle/library model (VPhoneBundle, VPhoneLibrary, VPhoneVirtualMachineManifest), bundle ops + reporting, restore helpers (VPhoneRestoreOps) - host process primitives: VPhoneProcessRunner, VPhoneManagedProcess (spawn + stdout pattern-match + SIGKILL-escalating terminate), VPhoneLaunchLayout, VPhoneBootPatterns - VPhoneResources: bundled-.app vs dev asset resolution, and Python resolution that provisions a per-user venv (~/.vphone/venv) on demand so the app is portable — never depends on the repo's .venv - VPhoneVerbosity (quiet/info/debug/trace) and VPhoneVMPicker Manifest moves out of the executable target into VPhoneCore. Full unit-test suite under tests/VPhoneCoreTests. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01Y4VDqWf5pVakcFLqB23CKe
73 lines
4.0 KiB
Swift
73 lines
4.0 KiB
Swift
import Foundation
|
|
|
|
// MARK: - VPhoneBootPatterns
|
|
|
|
/// Boot-log regex patterns and device-identity normalization, ported VERBATIM
|
|
/// from `scripts/setup_machine.sh` so the native `vm create` orchestrator
|
|
/// (`VPhoneCreateOrchestrator`, executable target) matches the proven shell
|
|
/// choreography exactly. Lives in VPhoneCore (rather than alongside the
|
|
/// orchestrator) purely so `VPhoneCoreTests` can unit-test these pure,
|
|
/// device-independent pieces — the orchestrator itself needs `FirmwarePatcher`
|
|
/// and can't live in VPhoneCore without creating a package dependency cycle
|
|
/// (FirmwarePatcher already depends on VPhoneCore).
|
|
public enum VPhoneBootPatterns {
|
|
/// `BOOT_BASH_PROMPT_REGEX` (setup_machine.sh:39) — the iosbinpack bash
|
|
/// prompt, or a ramdisk/root shell prompt.
|
|
public static let promptRegex = #"bash-[0-9]+(\.[0-9]+)+#|:/[^ ]* root#"#
|
|
|
|
/// `BOOT_PANIC_REGEX` (setup_machine.sh:40).
|
|
public static let panicRegex = #"(^|[^p])(panic|kernel panic|panic\.apple\.com|stackshot succeeded)"#
|
|
|
|
/// `monitor_boot_log_until` (setup_machine.sh:363-388) checks panic and
|
|
/// prompt with DIFFERENT case sensitivity — `grep -Eiq "$BOOT_PANIC_REGEX"`
|
|
/// (case-insensitive) vs. `grep -Eq "$BOOT_BASH_PROMPT_REGEX"` (case-
|
|
/// sensitive). A single `NSRegularExpression` has one global case-folding
|
|
/// setting, so the panic half is wrapped in an ICU scoped inline modifier
|
|
/// (`(?i:...)`) to fold ONLY that half, leaving the prompt half exactly as
|
|
/// case-sensitive as the shell's plain `grep -E`.
|
|
public static let panicOrPromptRegex = "(?i:\(panicRegex))|\(promptRegex)"
|
|
|
|
/// `send_first_boot_commands` (setup_machine.sh:344-361) — verbatim order,
|
|
/// including the exact PATH string (setup_machine.sh:348).
|
|
public static let firstBootCommands: [String] = [
|
|
"export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/usr/games:/iosbinpack64/usr/local/sbin:/iosbinpack64/usr/local/bin:/iosbinpack64/usr/sbin:/iosbinpack64/usr/bin:/iosbinpack64/sbin:/iosbinpack64/bin'",
|
|
"cp /iosbinpack64/etc/profile /var/profile",
|
|
"cp /iosbinpack64/etc/motd /var/motd",
|
|
"mkdir -p /var/dropbear",
|
|
"dropbearkey -t rsa -f /var/dropbear/dropbear_rsa_host_key",
|
|
"dropbearkey -t ecdsa -f /var/dropbear/dropbear_ecdsa_host_key",
|
|
"shutdown -h now",
|
|
]
|
|
|
|
/// Port of `normalize_ecid` (setup_machine.sh:80-86): strip one leading
|
|
/// `0x` and one leading `0X` (matching the shell's two sequential `#0x`/
|
|
/// `#0X` strips), require 1-16 ASCII hex digits, uppercase, and left-pad
|
|
/// with zeros to 16 characters. `nil` for anything that isn't valid hex —
|
|
/// the shell's `return 1` from the `[[ =~ ]]` guard.
|
|
public static func normalizeECID(_ raw: String) -> String? {
|
|
var value = raw
|
|
if value.hasPrefix("0x") { value.removeFirst(2) }
|
|
if value.hasPrefix("0X") { value.removeFirst(2) }
|
|
guard !value.isEmpty, value.count <= 16, value.allSatisfy(isASCIIHexDigit) else {
|
|
return nil
|
|
}
|
|
return String(repeating: "0", count: 16 - value.count) + value.uppercased()
|
|
}
|
|
|
|
/// ASCII-only hex digit check (`[0-9A-Fa-f]`) — deliberately narrower than
|
|
/// `Character.isHexDigit`, which also accepts Unicode fullwidth digits
|
|
/// that the shell's `[[ =~ ^[0-9A-Fa-f]{1,16}$ ]]` would reject.
|
|
private static func isASCIIHexDigit(_ c: Character) -> Bool {
|
|
guard let ascii = c.asciiValue else { return false }
|
|
return (0x30...0x39).contains(ascii) || (0x41...0x46).contains(ascii) || (0x61...0x66).contains(ascii)
|
|
}
|
|
|
|
/// Pure parse of `sysctl -n kern.hv_vmm_present` output — `"1"` means the
|
|
/// host is itself an Apple VM (nested), where Virtualization.framework
|
|
/// PV=3 guest boot is unavailable. Mirrors the `boot_host_preflight.sh`
|
|
/// gate `make boot` applied before native `vm create` replaced it.
|
|
public static func parseHVVmmPresent(_ output: String) -> Bool {
|
|
output.trimmingCharacters(in: .whitespacesAndNewlines) == "1"
|
|
}
|
|
}
|