Search code examples
macossocketskernelkernel-extension

Can Kernel Control API support multiple, simultaneous client connections?


I'm using the Kernel Control API (SYSPROTO_CONTROL) for a user-land application to request information from a kernel extension, based on the code in Apple's documentation.

All works as expected with a single connected client. If a 2nd client tries to connect whilst the first is connected, it fails with the message: -

Error 16 (Resource busy).

The first client is then automatically disconnected.

Is it possible for two clients to be connected using the Kernel Control API and if not, is the best solution to keep trying to connect if the resource is busy?


Solution

  • I don't know if it is possible but the recommend way is to always only have one user space client that talks to one kernel extension, usually a background daemon running in user space and started by launchd. If you want multiple other apps or processes to access data from your kernel extension or somehow interact with it, then these would talk to the user space daemon and not directly to the kernel extension, which can also cache data in user space as crossing the users space/kernel space bridge is always expensive for data, so when 10 processes all want the same data, it would be better to just pull it once from the kernel and then distribute it 10 times in user space using any IPC mechanism of your choice.

    This setup is recommend as you should limit kernel control to root processes (using the CTL_FLAG_PRIVILEGED flag), so your daemon would run as such a root process, whereas normal apps and processes run with the privileges of the current user. Such a root helper daemon can be bundled inside an app bundle and using the SMJobBless API, the daemon is automatically copied to /Library/PrivilegedHelperTools and it's embedded plist (see SMJobBless documentation, which is only available in its header file AFAIK) is copied to /Library/LaunchDaemons and registered with launchd. Within such a plist you can use various triggers when the daemon shall be started by launchd, e.g. when your application tries to connect to a specific IPC UNIX socket. Then all you have to do in your app is trying to open that socket, what launchd will detect and start the daemon for you, which can then use launchd's API to get hold of that connected socket and immediately start talking with your app. So the whole thing is only installed once and then brings up itself each time your app is launched.

    For a SMJobBless sample project, see here.