Search code examples
bashmacoslaunchdlaunch-daemon

Running mac os x application as a user does not stop by its own


I am creating a macOS installer package.

For this I am using a post-install script file that launches an application and then loads a LaunchDaemon plist.

Here is the post-install script:

#!/bin/bash

cd /usr/local/TestApp
USER_NAME=$(who | head -1 | head -1 | awk '{print $1;}')
sudo -u $USER_NAME /usr/local/TestApp/Test.app/Contents/MacOS/Test -l

sudo launchctl load /Library/LaunchDaemons/com.testapp.plist

The result is that it starts the application with the sudo -u $USER_NAME /usr/local/TestApp/Test.app/Contents/MacOS/Test -l command and then blocks, because the application keeps running.

Therefore, the script gets stuck, and the LaunchDaemon is never loaded.

Please let me know what I can do this in case.


Solution

  • If you simply want to launch a Mac application (*.app) asynchronously,

    • use open -a with the bundle directory path (ending in .app)
    • and pass any pass-through command-line arguments after --args (see man open):
    sudo -u $USER_NAME open -a /usr/local/TestApp/Test.app --args -l
    

    See note at the bottom re reliably determining $USER_NAME, the invoking user's username.

    If, for some reason, you do need to target the executable embedded in the *.app bundle directly, you must use Bash's & control operator to run the command in the background:

    #!/bin/bash
    
    # Get the underlying username (see comments below).
    userName="${HOME##*/}"
    
    # Launch the app in the background, using control operator `&`
    # which prevents the command from blocking.
    # (Given that the installer runs the script as the root user,
    # `sudo` is only needed here for impersonation.)
    sudo -u "$userName" /usr/local/TestApp/Test.app/Contents/MacOS/Test -l &
    
    # Load the daemon.
    # (Given that the installer runs the script as the root user,
    # `sudo` is not needed here.)
    launchctl load /Library/LaunchDaemons/com.testapp.plist
    

    Note that I've changed the way that the underlying username is determined:

    • ${HOME##*/} extracts the last path component from $HOME, the underlying user's home directory path, which reflects the user who invoked the installer.

    • This is more robust than using who without arguments, whose output can include other users.

    (As an aside, who | head -1 | head -1 | awk '{print $1;}' can be simplified to the more efficient who | awk '{print $1; exit}).