如何在Xcode中调试含XPC服务、DockTile插件的macOS Privileges应用
问题背景
我正在对SAP开源的Privileges.app进行功能修改,该项目官方明确声明仅按“现状”提供,无任何官方支持、不做后续更新,欢迎开发者自行改进,但不提供任何形式的问题答疑与技术支持。
该应用包含两类我此前职业开发经历中从未接触过的组件:XPC service + helper tool(Launch daemon类型)以及DockTile插件。我在Xcode中启动运行该应用时遇到基础功能异常问题:应用可正常启动,但存在sandboxing、签名、entitlements相关的适配问题。我已将签名配置替换为自己的开发团队信息,项目可正常编译、链接、启动,但XPC服务尝试安装helper tool时会触发失败,对应运行日志如下:
2022-06-29 17:03:56.284544-0500 PrivilegesXPC[13079:128535] [logging-persist] cannot open file at line 45530 of [9ff244ce07] 2022-06-29 17:03:56.284570-0500 PrivilegesXPC[13079:128535] [logging-persist] os_unix.c:45530: (0) open(/var/db/DetachedSignatures) - Undefined error: 0 2022-06-29 17:04:21.060214-0500 PrivilegesXPC[13079:128537] SAPCorp: ERROR! Failed to connect to helper tool: NSCocoaErrorDomain / 4097 2022-06-29 17:04:31.471555-0500 Privileges[13064:127420] SAPCorp: ERROR! Error Domain=NSPOSIXErrorDomain Code=25 "Inappropriate ioctl for device" 2022-06-29 17:04:45.717751-0500 Privileges[13064:129162] SAPCorp: ERROR! Installation of the helper tool failed: Error Domain=CFErrorDomainLaunchd Code=4 "(null)"
经初步排查,最后两条报错来自如下代码段的执行失败:
success = SMJobBless( kSMDomainSystemLaunchd, CFSTR("corp.sap.privileges.helper"), self->_authRef, &error );
我暂时无法定位该调用失败的根本原因,检索“Inappropriate ioctl for device”相关错误资料也未获得有效排查方向,需要两方面的实操经验参考:
- 同类系统组件开发场景下
SMJobBless调用失败的可行排查思路 - DockTile插件的调试方法:目前观察到该插件运行在
SystemUIServer进程中,但即便使用root权限也无法通过Xcode附加到该进程进行调试。
排查与解决思路
SMJobBless安装Helper Tool失败排查
- 优先核对所有配置的ID一致性:
SMJobBless对签名、ID匹配校验极严,主App、XPC Service、Helper Tool三者的签名团队ID必须完全一致;传入SMJobBless的Helper ID必须和Helper的Info.plist里CFBundleIdentifier、Launchd配置plist里的Label完全匹配;尤其别漏改主App Info.plist里的SMPrivilegedExecutables字段,必须把原SAP的团队ID、Bundle ID前缀全部替换成你自己的,90%的CFErrorDomainLaunchd Code=4报错都是漏改这个字段导致的。 - 核对entitlements配置:Helper Tool作为系统级Launch Daemon 绝对不能开启App Sandbox,主App需要包含
com.apple.developer.security.smjobbless权限,XPC Service的entitlements要正确配置和Helper Tool的通信权限,不要给XPC或Helper误开沙盒,否则会因为权限不足无法写入系统级LaunchDaemon目录。 - 清理旧版本残留:之前安装失败的残留文件会导致签名校验冲突,先执行以下命令彻底清理:
如果你改了Helper的Bundle ID,把上面命令里的ID换成你自己新的即可。sudo rm -rf /Library/PrivilegedHelperTools/corp.sap.privileges.helper sudo rm -rf /Library/LaunchDaemons/corp.sap.privileges.helper.plist sudo launchctl remove corp.sap.privileges.helper - 错误信息优先级判断:
- 日志里的
open(/var/db/DetachedSignatures)是系统日志组件的通用无关报错,和当前应用问题完全无关,直接忽略即可。 NSPOSIXErrorDomain Code=25 "Inappropriate ioctl for device"是XPC通道未建立时的连带报错,不是根因,只要Helper安装成功、XPC通信正常就会自动消失,不需要单独排查。CFErrorDomainLaunchd Code=4是核心报错,对应launchd拒绝加载作业,除了前面说的ID、签名、entitlements问题,还要注意Helper安装到/Library/PrivilegedHelperTools/后权限必须是root:wheel 0755,权限不对也会触发这个错误。
- 日志里的
- 校验授权对象:在
SMJobBless调用前加断点,检查传入的_authRef是否正确获取了管理员授权,授权流程没走完、拿到的授权对象无效也会导致调用失败。
DockTile插件调试方法
- SIP开启状态下系统会保护
SystemUIServer、Dock这类UI核心进程,禁止调试器附加,要做断点调试必须先到恢复模式执行csrutil disable关闭SIP,重启后再操作。 - 不要直接附加已经运行的SystemUIServer,先执行
killall SystemUIServer让系统自动重启该进程,等进程重启完成后,用当前登录用户的权限(不需要root)在Xcode的Attach to Process列表里找到SystemUIServer附加即可。 - 比附加调试更稳定的方式是打日志:在DockTile插件的代码入口、核心逻辑处加
NSLog输出,日志会直接同步到系统「控制台」App里,不需要附加进程就能看到完整执行流程,也不会触发系统进程保护。 - 每次修改DockTile插件代码后,要把最新编译的App重新拖到Dock上,再执行
killall Dock重启Dock和SystemUIServer,确保系统加载的是你最新编译的插件版本,避免Xcode构建缓存、Dock缓存导致加载旧版本插件。
内容的提问来源于stack exchange,提问作者bruth
相关产品推荐
相关产品推荐

