How does an app install/activate an independently-running daemon?

I had assumed the answer was "copy or create a plist in /Library/LaunchDaemons," but after poking around here and google a bit, I'm more confused. (Which seems to be a normal thing for me, so I'll hold off deciding I'm stupid for a while.)

The options that I seem to see are:

  • Copy/create a plist in /Library/LaunchDaemons
  • Have Foo.app/Contents/Library/LaunchAgents, which will, I presume, run something as long as the app is running?
  • Use SMJobBless to install the daemon. This is the preferred way, and requires an embedded launchd plist, which I presume is what will be installedinto /Library/LaunchDaemons? And "embedded" means "pushed into the binary because MachO is infinitely versatile so we can do this if we want to"? This requires user interaction to get an authorization?

And... if the app is distributed via MDM, then that can install the launchd plist file without the app needing to run, just like it can install a system extension without the app needing to run?

My question is complicated by me (currently anyway) having the daemon as an app bundle, embedded inside my wrapping app so it has its own set of resources. But I can deal with that by simply asking for the bundle for the main app by its bundle ID, I presume, and having something like MyApp.app/Contents/Library/Daemon/Resources. I had gone for the app-like approach because of a post Eskimo wrote, that I can't find right now but I probably have stashed in my copious and disorganized notes.

No, that's right, I need to use the app-like structure because of entitlements. So given that, I don't think I can use SMJobBless?

I need to use the app-like structure because of entitlements. So given that, I don't think I can use SMJobBless?

That’s correct.

As to your other questions, it’s hard to offer concrete advice without knowing more about your specific requirements. However, there are basically four ways of installing a daemon:

  • Packing the daemon as a system extension and then installing it using System Extensions framework (strictly speaking this isn’t a launchd daemon but I suspect it’s relevant to your setup).

  • As above, except using MDM.

  • Calling SMJobBless.

  • Laying down the files on disk and then invoking launchctl to load the daemon. Traditionally this is done with an installer package.


I must stress that launchd daemons and agents are different things. An agent runs in the context of a specific user whereas a daemon runs in the global context. See Technote 2083 Daemons and Agents for more about this.

There are three ways to install something that acts like an agent:

  • Calling SMLoginItemSetEnabled. Technically this isn’t an agent, but it’s close enough. Specifically, you can communicate with it with XPC.

    Also, this is supported in the App Sandbox whereas the others are not.

  • Installing a login item via the legacy LSSharedFileList API. This isn’t an agent at all — there’s no support for XPC — but it does have one key advantage, namely that the login item is visible to the user in System Preferences > Users & Groups.

  • Laying down the files on disk and then invoking launchctl to load the agent. This can be tricky if there are multiple users installed at the point you install the agent.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Fortunately, I am familiar with the differences between an agent and a daemon. 😄

I am very relieved to know I wasn't reading things incorrectly about SMBlessJob.

The basics of what I was trying to achieve: we'll have a daemon, which will be used to do things like start and stop the proxy as needed, coordinate information about the system account and related settings, check for updates (if we don't end up using App Store deployment, which seems likely to be the case), and various other things. It'll need entitlements because -- once I get the bit from Apple -- I'll also have it doing anti-tampering using Endpoint Security.

All of that is pretty simple and basic. What I had hoped to do, however, was allow a drag&drop installation. In that case, when the container application was launched, it would check to see if various files were installed, and if not, do that itself. But to do that it would need user permission, and the call I would have used has been deprecated. I think I can do that using a helper-tool with privilege, but I haven't mapped that out yet (for clarification: the "think I can do that" means that, yes, I am fairly positive it's technically possible, but I haven't figured out what the details are or what the code would look like). Instead, at least for now, I think we'll have to rely on a pkg installation method, which can run a script to install a plist in /Library/LaunchDaemons as well as a few other things. The preferred installation mechanism will presumably be via MDM. Which means that's going to be one of the next things I have to ask about, but that's for another time. 😄

I hadn't at all thought about having it claim it's a system extension. But since I want that tasty Endpoint Security capability... that might work, mightn't it? In which case, the daemon would be part of the app bundle, and then get it installed using OSSystemExtensionRequest. (If you could see my face right now, you'd see my eyes wide and looking up to my left...)

I hadn't at all thought about having it claim it's a system extension. But since I want that tasty Endpoint Security capability... that might work, mightn't it?

A sysex is our preferred deployment model for an ES client. For an explanation as to why, see WWDC 2020 Session 10159 Build an Endpoint Security app.

If you have an ES sysex then I’m not sure you need a separate launchd daemon. A sysex can do pretty much anything that a launchd daemon can do. Specifically:

  • It runs with privileges.

  • It can advertise an XPC listener using NSEndpointSecurityMachServiceName.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Well it's not an ES sysex yet because I'm still waiting for Apple to give it to me. It's been 5 or 6 weeks, although I recognize the holidays mess everyone's schedules up.

Is it possible to customize an ES sysex launchd file? This is for future reference, I'm not sure that I do need any such customization -- my initial thought had simply been to have a simple launchd.plist that said "run this all the time" and install that via the pkg installer.

I'm rewriting my containing app's extension loader code right now, to handle multiple extensions.

Is it possible to customize an ES sysex launchd file?

No.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Well that's a nice, simple answer for that, at least. 😄

How does an app install/activate an independently-running daemon?
 
 
Q