Issues with LaunchAgent and LaunchDaemon

I have this application that is divided in 3 parts.

  • A server that handles all the networking code
  • A agent that handles all System related code
  • A manager (NSApplication) to interact with the other two processes.

Goals

  • All three process should be kept alive if they crash
  • All three processes must not restart if the user quits them though the NSApplication
  • They need to run during the login window.

My current set up using LaunchD is as follows.

My Server process plist (relevant part) saved in System/LaunchDaemons

key>MachServices</key>
<dict>
	<key>com.myCompany.Agent.xpc</key>
	<true/>
	<key>com.myCompany.Manager.xpc</key>
	<true/>
</dict>
<key>ProgramArguments</key>
<array>
	<string>PathToExecutable</string>
	<string>-service</string>
</array>
<key>RunAtLoad</key>
<false/>

My agent plist (saved in System/LaunchAgent)

<key>QueueDirectories</key>
<array>
	<string>PathToDirectory</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>ProgramArguments</key>
<array>
	<string>PathToExecutable</string>
	<string>service</string>
</array>

my Manager app plist (saved in System/LaunchAgent)

<key>LimitLoadToSessionType</key>
        <string>Aqua</string>
    <key>RunAtLoad</key>
        <false/>
    <key>ProgramArguments</key>
        <array>
            <string>PathToExecutable</string>
        </array>
    <key>MachServices</key>
        <dict>
	        <key>com.myCompany.Agent.manager.xpc</key>
	        <true/>
        </dict>

Currently I have another app that saves a file to the path of the QueueDirectories which triggers the launch of my Agent which then triggers the Server and Manager by starting a XPC Connection. QueueDirectories keeps the Agent alive (and hence all other processes) til file is removed and processes are quited through the manager.

XPC Connections

  • Server listens for a connection from agent and manager
  • Manager listens for a connection from agent and starts a connection with server
  • Agent starts a connection with Manager and Server

Agent and Manager are owned by the user and server by root

The problems

When I start Agent by saving a file in the QueueDirectories path and connect to the Server over xpc I end up with two Agents, one owned by the user (the one I expect) and one owned by root.

But if I manually start the Agent I do not have that problem.

As I mentioned before, the server listens for a connection from the Agent.

How do I stop getting two instances? or what is a better way to approach this?

All three processes must not restart if the user quits them

It’s impossible to achieve this goal for a launchd agent. That’s because:

  • A launchd agent must necessarily be running on the user associated with its session, otherwise it won’t be able to do agent-y things.

  • If it is, the user can kill it.

The only solution here is to structure your product so that:

  • The core functionality lives in the daemon, which the user can’t kill [1].

  • The user doesn’t gain anything by killing your agent.

I end up with two Agents, one owned by the user (the one I expect) and one owned by root.

It’s not clear how that comes about. One obvious way this can happen is if your agent loads into the pre-login session. However, you haven’t listed LoginWindow in your LimitLoadToSessionType. However^2, you earlier said that “They need to run during the login window”, which suggests that you should be listing it there. So I’m a bit confused.

Share and Enjoy

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

[1] Unless they’re as an admin user, and you can’t protect yourself from admin users.

Issues with LaunchAgent and LaunchDaemon
 
 
Q