[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