[Un]loading user agents on [Un]install
Hi all, So I think this is probably quite a "sane" task to have to perform: - Upon installing a product, detect which users are logged in, and load the installed LaunchAgent script for each user. - Upon uninstalling the product, detect which users are logged in, and unload the LaunchAgent script for each user. These tasks would be performed preupgrade, postinstall, etc... I wrote the following script to perform this task: ---- for user in `/usr/bin/users` do echo "Loading agent for user $user..." /usr/bin/su -l $user -c "/bin/launchctl load -SAqua /Library/LaunchAgents/com.mycompany.myproduct.plist" EXIT_STATUS=$? if [ $EXIT_STATUS != 0 ] then echo "failed (non-fatal, status $EXIT_STATUS)." else echo "Done!" fi done ---- And similarly for unloading. The script works for the user that launched the installer. But if for example another user is logged in using Fast-User Switching, the following error is printed in the installer log when preupgrade attempts to unload the script: preupgrade[pid]: launchctl: Error unloading: com.mycompany.myproduct.plist Also, on post[upgrade/install], the following error is printed when attempting to load the script for other users: postupgrade[pid]: launch_msg(): socket is not connected I've searched around for answers to these issues, but had no luck. I was hoping someone here might be able to point me in the right direction? Many thanks for any help! Kind regards, Tim
Hi all, Is nobody able to advise on this type of scenario? Kind regards, Tim On Tue, 23 Sep 2008 12:13:37 +0100, Tim Schooley wrote:
Hi all,
So I think this is probably quite a "sane" task to have to perform:
- Upon installing a product, detect which users are logged in, and load the installed LaunchAgent script for each user.
- Upon uninstalling the product, detect which users are logged in, and unload the LaunchAgent script for each user.
These tasks would be performed preupgrade, postinstall, etc...
I wrote the following script to perform this task:
---- for user in `/usr/bin/users` do echo "Loading agent for user $user..." /usr/bin/su -l $user -c "/bin/launchctl load -SAqua /Library/LaunchAgents/com.mycompany.myproduct.plist" EXIT_STATUS=$? if [ $EXIT_STATUS != 0 ] then echo "failed (non-fatal, status $EXIT_STATUS)." else echo "Done!" fi done ----
And similarly for unloading.
The script works for the user that launched the installer. But if for example another user is logged in using Fast-User Switching, the following error is printed in the installer log when preupgrade attempts to unload the script:
preupgrade[pid]: launchctl: Error unloading: com.mycompany.myproduct.plist
Also, on post[upgrade/install], the following error is printed when attempting to load the script for other users:
postupgrade[pid]: launch_msg(): socket is not connected
I've searched around for answers to these issues, but had no luck. I was hoping someone here might be able to point me in the right direction?
Many thanks for any help!
Kind regards,
Tim
_______________________________________________ launchd-dev mailing list launchd-dev@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/launchd-dev
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
participants (2)
-
Quinn
-
Tim Schooley