2

I have an app service I want to start at system startup with a plist file:

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.my.app.ident</string>
  <key>ProgramArguments</key>
  <array>
    <string>/Users/me/Desktop/MyApp/App</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <true/>
  <key>StandardOutPath</key>
  <string>/Users/me/Desktop/MyApp/logfile.log</string>
  <key>StandardErrorPath</key>
  <string>/Users/me/Desktop/MyApp/logerr.log</string>
  <key>UserName</key>
  <string>me</string>
</dict>
</plist>

This has been created with

sudo chown root:wheel /Library/LaunchDaemons/com.my.app.ident.plist

and permissions

sudo chmod a+x /Library/LaunchDaemons/com.my.app.ident.plist

and then loaded:

sudo launchctl load -w /Library/LaunchDaemons/com.my.app.ident.plist

The application has permissions like:

-rwxr-xr-x 1 me staff 54755728 29 Nov 12:46 App

Which runs fine but is not starting up with the system - it just logs errors to logerr.log repeating:

Couldn't memory map the bundle file for reading.
A fatal error occured while processing application bundle
Failed to map file. open(/Users/me/Desktop/MyApp/App) failed with error 1
Failure processing application bundle.

These errors stop when the user "me" signs in and then the service starts working. I need it to work without "me" signing in.

any ideas?

Sean
  • 2,033
  • 2
  • 23
  • 28
  • 1
    A few ideas here: (1) is this truly an app, as in, using Cocoa and calling `NSApplicationMain()`? If so, I don't think you'll have much joy running this as a launch *daemon*. (LoginWindow LaunchAgent would be more appropriate for this case) (2) Do you have encrypted home directories enabled? (3) Is the app sandboxed? (4) Have you tried placing the app somewhere more generic, such as in `/Applications/`? – pmdj Dec 03 '22 at 10:01
  • It's a dotnet core console app. It doesn't have a native ui just a Web server. 2 no 3 no 4 no not yet you think that would make a difference? – Sean Dec 03 '22 at 12:01
  • OK, for a web service, LaunchDaemon makes sense. I think (4) is worth a try. To clarify, is it in an .app bundle? Because the errors suggest that it is, but the path suggests that it isn't. If it's an .app bundle, then App Translocation also becomes an issue. I'm not super familiar with how dotnet works with macOS - is your program a true macOS (Mach-O) executable binary, or does it launch a runtime which loads the dotnet assembly containing the code? If it's anything other than a native binary, it might not be able to find the other files it needs to run. – pmdj Dec 03 '22 at 14:55
  • Its a native binary – Sean Dec 03 '22 at 21:24
  • 1
    @pmdj so it seems that installing it to /Applications/ resolves the issue. For some reason having it in a Users directory means it can't start it which is weird as I thought LaunchDaemon ran as root? But whatever, thanks so much for the idea! – Sean Dec 04 '22 at 04:03
  • In your case, due to `UserName me`, it doesn't run as root, although as long as the name matches the `Users/me` you'd think this wouldn't be a problem. I'm not exactly sure which mechanism prevents access here, but pre-login is an oddly locked down environment, so I'm not especially surprised. – pmdj Dec 04 '22 at 10:36
  • 1
    I suspect it's due to the "Files and Folders" consent system, which protects various home directory subfolders such as Desktop, Documents, Downloads, etc. (see Security & Privacy system preference pane) – pmdj Dec 04 '22 at 10:51

1 Answers1

2

From discussion in the comments, we have narrowed it down to a path issue. The exact mechanism preventing access isn't 100% clear to me here, but pre-login is a fairly locked down environment, so I'm not terribly surprised it doesn't work running from your user's desktop folder.

I suspect that in your particular case it's down to the consent system: the logged-in user needs to grant each process access to certain directories such as Desktop, Documents, Downloads, etc., and as there is no user logged in, there will be no consent on record.

If your executable was inside an .app bundle, app translocation would be an additional concern.

The solution is to install launch daemon or global launch agent binaries system-wide. If they're part of an .app bundle, install the app in /Applications (this will also avoid app translocation issues). Otherwise, a "good" location is /Library/Application Support/[Your-Application]/.

Incidentally, if you're installing your daemon from a GUI app, on macOS 12 'Monterey' or older, SMJobBless is an even better solution than manually picking a location and dropping a plist into /Library/LaunchDaemons.

For macOS 13 'Ventura' and newer, take a look at the section about daemons and agents in the WWDC22 'What's new in Privacy' session and the new SMAppService APIs available there.

pmdj
  • 22,018
  • 3
  • 52
  • 103