How to correctly deploy bundled launchdaemons/launchagents?

I'm working on an enterprise product that's mainly a daemon (with Endpoint Security) without any GUI component. I'm looking into the update process for daemons/agents that was introduced with Ventura (Link), but I have to say that the entire process is just deeply unfun. Really can't stress this enough how unfun. Anyway...

The product bundle now contains a dedicated Swift executable that calls SMAppService.register for both the daemon and agent. It registers the app in the system preferences login items menu, but I also get an error.

Error registering daemon: Error Domain=SMAppServiceErrorDomain Code=1 "Operation not permitted" UserInfo={NSLocalizedFailureReason=Operation not permitted}

What could be the reason?

I wouldn't need to activate the items, I just need them to be added to the list, so that I can control them via launchctl.

Which leads me to my next question, how can I control bundled daemons/agents via launchctl? I tried to use launchctl enable and bootstrap, just like I do with daemons under /Library/LaunchDaemons, but all I get is

sudo launchctl enable system/com.identifier.daemon
sudo launchctl bootstrap /Path/to/daemon/launchdplist/inside/bundle/Library/LaunchDaemons/com.blub.plist

Bootstrap failed: 5: Input/output error (not super helpful error message)

I'm really frustrated by the complexity of this process and all of its pitfalls.

Answered by DTS Engineer in 809441022

I can see how you might get that impression but…

My take on this is that /Library/Launch{Daemons,Agents} is critical to many folks, not just developers but also sysadmins. If we were to deprecate it, we’d have to be very clear about that.

Sometimes it pays to read between the lines and proactively adopt new technologies. Sometimes it pays to sit back, relax, and see how things pan out. The tricky is telling those cases apart O-:

Oh, one last thing: If you do stick with launchd property lists, make sure you set AssociatedBundleIdentifiers. That way the system can tie your launchd job to something user visible.

Share and Enjoy

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

Accepted Answer

Lemme take a step back here. ES products typically ship in one of two ways:

  • If they’re consumer focused, they use an ES system extension.

  • If they’re enterprise focused, they use an ES daemon.

It sounds like you fall into the latter category. If so, why are you adopting SMAppService? That mechanism is intended for folks shipping consumer focused products. For an ES client that’s targeting enterprise environments it makes sense to stick with an installer package.

In short, SMAppService is meant to be a replacement for things like SMJobBless and SMLoginItemSetEnabled, not a replacement for installer packages and launchctl.

Share and Enjoy

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

Thanks for your help Quinn, as always. Yes, we definitively fall under the second category.

We were under the impression that daemons and agents must live under /Library/LaunchDaemons starting with Ventura and that this will become mandatory some time in the future. The documentation said: In apps that target macOS 13 and later, your app needs to only use the property list locations outlined above.

If we can continue to put our launchd plists under /Library/Launch... and ignore SMAppService that would be great. I was following the provided example Xcode project in which an installer package is created and then calls the SMAppService part during the postinstall. I just assumed that "this is the way", even if it is very painful.

I can see how you might get that impression but…

My take on this is that /Library/Launch{Daemons,Agents} is critical to many folks, not just developers but also sysadmins. If we were to deprecate it, we’d have to be very clear about that.

Sometimes it pays to read between the lines and proactively adopt new technologies. Sometimes it pays to sit back, relax, and see how things pan out. The tricky is telling those cases apart O-:

Oh, one last thing: If you do stick with launchd property lists, make sure you set AssociatedBundleIdentifiers. That way the system can tie your launchd job to something user visible.

Share and Enjoy

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

What I've gleaned from this thread is that if you need your LaunchDaemon to be installed via MDM, you need to use launchctl from an installer, and that you can't use SMAppService at all?

We've been using SMAppService (and GUI prompts to approve it) on the assumption it behaves similarly to Network Extensions, where they can be preapproved by MDM - but it's become clear that's not the case.

Is our only option in supporting MDM installations to switch to launchctl in an installer and no longer distribute a dmg or zip?

Is our only option in supporting MDM installations to switch to launchctl in an installer … ?

I don’t know if that’s your only option, but I believe that it’s a good option. Managed sites usually have a standard path to run installer packages, so they’re generally pretty happy to receive one.

Oh, and it’s absolutely possible to package your code in two different ways, as an app for consumers and an installer package for managed sites.

Finally, I should point out that the Updating your app package installer to use the new Service Management API sample code specifically recommends running SMAppService code from an installer Postinstall script. I wasn’t aware of that last year when I wrote the above replies. I’ve seen a lot of folks bump into weird problems doing this, so I’m not a huge fan. However, it’s hard to argue that this is anything other than an officially documented approach.

Share and Enjoy

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

I believe that it’s a good option.

Good to know, thanks!

I’ve seen a lot of folks bump into weird problems doing this, so I’m not a huge fan.

Yeah, myself included. The sample project worked fine out the box for a LaunchAgent, but adapting it to a LaunchDaemon didn't work. The SMAppService.register call went through fine, but it didn't automatically approve it in System Settings, so it was obviously not a complete installation.

The sample project worked fine out the box for a LaunchAgent, but adapting it to a LaunchDaemon didn't work.

Right.

And even for a launchd agent, the solution isn’t ideal because SMAppService only lets you install an agent for the current user. There’s no way to install an agent for all users (r. 92457638). That is, you can do the equivalent of installing in ~/Library/LaunchAgents but not /Library/LaunchAgents. OTOH, most folks who create an installer package expect that it’ll take effect globally, not just for the user who ran the installer.

Regarding the issues you’re seeing, fixes might need to come from elsewhere, but I’m gonna recommend that you start by filing a bug against the docs.

Please post your bug number, just for the record.

Share and Enjoy

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

How to correctly deploy bundled launchdaemons/launchagents?
 
 
Q