How to launch a GUI application in another user's graphical session?

I'm trying to figure out how to launch a GUI application as another user that's logged in interactively, in that user's graphical session.

For example, say I have two users, foo and bar. Both are logged in, but the current interactive user is foo. I'd like to launch Calculator.app as user "bar", so that when I fast user switch to bar, I find the Calculator window is open in bar's session.

Here's what I've tried that doesn't work:

sudo -u bar /Applications/Calculator.app/Contents/MacOS/Calculator

This launches Calculator.app as bar, but the window opens in foo's graphical session.

sudo -u bar osascript -e "tell application \"Calculator\" to activate"

Same effect.

sudo -u bar open "/Applications/Calculator.app"

Launches Calculator as foo, not bar.

launchctl asuser [uid of bar] [any of the above commands]

Same effect.

Is there any way to accomplish this? I'm willing to entertain all manner of possible solutions, including bash scripting, AppleScript, writing a Core Foundation or Cocoa program, and so on. In my situation, any program or script could execute as any user, including root.

Note: I'm aware that it's possible using remote Apple Events, but I can't use that since in the situation I'm trying to do this I have no guarantee that "Remote Apple Events" will be enabled in Sharing preferences.

Any help would be greatly appreciated!


What you want to achieve is possible but difficult. You need to launch the application within the appropriate user session. For security reasons, crossing the user session divide is difficult.

You need a process already running in the other user's session to listen to your request and launch the application on your behalf.

launchd's bsexec

Thankfully, recent versions of launchd have this ability; although Apple engineers have not recommended its general use. Use the bsexec option in launchctl to target the appropriate user session:

 bslist [PID | ..] [-j]
          This prints out Mach bootstrap services and their respective states. While the namespace
          appears flat, it is in fact hierarchical, thus allowing for certain services to be only avail-
          able to a subset of processes. The three states a service can be in are active ("A"), inactive
          ("I") and on-demand ("D").

          If [PID] is specified, print the Mach bootstrap services available to that PID. If [..] is
          specified, print the Mach bootstrap services available in the parent of the current bootstrap.
          Note that in Mac OS X v10.6, the per-user Mach bootstrap namespace is flat, so you will only
          see a different set of services in a per-user bootstrap if you are in an explicitly-created
          bootstrap subset.

          If [-j] is specified, each service name will be followed by the name of the job which regis-
          tered it.

 bsexec PID command [args]
          This executes the given command in the same Mach bootstrap namespace hierachy as the given
          PID.

 bstree [-j]
          This prints a hierarchical view of the entire Mach bootstrap tree. If [-j] is specified, each
          service name will be followed by the name of the job which registered it.  Requires root priv-
          ileges.

The recommended approach is to write a launchd job ticket and restart the Mac - or ask the user to log-out and back in again.

Cause of the Problems

The problems stem from the application being connected to the wrong WindowServer process. Each user session has a separate WindowServer; this process handles the user interface. Your earlier methods place the ownership of the process with the right user but connected to your own WindowServer process.

This problem is mentioned in the Daemons and Agents technical note from Apple.

Experience

I know this from personal experience. For Power Manager, I wrote pmuser to exist within each user session. pmuser listens to our daemon and handles the per-user launches and commands. Despite our daemon having root authority, we still needed a per-user process to work reliably within user sessions.


None of the bsexec answers above work on El Capitan (10.11), due to System Integration Protection (SIP) closing the ports. "launchctl asuser" works, but requires to be run as root. The command below works on El Capitan (and most recent OS-es):

sudo launchctl asuser 501 open /Applications/Calculator.app

Note that 501 is the userid for my other user.


As finally 10.10 provides a correct "launchctl bsexec" implementation yo can use:

sudo /bin/launchctl bsexec PID chroot -u UID -g GID / open /Applications/TextWrangler.app

man says

This executes the given command in as similar an execution context as possible to the target PID.

So as PID param you can use the pid of the appropriate loginwindow process. The UID is the user id of the user owns that loginwindow and the GID is it's primary group.

This works fine for any command and of course for launchd jobs (f.e. launchagents) also at last like:

/bin/launchctl bsexec 104 chroot -u 501 -g 20 / /bin/launchctl load -S Aqua /Library/LaunchAgents/com.youragent.plist 2>&1