[launchd-dev] [Un]loading user agents on [Un]install

Quinn eskimo1 at apple.com
Tue Sep 30 06:00:57 PDT 2008


At 12:24 +0100 30/9/08, Tim Schooley wrote:
>Is nobody able to advise on this type of scenario?

Alas, if there was an easy answer I would've answered earlier.  This 
is a known limitation of launchd's current architecture.  There's 
already a bug to cover this <rdar://problem/5476420> but I don't have 
information to share about when it might be addressed.

I've seen two broad classes of workaround:

A. application specific -- Code your agent to listen for upgrade 
notifications.  That is, rather than try to have your installer find, 
unload, and reload each agent, have it broadcast a notification that 
instructs to agents to unload.  There are two aspects to this:

- communication -- If your agents are all talking to a common piece 
of code (for example, a daemon or a kext), you can have that common 
code send the notification to the agent via its standard 
communications channel.  This could be as simple as having your 
daemon quit (and hence drop its connection to all of the agents) and 
having the agent respond to a dropped connection by quitting (and 
being relaunched by its per-user launchd).

If your agents don't all talk to a common piece of code, you'll need 
to use some other notification mechanism (perhaps 
<x-man-page://3/notify> or CFNotificationCenter with 
kCFNotificationPostToAllSessions).

- quit vs unload -- In most cases you can just overwrite your agent 
from your installer and then have the agent quit.  launchd will 
relaunch your agent from the new code.  There are some gotchas here:

   * Your old and new agents must be sufficiently compatible on disk
     to avoid problems.  For example, if your new agent removes a
     .strings file that your old agent relies on, you could run into
     transient and very hard to debug problems.

   * You won't be able to change your agent's property list file
     (because each per-user launchd has cached its contents when
     the job was loaded).

If these are a problem, your agent will need to be smarter.  For 
example, it could use launchctl to unload and then reload itself. 
Yeah, that gets ugly fast )-:

Finally, this option has one limitation for which there is no good 
workaround: if there are two GUI login sessions when you first 
install, you can't get the agent running in the background session. 
This works in the upgrade case (because you have already have an 
agent running in the background session to help out) but in the first 
install case you're SOL.

B. bootstrap switch -- Another option is to explicitly talk to a 
given per-user launchd by switching your bootstrap namespace.  You 
can find out all the GUI login sessions using the techniques from 
QA1133 "Determining console user login status".

<http://developer.apple.com/qa/qa2001/qa1133.html>

You can use launchctl's bsexec command to run commands in a different 
login session.  Thus, you can explicitly unload the job from all 
per-user launchds, do your install, and then reload it.

                  *                   *                   *

Neither of these solutions is ideal.  Both are hard to implement and 
entail a certain amount of compatibility liability.  If I had to 
choose between them, I'd probably go for A because it's likely to be 
the most compatible.

Overall, I recommend that you carefully weigh these options against 
the cop-out solution of forcing a restart.  While I agree that it is 
not very Mac like, it is easy to implement and will be very 
compatible.

S+E
-- 
Quinn "The Eskimo!"                    <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware


More information about the launchd-dev mailing list