Way to load a LaunchAgent as another user

launchdpermission

From a script run as root I copy a LaunchAgent .plist to /Users/xyz/Library/LaunchAgents.

If I load that via launchctl logged in as user xyz it works without problems. If I try to load it from the script with the following command:

sudo -u xyz launchctl load /Users/xyz/Library/LaunchAgents/x.y.z.plist

it doesn't work. The error message is

Could not open job overrides database at /private/var/db/launchd.db/com.apple.launchd.peruser.0/overrides.plist: 13: Permission denied  
launch_msg(): Socket is not connected

Is there any other way to accomplish my task?

Best Answer

It looks to me like the permissions error is just a symptom of a much deeper problem: you're trying to load the item into the wrong mach bootstrap context. Specifically, you're running the launchctl command as xyz, but it's trying to load the item into root's context, which xyz doesn't have permissions to do. What you want is to load the item into xyz's context instead (which xyz should have permissions to do).

Ok, let me back up a bit, and give you a bit of a summary of the launchd hierarchy in OS X. When the OS boots, it starts a master instance of launchd (the "system" launchd), which loads items from the various Library/LaunchDaemons folders.

When a user logs in (or otherwise starts a "session"), a new "bootstrap context" is created, a new instance of launchd (the "user" launchd) is started (in that new context) which loads items from the various Library/LaunchAgents folders (all within that new context).

When you use sudo -u xyz ..., it switches user IDs to xyz, but doesn't switch to xyz's context.

Now, there are a couple of cases here: first, if xyz isn't logged in (i.e. doesn't have an active session), they don't have a bootstrap context or a launchd instance, so you can't load a LaunchAgent as them. Basically, LaunchAgents load into user sessions, so if there's no session there's nothing to load it into. On the other hand, this shouldn't be a problem: next time xyz logs in, your item will be loaded automatically (as long as it's in the right place).

If xyz is logged in, you can get the process ID of some process within their session, and use launchctl bsexec to switch to that process's bootstrap context (and then use sudo -u xyz to switch UIDs to xyz, and then load the item):

loadUser=xyz
loadPID=$(ps -axj | awk "/^$loadUser / {print \$2;exit}")
launchctl bsexec $loadPID sudo -u $loadUser launchctl load /Users/xyz/Library/LaunchAgents/x.y.z.plist