mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-10-02 08:04:32 +08:00
The launchd hook used to add the bootstrap's Library/LaunchDaemons to launchd's cache under /System/Library/LaunchDaemons/vphone.<name> keys. A job imported that way does not match the plist path a package script boots out, so upgrades could not unload their own daemons. RootHide's own hook loads only basebin/LaunchDaemons and leaves the rest to `jbctl startup`. The hook no longer interposes xpc_dictionary_get_value. After boot vphoned loads each plist in <root>/Library/LaunchDaemons under its real path. For RootHide it first rewrites the plist on disk the way RootHide's launchctl does (plistpatch.m, ported to Swift): Program, ProgramArguments[0], the working and root directories, standard streams, watch and queue paths, HOME/TMPDIR, KeepAlive.PathState, socket paths and fsevents paths get the root prepended, and __Patched marks the plist. services.load patches a plist under the root the same way, so no launchctl binary is needed. vphoned installs RootHide under one fixed name, .jbroot-000114514191980C, and the hook looks only there and in /var/jb. A RootHide root under another name must be uninstalled first. Tested on a RootHide guest (iOS 26.6.2): every daemon plist carries __Patched and is registered under its own path, ssh and sudo work, and launchctl bootout/bootstrap of ighostvtd and a dpkg install, remove and reinstall of openssh-server all load the job again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
74 lines
3.1 KiB
Swift
74 lines
3.1 KiB
Swift
import Foundation
|
|
import IcliKit
|
|
import IcliSystem
|
|
|
|
// MARK: - launchd Services
|
|
|
|
extension GuestAPI {
|
|
static func executeService(_ method: String, _ params: [String: Any]) throws -> [String: Any]? {
|
|
switch method {
|
|
case "services.list":
|
|
var result = try listServices()
|
|
if let disabled = try? disabledServiceOverrides()["disabled"] as? [String: Bool] {
|
|
let rows = result["services"] as? [[String: Any]] ?? []
|
|
result["services"] = rows.map { row -> [String: Any] in
|
|
var service = row
|
|
if let label = row["label"] as? String, let isDisabled = disabled[label] {
|
|
service["disabled"] = isDisabled
|
|
}
|
|
return service
|
|
}
|
|
}
|
|
return result
|
|
case "services.status":
|
|
return try serviceStatus(string(params, "label"))
|
|
case "services.print":
|
|
return try printService(string(params, "label"))
|
|
case "services.dump":
|
|
return try servicesDump()
|
|
case "services.disabled":
|
|
return try disabledServiceOverrides()
|
|
case "services.start":
|
|
return try startService(string(params, "label"))
|
|
case "services.enable":
|
|
return try setServiceEnabled(string(params, "label"), enabled: true)
|
|
case "services.stop":
|
|
let label = try string(params, "label")
|
|
try requireForce(params, "stop \(label)")
|
|
return try stopService(label)
|
|
case "services.disable":
|
|
let label = try string(params, "label")
|
|
try requireForce(params, "disable \(label)")
|
|
return try setServiceEnabled(label, enabled: false)
|
|
case "services.remove":
|
|
let label = try string(params, "label")
|
|
try requireForce(params, "remove \(label)")
|
|
return try removeService(label)
|
|
case "services.signal":
|
|
let label = try string(params, "label")
|
|
let signal = try string(params, "signal")
|
|
try requireForce(params, "send \(signal) to \(label)")
|
|
return try signalService(label, signal: signal)
|
|
case "services.load", "services.unload":
|
|
guard let paths = params["paths"] as? [String], !paths.isEmpty else {
|
|
throw GuestAPIError.invalidRequest("paths must list plist files or directories")
|
|
}
|
|
let load = method == "services.load"
|
|
if load {
|
|
try GuestIrisinInstaller.prepareBootstrapDaemons(paths)
|
|
} else {
|
|
try requireForce(params, "unload services")
|
|
}
|
|
return try loadServices(paths, load: load, override: bool(params, "override"))
|
|
case "launchd.getenv":
|
|
return try launchdEnvironment(string(params, "key"))
|
|
case "launchd.setenv":
|
|
return try setLaunchdEnvironment(string(params, "key"), value: string(params, "value"))
|
|
case "launchd.unsetenv":
|
|
return try setLaunchdEnvironment(string(params, "key"), value: nil)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
}
|