Hi,
We have a macOS application that contains a helper daemon that was registered with launchd using the SMAppService
API and for the most part its been working okay until we tried to release an update that added an XPC service to the daemon. When users try to upgrade the software, the new service now fails to launch due to a launch constraint violation.
The Console log shows the following error after the upgrade:
AMFI: Launch Constraint Violation (enforcing), error info: c[5]p[1]m[1]e[0], (Constraint not matched) launching proc[vc: 6 pid: 1422]: /Applications/Mozilla VPN.app/Contents/Library/LaunchServices/org.mozilla.macos.FirefoxVPN.daemon, launch type 0, failure proc [vc: 6 pid: 1422]: /Applications/Mozilla VPN.app/Contents/Library/LaunchServices/org.mozilla.macos.FirefoxVPN.daemon
The service plist before the upgrade looked like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AssociatedBundleIdentifiers</key>
<string>org.mozilla.macos.FirefoxVPN</string>
<key>Label</key>
<string>org.mozilla.macos.FirefoxVPN.service</string>
<key>BundleProgram</key>
<string>Contents/MacOS/Mozilla VPN</string>
<key>ProgramArguments</key>
<array>
<string>Mozilla VPN</string>
<string>macosdaemon</string>
</array>
<key>UserName</key>
<string>root</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>1024</integer>
</dict>
<key>StandardErrorPath</key>
<string>/var/log/mozillavpn/stderr.log</string>
</dict>
</plist>
The updated plist changes the BundleProgram
, removes ProgramArguments
and adds MachServices
, which results in the following plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AssociatedBundleIdentifiers</key>
<string>org.mozilla.macos.FirefoxVPN</string>
<key>Label</key>
<string>org.mozilla.macos.FirefoxVPN.service</string>
<key>BundleProgram</key>
<string>Contents/Library/LaunchServices/org.mozilla.macos.FirefoxVPN.daemon</string>
<key>MachServices</key>
<dict>
<key>org.mozilla.macos.FirefoxVPN.service</key>
<true/>
</dict>
<key>UserName</key>
<string>root</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>1024</integer>
</dict>
<key>StandardErrorPath</key>
<string>/var/log/mozillavpn/stderr.log</string>
</dict>
</plist>
On a fresh machine/VM, this works just fine, we only encounter the Launch Constraint Violation when upgrading from one version to the next.
We were hoping that the service could have been upgraded by calling unregisterWithCompletionHandler first, but this seems have no effect on the bug.
So, I guess my questions are:
- Is there a way to diagnose what the launch constraints are for a service, and which why the constraints are being violated?
- How does one go about changing the plist for a daemon installed via
SMAppService
?
Thanks, Naomi
Ah, launch constraint errors, they’re always fun. Lemme provide some hints for specific fields. But first…
IMPORTANT The following uses a lot of concepts from the LightweightCodeRequirements. It’d be good to read that first.
So, about those fields:
-
vc: 6
— This is the validation category. You can actually find the corresponding values in the macOS SDK. Search<Kernel/kern/cs_blobs.h>
forCS_VALIDATION_CATEGORY
values. In this case, 6 isCS_VALIDATION_CATEGORY_DEVELOPER_ID
, which is exactly what you’d expect.Validation categories are also part of the LightweightCodeRequirements framework, expressed via the
ValidationCategory
type. -
pid: 1422
— That’s obviously a process ID. -
/Applications/Mozilla VPN.app/Contents/Library/LaunchServices/org.mozilla.macos.FirefoxVPN.daemon
— That one’s obvious too (-: -
launch type 0
— This value is of typecs_launch_type_t
, which you can also find in<Kernel/kern/cs_blobs.h>
. 0 is the valueCS_LAUNCH_TYPE_NONE
. -
c[5]
— This identifies the type of constraint. The value of 5 is a LightweightCodeRequirements (LWCR) spawn constraint. -
p[1]
— This identies the process the constraint applies to. The value of 1 is the process itself (2 is the parent, 3 is the responsible process). -
m[1]
— This is what happening with the matching. The value here, 1, indicates that there was no match. -
e[0]
— This is an error code. The value of 0 indicates no error (that is, the operation ran successfully and generated no match).
As to what this actually means, I’m not sure. My best guess is that launchctl
is applying a spawn constraint to invocation of your daemon and that’s somehow failing. However, I’m not sure how to debug that further.
Oh, wait, you changed the name of the executable. Did that coincidentally change its code signing identifier? If so, that’ll have changed its designated requirement (DR), and I can easily imagine that triggering this issue.
If you get the DR of the old executable and then check whether the new executable satisfies that DR, does it?
See TN3127 Inside Code Signing: Requirements for more background on DRs.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"