This article is for serious nerds only.
Apple’s security schemes have caused so many people so much confusion and pain, it’s just unbelievable.
It took me 10 hours of my time to track down what appears to be a nasty bug, or perhaps design defect, in Apple’s macos Launch Daemon support. It took that long because the nature of the bug hid all useful information from me until I chanced upon a solution.
ISSUE: launch daemon’s stdout and stderr always fail on external volume
If there is a solution to this, I’d be delighted to hear it.
A daemon process created by launchctl can have (and needs) a file for stdout and stderr (StandardOutPath and/or StandardErrorPath).
As far as I can tell, there is no way to allow permissions for StandardOutPath and/or StandardErrorPath on an external volume. That is, macOS will always deny access, no matter the conditions:
- Whether or not that volume has "ignore permissions" or not.
- Even if 'sh' and the script itself are both given Full Disk Access.
- Even when the launch daemon is running as the user that owns the entire folder path (eg "webserver" and the volume and its contents are owned by webserver).
- No ACLs are present, all permissions are appropriate; it's not a simple error like that.
- Works fine if the locations are on an internal (non-ejectable) volume.
Note that this case is different than, say, giving java or python or some program Full Disk Access—that works fine.
This case is with launchctl itself, in setting up stdout and stderr; the issue is that the shell cannot even set up stdout and stderr, and aborts before the script can be run.
Snippet of launch daemon (plist) script (paths simplified for clarity) in /Library/LaunchDaemons:
<key>ProgramArguments</key> <array> <string>/webserver/scripts/tomcat-launchd.sh</string> <====== #!/bin/sh </array> <key>UserName</key> <string>webuser</string> <key>StandardOutPath</key> <string>/Volumes/webserver/logs/stdout.log</string> <key>StandardErrorPath</key> <string>/Volumes/webserver/logs/stdout.log</string>
If a StandardOutPath or StandardErrorPath is on an *external* volume, one always gets permissions error, as shown. On an internal volume, no problem, everything works fine.
File paths and script names modified/simplified simplified for brevity/clarity.
2022-11-25 21:28:10.766461 (system)
: Bootstrap by launchctl for /Library/LaunchDaemons/webserver.plist succeeded (0: ) 2022-11-25 21:28:10.766469 (system) : exiting bootstrap mode 2022-11-25 21:28:10.766499 (system/WEBSERVER ) : internal event: SOURCE_ATTACH, code = 0 2022-11-25 21:28:10.769313 (system/WEBSERVER ) : Service could not initialize: posix_spawn(/Volumes/webserver/scripts/tomcat-launchd.sh) error: 0x1: Operation not permitted 2022-11-25 21:28:10.769320 (system/WEBSERVER ) : initialization failure: 21G217: xpcproxy + 23748 [7B354E71-81A5-3251-9AA5-BAF533AC5A21]: 0x1 2022-11-25 21:28:10.769322 (system/WEBSERVER ) : internal event: INIT, code = 1 2022-11-25 21:28:10.769585 (system/WEBSERVER ) : xpcproxy exited due to exit(78) 2022-11-25 21:28:10.769594 (system/WEBSERVER ) : exited due to exit(78)
There is no apparent mechanism to allow the specified file paths to work.