Search code examples
macoslaunchd

Managing software updates for daemons -- ensuring they are not running


I have a general question regarding the right way to do software updates on OS X. My application runs two daemons, both as root. One of the daemons can download the software update package from the network. When downloaded it must install the new binaries, including two binaries replacing the current ones for the daemons. Since both the daemons are currently running, replacing them might be a issue. Hence I have few ideas, however it would helpful to get expert advice on the same.

Method 1: After downloading the update, it can be kept in some location. The update daemon can place a bash script in another specified location and create a plist which can be placed in the LaunchDaemons directory to be triggered during the next reboot. The script simply replaces both the binaries before startup. This is possible only if we can ensure that the two daemons do not start up before my script executes. I didn't find a good way to tell launchd to run my script first, before starting the daemons. So I am not quite sure if this is a good solution.

Method 2: After download, immediately launch a script using launchd programatically. This should be possible, right? The script waits for a TERM signal like shown here https://discussions.apple.com/thread/3636062?start=0&tstart=0 to actually replace the binaries and exit gracefully. Again the problem is, when my script gets launched, I must be sure that my daemons are shut down before replacing the binaries. I am not sure how to do this.

Can anyone suggest which is the better approach and also let me know of any better way?


Solution

  • The solution I use to update daemon is to launch separate shell script which do the next:

    • unload daemon via launchctl unload -w path.to.plist, it will wait for the job to finish.
    • remove old files
    • install new files (here I actually install the package, which will launch my daemon in postflight script)
    • optional: launch daemon with launchctl load -w path.to.plist

    To execute script you need just fork(), setsid() and execv()

    You can use applescript in your shell script to gracefully quit application:

    osascript << EOF
    if application "Your Application" is running then
        -- Force application to quit
        tell application "Your Application" to quit
        -- Wait until it quits
        repeat
            if application "Your Application" is not running then exit repeat
            delay 1
        end repeat
    end if
    EOF
    killall -15 your_app # just in case