fix(agent): write absolute ProgramArguments path before SMAppService registration

SMAppService does not reliably resolve relative ProgramArguments paths
for launchd (launchd reports inferred program + partial import and
rejects spawn with EX_CONFIG). Rewrite the plist's ProgramArguments[0]
to the wrapper's absolute path just before register().
This commit is contained in:
Qiu Yuzhou
2026-09-26 23:13:26 +08:00
parent 354b0fb59e
commit d08f0583d7
+30 -3
View File
@@ -9,9 +9,11 @@ protocol LaunchAgentControlling {
func unregister() throws
}
/// 生产实现:plist 必须位于 app bundle `Contents/Library/LaunchAgents/`,
/// ProgramArguments 为 Contents/ 相对路径(launchd 按注册 app 的 Contents/
/// 解析)。
/// 生产实现:注册时从 bundle 内模板 plist 生成带绝对 ProgramArguments 的
/// 临时 plist,写入 `Contents/Library/LaunchAgents/` 后交给 SMAppService。
/// SMAppService 对相对路径的 `ProgramArguments` 解析不可靠(launchd 显示
/// `inferred program` + `partial import` 并以 EX_CONFIG 拒绝 spawn),
/// 因此注册前把 wrapper 可执行文件的绝对路径写入 plist。
struct SMAppLaunchAgentService: LaunchAgentControlling {
static let plistName = "com.qiuyuzhou.ShadowsocksX-NG2.agent.plist"
@@ -33,10 +35,35 @@ struct SMAppLaunchAgentService: LaunchAgentControlling {
}
func register() throws {
try rewritePlistWithAbsoluteProgramPath()
try service.register()
}
func unregister() throws {
try service.unregister()
}
/// 把 bundle 内 plist 的 ProgramArguments[0] 重写为 wrapper 的绝对路径。
/// 只在注册前调用;卸载不要求还原。
private func rewritePlistWithAbsoluteProgramPath() throws {
let bundle = Bundle.main.bundleURL
let plistURL = bundle.appendingPathComponent(
"Contents/Library/LaunchAgents/\(Self.plistName)")
let agentURL = bundle.appendingPathComponent("Contents/MacOS/ShadowsocksX-NG2Agent")
guard
let data = try? Data(contentsOf: plistURL),
var plist = try? PropertyListSerialization.propertyList(from: data, format: nil)
as? [String: Any]
else {
throw LaunchAgentPlistError.unreadable
}
plist["ProgramArguments"] = [agentURL.path]
let updated = try PropertyListSerialization.data(
fromPropertyList: plist, format: .xml, options: 0)
try updated.write(to: plistURL, options: .atomic)
}
}
enum LaunchAgentPlistError: Error, Equatable {
case unreadable
}