Current wisdom on multiple XPC services in a System Extension?

I'm following up on a couple of forum threads from 2020 to get more clarity on the current guidance for supporting multiple XPC services in system extensions. For context, I'm trying to create a system extension that contains both an Endpoint Security client and a Network Extension filter, and I'm seeing indications that the system may not expect this and doesn't handle it smoothly.

First: Previous guidance indicated that the system would automatically provide a Mach service named <TeamID>.<BundleID>.xpc to use for communicating with the system extension. However, the SystemExtension man page currently documents an Info.plist key called NSEndpointSecurityMachServiceName and suggests that the default service name is deprecated; and in fact if this key is not set, I find a message in the Console:

The extension from <app-name> (<bundle-id>) is using the deprecated default mach service name. Please update the extension to set the NSEndpointSecurityMachServiceName key in the Info.plist file.

I have accordingly set this key, but I wanted to confirm that this is the current best practice.

Second, and more interesting: Another user was trying to do something similar and observed that the Mach service for the endpoint security client wasn't available but the NE filter was. Quinn did some research and replied that this was intended behavior, quoting the EndpointSecurity man page:

"If ES extension is combined with a Network Extension, set the NEMachServiceName key in the Info.plist"

(which I have also done), and concluding from this:

... if you have a combined ES and NE system extension then the Mach service provided by the NE side takes precedence.

However, the current man page does not include this quoted text and says nothing about a combined ES and NE system extension.

So I'm wondering about current best practice. If I do combine the ES and NE clients in a single system extension, should they each declare the Mach service name under their respective Info.plist keys? And could there be a single XPC listener for both, using the same service name under each key, or would it be better to have separate XPC listeners?

Alternatively, would it be preferable to have each component in a separate system extension? (This would entail some rearchitecting of the current design.)

Answered by DTS Engineer in 879626022
I wanted to confirm that this is the current best practice.

For an ES sysex, yes.

As to what happens when you combine ES and NE, I did some digging and that text is definitely present in the Xcode 12.5 man page and definitely gone in the Xcode 13 one. Based on that info I was able to track down FB8701548, which was a developer request that we remove that limit. This was resolved in macOS 11 and, as part of that, we removed that text from the man page.

Neat!

With that in mind, let’s return to your other questions:

should they each declare the Mach service name under their respective Info.plist keys?

That’s up to you. I can see arguments either way:

  • If the ES and NE subsystems within your sysex are tightly bound together, it’d make sense to use a single named endpoint.
  • OTOH, if those subsystems act independently, it’d make sense to have two different named endpoints.
And could there be a single XPC listener for both, using the same service name under each key … ?

Don’t do that. If you want two listeners, use two different endpoint names.

would it be preferable to have each component in a separate system extension?

Again, that’s pretty much up to you. Having said that, there are good reasons to use a single sysex:

  • Managing a sysex is a pain, and managing two sysexes is extra pain.
  • With a single sysex, both subsystem run in the same process, which is an advantage if your subsystems are tightly bound. For example, if you have a single rules database then both subsystems can share that database using just a mutex, rather than requiring some complicated shared memory or IPC shenanigans.

OTOH, I can see at least one counter argument:

  • If you put both subsystems in the same sysex then a failure in one subsystem will also take out the other subsystem. Notably, the system kills an ES sysex if it doesn’t respond to events promptly, and in a combined sysex that will also take out your NE providers as well.

Share and Enjoy

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

I wanted to confirm that this is the current best practice.

For an ES sysex, yes.

As to what happens when you combine ES and NE, I did some digging and that text is definitely present in the Xcode 12.5 man page and definitely gone in the Xcode 13 one. Based on that info I was able to track down FB8701548, which was a developer request that we remove that limit. This was resolved in macOS 11 and, as part of that, we removed that text from the man page.

Neat!

With that in mind, let’s return to your other questions:

should they each declare the Mach service name under their respective Info.plist keys?

That’s up to you. I can see arguments either way:

  • If the ES and NE subsystems within your sysex are tightly bound together, it’d make sense to use a single named endpoint.
  • OTOH, if those subsystems act independently, it’d make sense to have two different named endpoints.
And could there be a single XPC listener for both, using the same service name under each key … ?

Don’t do that. If you want two listeners, use two different endpoint names.

would it be preferable to have each component in a separate system extension?

Again, that’s pretty much up to you. Having said that, there are good reasons to use a single sysex:

  • Managing a sysex is a pain, and managing two sysexes is extra pain.
  • With a single sysex, both subsystem run in the same process, which is an advantage if your subsystems are tightly bound. For example, if you have a single rules database then both subsystems can share that database using just a mutex, rather than requiring some complicated shared memory or IPC shenanigans.

OTOH, I can see at least one counter argument:

  • If you put both subsystems in the same sysex then a failure in one subsystem will also take out the other subsystem. Notably, the system kills an ES sysex if it doesn’t respond to events promptly, and in a combined sysex that will also take out your NE providers as well.

Share and Enjoy

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

Current wisdom on multiple XPC services in a System Extension?
 
 
Q