Post

Replies

Boosts

Views

Activity

Reply to SMAppService re-register after app upgrade
Correct. I embedded an XPC Service and now it's bundled with the main app in XPCServices folder. So instead of agent(plistName: String) I init the service with daemon(plistName: String). In order to run the service as a LaunchDaemon I need either (1) register it using SMAppService API (plist is in the app bundle at Contents/Library/LaunchDaemons/ folder) or (2) put the plist to /Library/LaunchDaemons/ and run launchctl load <plist>. Approach (1) with SMAppService is more user friendly as it provides more visibility and user control by showing 'Background Item needs approval' notification and listing it in Login Items in Settings. So I was looking into migrating (2) launchctl approach to (1) SMAppService. For the use case of application upgrade (new .pkg file released) we need to re-launch currently running LaunchDaemon from postinstall script. Doing it by invoking launchctl unload and then launchctl load works seamless for end users (for initial installation users are not even aware that a service has been also installed since they were not presented with any visuals). Using (1) SMAppService it can be done by calling unregister and then register from postinstall script invoking a helper binary within the same app bundle (during initial installation users are presented with above mentioned notification which is great). Unfortunately calling register alone doesn't re-register the service and the old process from old package continues running. In order to kill currently running process it's required to unregister the service first. But unfortunately let service = SMAppService.daemon(plistName: "com.xpc.example.service.plist") try await service.unregister() // try await Task.sleep(nanoseconds: 500_000_000) try await service.register() requires experimentally discovered 500ms delay between unregister and register as mentioned in the original post. (Trying another unregister with completion handler doesn't make any difference). Otherwise register throws Unable to register LaunchDaemon(com.xpc.example.service.plist): Error Domain=SMAppServiceErrorDomain Code=1 "Operation not permitted" UserInfo={NSLocalizedFailureReason=Operation not permitted} (+ problem #2 with strict requirements of where the helper binary must reside inside application bundle).
Nov ’24
Reply to LaunchCodeRequirement alternatives
No. Well, if there were, that’d be a significant security bug (-: Nice! Thanks for confirming that! At least we can rely on LaunchCodeRequirement on macOS 14.4+. Not really. As you’ve determined, from the parent’s perspective it looks like the process died very early with a SIGKILL. There’s no additional information that comes along with that termination status. You could turn around and run code signing checks on the executable, but at best that only gives you indirect information. Well, I expected process.run() to throw an exception I could catch and learn from it what exactly has happened in case of failed codesign requirement check. I can submit a feature request if you think it's doable.
Topic: Code Signing SubTopic: General
Feb ’25
Reply to SMAppService re-register after app upgrade
Correct. I embedded an XPC Service and now it's bundled with the main app in XPCServices folder. So instead of agent(plistName: String) I init the service with daemon(plistName: String). In order to run the service as a LaunchDaemon I need either (1) register it using SMAppService API (plist is in the app bundle at Contents/Library/LaunchDaemons/ folder) or (2) put the plist to /Library/LaunchDaemons/ and run launchctl load <plist>. Approach (1) with SMAppService is more user friendly as it provides more visibility and user control by showing 'Background Item needs approval' notification and listing it in Login Items in Settings. So I was looking into migrating (2) launchctl approach to (1) SMAppService. For the use case of application upgrade (new .pkg file released) we need to re-launch currently running LaunchDaemon from postinstall script. Doing it by invoking launchctl unload and then launchctl load works seamless for end users (for initial installation users are not even aware that a service has been also installed since they were not presented with any visuals). Using (1) SMAppService it can be done by calling unregister and then register from postinstall script invoking a helper binary within the same app bundle (during initial installation users are presented with above mentioned notification which is great). Unfortunately calling register alone doesn't re-register the service and the old process from old package continues running. In order to kill currently running process it's required to unregister the service first. But unfortunately let service = SMAppService.daemon(plistName: "com.xpc.example.service.plist") try await service.unregister() // try await Task.sleep(nanoseconds: 500_000_000) try await service.register() requires experimentally discovered 500ms delay between unregister and register as mentioned in the original post. (Trying another unregister with completion handler doesn't make any difference). Otherwise register throws Unable to register LaunchDaemon(com.xpc.example.service.plist): Error Domain=SMAppServiceErrorDomain Code=1 "Operation not permitted" UserInfo={NSLocalizedFailureReason=Operation not permitted} (+ problem #2 with strict requirements of where the helper binary must reside inside application bundle).
Replies
Boosts
Views
Activity
Nov ’24
Reply to SMAppService re-register after app upgrade
No, the app is not sandboxed and the service is declared as a MachService in plist.
Replies
Boosts
Views
Activity
Nov ’24
Reply to LaunchCodeRequirement alternatives
No. Well, if there were, that’d be a significant security bug (-: Nice! Thanks for confirming that! At least we can rely on LaunchCodeRequirement on macOS 14.4+. Not really. As you’ve determined, from the parent’s perspective it looks like the process died very early with a SIGKILL. There’s no additional information that comes along with that termination status. You could turn around and run code signing checks on the executable, but at best that only gives you indirect information. Well, I expected process.run() to throw an exception I could catch and learn from it what exactly has happened in case of failed codesign requirement check. I can submit a feature request if you think it's doable.
Topic: Code Signing SubTopic: General
Replies
Boosts
Views
Activity
Feb ’25
Reply to Proper Approach to Programmatically Determine SIP State
Thanks for confirming there's currently no good way to determine SIP status. I've submitted FB17530892 as you recommended. Yeah, we can't really trust csrutil status due to non-working LaunchCodeRequirement and inability to validate its code signing requirement when SIP is disabled so didn't even mention it.
Replies
Boosts
Views
Activity
May ’25
Reply to SSO Extension Fails XPC Connection to System Daemon (mach-lookup exception used)
Thanks for the links! Adding the service to AppGroup and updating its MachService name to <AppGroupName>.serviceName did the trick!
Replies
Boosts
Views
Activity
Oct ’25